Doble Factor de Autenticación – Cisco VPN SSL con MultiOTP y Freeradius

En el post anterior vimos concepto de Doble Factor de Autenticación. En este post, vamos a realizar una prueba de concepto para usar doble factor de autenticación en un escenario de VPN.

  • 1 – Dispositivos hardware y software usados en este PoC:
Cliente Cisco VPN AnyConnect
Firewall Cisco ASA ( servidor VPN SSL )
Radius IAS o NPS integrado en Active Directory
Freeradius sobre Ubuntu Server + MultiOTP ( clase PHP )
Token compatible con OATH ( algoritmos TOTP - rfc6238 y HTOP - rfc4226 )
  • 2 – Topología:

2-factor-authentication-multiotp-freeradius-cisco-asa

  • 3 – Instalaremos y configuraremos en este post:
Freeradius 2.1.12 
MultiOTP 4.0.7 
La autenticación doble en el Firewall Cisco.
  • 4 – Roles:

Firewall Cisco ASA como servidor de VPN SSL con autenticación primaria basada en Active Directory.
El autenticador de las credenciales de Active Directory será un servidor radius Microsoft IAS o NPS.
El cliente de software VPN que tienen instalados los usuarios es Cisco VPN AnyConnect.
La autenticación secundaria de los clientes vpn anyconnect se basará en un método de doble factor de autenticación mediante un token ( sirven los harware tokens y los soft tokens de los RFC’s mencionados antes ).
El autenticador secundario será un servidor Freeradius sobre Ubuntu Server 13.10 que usará la clase PHP MultiOTP.

  • 5 – Proceso de la autenticación múltiple, correcta y simplificada:

5.1 – El usuario arranca el cliente VPN-SSL e inicia la validación.
5.2 – El firewall le pregunta las credenciales de dominio y el OTP al usuario y éste se autentica.
5.3 – El firewall reenvía la autenticación del usuario al servidor Radius IAS o NPS integrado en Active Directory y éste acepta.
5.4 – El firewall reenvía la autenticación del token a un servidor freeradius con MultiOTP.
5.5 – Freeradius ejecuta el módulo MultiOTP y acepta 5.6 – El firewall acepta el túnel VPN con el cliente.

  • 6- Instalación de Freeradius y MultiOTP

Partimos de un sistema Ubuntu Server 13.10 recién instalado. Ubuntu Server 13.10 incorpora un cliente ntp que sincroniza cuando arranca el sistema. Debemos confirmar que la hora del sistema es correcta ( para nuestra zona horaria ). 6.1 – Instalamos los paquetes

date
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install build-essential -y
sudo apt-get install freeradius freeradius-utils unzip apache2 php5 php5-gd ntp -y

6.2 – Paramos freeradius y verificamos ntp

sudo initctl stop freeradius
ntpq -p

6.3 – Descargamos y descomprimimos MultiOTP. Lo hacemos accesible via web para visualizar los códigos QR para aprovisionar soft tokens (opcional, pero mejor toma medidas de seguridad)

sudo mkdir /var/www/multiotp
sudo wget -O /var/www/multiotp/multiotp-4.0.7.zip 'http://files.sysco.ch/downloadfile.php?group=3&dir=public%2FmultiOTP&file=multiotp-4.0.7.zip'
cd /var/www/multiotp/
sudo unzip -q /var/www/multiotp/multiotp-4.0.7.zip

6.4 – Nos logamos como root:

sudo su -

6.5 Modificamos la configuración de Freeradius para permitir la autenticación mediante MultiOTP: 6.5.1 – Empezamos con el fichero /etc/freeradius/modules/multiotp Copiamos y pegamos directamente en el terminal:

cat > /etc/freeradius/modules/multiotp <<EOF
exec multiotp {
        wait = yes
        input_pairs = request
        output_pairs = reply
program = "/var/www/multiotp/multiotp.php %{User-Name} %{User-Password} -chap-challenge=%{CHAP-Challenge} -chap-password=%{CHAP-Password}"
        shell_escape = yes
}
EOF
#

6.5.2 – Fichero /etc/freeradius/sistes-enabled/default Seguimos con el copy-paste sobre el terminal.

cp /etc/freeradius/sites-enabled/default /etc/freeradius/sites-enabled/default.bkp
cat > /etc/freeradius/sites-enabled/default <<EOF
authorize {
        preprocess
        mschap
        digest
        suffix
        eap {
                ok = return
        }
        files
        expiration
        logintime
    multiotp
        pap
}
authenticate {

    Auth-Type multiotp {
        multiotp
        }
        Auth-Type PAP {
                pap
        }
        Auth-Type CHAP {
                chap
        }
        Auth-Type MS-CHAP {
                mschap
        }
        digest
        unix
        eap
}
preacct {
        preprocess
        acct_unique
        suffix
        files
}
accounting {
        detail
        unix
        radutmp
        exec
        attr_filter.accounting_response
}
session {
        radutmp
}
post-auth {
        exec
        Post-Auth-Type REJECT {
                attr_filter.access_reject
        }
}
pre-proxy {
}
post-proxy {
        eap
}
EOF
#

6.5.3 – Fichero /etc/freeradius/sites-enabled/inner-tunnel Seguimos con copy & paste :

cat > /etc/freeradius/sites-enabled/inner-tunnel <<EOF
server inner-tunnel {
listen {
       ipaddr = 127.0.0.1
       port = 18120
       type = auth
}
authorize {
        mschap
        suffix
        update control {
               Proxy-To-Realm := LOCAL
        }
        eap {
                ok = return
        }
        files
        expiration
        logintime
    multiotp
        pap
}
authenticate {

    Auth-Type multiotp {
        multiotp
    }
        Auth-Type PAP {
                pap
        }
        Auth-Type CHAP {
                chap
        }
        Auth-Type MS-CHAP {
                mschap
        }
        unix
        eap
}
session {
        radutmp
}
post-auth {
        Post-Auth-Type REJECT {
                attr_filter.access_reject
        }
}
pre-proxy {
}
post-proxy {
        eap
}
}
EOF
#

6.5.4 – Le toca al fichero policy.conf. Realizamos un backup y con el editor “vi” insertamos la configuración:

cp /etc/freeradius/policy.conf /etc/freeradius/policy.conf.bkp 
vi /etc/freeradius/policy.conf

Configuración de policy.conf:

policy {
        forbid_eap {
                if (EAP-Message) {
                        reject
                }
        }
        permit_only_eap {
                if (!EAP-Message) {
                        if (!"%{outer.request:EAP-Message}") {
                                reject
                        }
                }
        }
        deny_realms {
                if (User-Name =~ /@|\\/) {
                        reject
                }
        }
        do_not_respond {
                update control {
                        Response-Packet-Type := Do-Not-Respond
                }
                handled
        }
        filter_username {
                if (User-Name =~ /^ /) {
                        reject
                }
                if (User-Name =~ / $$/) {
                        reject
                }
                if (User-Name != "%{tolower:%{User-Name}}") {
                        reject
                }
        }
        cui_authorize {
                update request {
                        Chargeable-User-Identity:='\\000'
                }
        }
        cui_postauth {
                if (FreeRadius-Proxied-To == 127.0.0.1) {
                        if (outer.request:Chargeable-User-Identity) {
                                update outer.reply {
                                        Chargeable-User-Identity:="%{md5:%{config:cui_hash_key}%{User-Name}}"
                                }
                        }
                }
                else {
                        if (Chargeable-User-Identity) {
                                update reply {
                                        Chargeable-User-Identity="%{md5:%{config:cui_hash_key}%{User-Name}}"
                                }
                        }
                }
        }
        cui_updatedb {
                if (reply:Chargeable-User-Identity) {
                        cui
                }
        }
        cui_accounting {
                if (!Chargeable-User-Identity) {
                        update control {
                                Chargable-User-Identity := "%{cui: SELECT cui FROM cui WHERE clientipaddress = '%{Client-IP-Address}' AND callingstationid = '%{Calling-Station-Id}' AND username = '%{User-Name}'}"
                        }
                }
                if (Chargeable-User-Identity && (Chargeable-User-Identity != "")) {
                        cui
                }
        }
        mac-addr = ([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})
        rewrite.called_station_id {
                if((Called-Station-Id) && "%{Called-Station-Id}" =~ /^%{config:policy.mac-addr}(:(.+))?$/i) {
                        update request {
                                Called-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
                        }
                        if ("%{8}") {
                                update request {
                                        Called-Station-Id := "%{Called-Station-Id}:%{8}"
                                }
                        }
                        updated
                }
                else {
                        noop
                }
        }
        rewrite.calling_station_id {
                if((Calling-Station-Id) && "%{Calling-Station-Id}" =~ /^%{config:policy.mac-addr}$/i) {
                        update request {
                                Calling-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
                        }
                        updated
                }
                else {
                        noop
                }
        }
    multiotp.authorize {
        if (!control:Auth-Type) {
            update control {
                Auth-Type := multiotp
            }
        }
        }
}

6.5.5 – El último fichero de configuración, vamos a por clients.conf:
En este caso está permitido realizar peticiones radius a any ( 0.0.0.0/0 ), pero por seguridad, acótalo a las ip’s de tus dispositivos de red.

cp /etc/freeradius/clients.conf /etc/freeradius/clients.conf.bkp
cat > /etc/freeradius/clients.conf <<EOF
client localhost {
        ipaddr = 127.0.0.1
        secret          = 9UFvgA0eK5Veq8APSZC4
        require_message_authenticator = no
}
    client 0.0.0.0 {
    netmask = 0
    secret = ZA35Qysrmd7p7Gy7QHXQ
    }
EOF
#
  • 7 – Verificamos la configuración de Freeradius arrancándolo en modo debug.
root@ubuntu:~# freeradius -X

Deberíamos ver en pantalla las siguientes líneas del listener de freeradius en modo debug:

Listening on authentication address * port 1812
Listening on accounting address * port 1813
Listening on authentication address 127.0.0.1 port 18120 as server inner-tunnel
Listening on proxy address * port 1814
Ready to process requests.
  • 8 – Si ha ido bien, paramos con control+c y lanzamos el daemon:
root@ubuntu:~# initctl start freeradius
  • 9 – Permisos de MultiOTP

Permitimos la ejecución de MultiOTP para la creación de usuarios y troubleshooting en terminal:

root@ubuntu:~# cd /var/www/multiotp
root@ubuntu:/var/www/multiotp# chmod +x multiotp.php
root@ubuntu:/var/www/multiotp# ./multiotp.php -config debug=1 
root@ubuntu:/var/www/multiotp# ./multiotp.php -config display-log=1
19 INFO: Requested operation successfully done
  • 10 – Creación de usuarios en MultiOTP:

En el siguiente ejemplo, crearemos un usuario “capa3”, que existe con ese sAMAccountName en el dominio de Active Directory, vinculado a un soft token TOTP. Crearemos un código QR que contendrá el seed. Visualizaremos los códigos de “rescate offline”, de un sólo uso, por si no tenemos acceso al soft token (ej. sin batería en el móvil)

cd /var/www/multiotp
root@ubuntu:/var/www/multiotp# ./multiotp.php -fastcreate capa3
LOG 2013-12-01 22:30:40 (user capa3) Info: File created: ./users/capa3.db
LOG 2013-12-01 22:30:40 (user capa3) Info: User capa3 successfully created and saved.
LOG 2013-12-01 22:30:40 (user capa3) Info: user capa3 successfully created
11 INFO: User successfully created or updated

root@ubuntu:/var/www/multiotp# ./multiotp.php -qrcode capa3 qrcode/capa3.png
16 INFO: QRcode successfully created

root@ubuntu:/var/www/multiotp# ./multiotp.php -scratchlist capa3
328482
812658
073348
887919
320986
435986
964213
267638
985814
003773
19 INFO: Requested operation successfully done

Modificamos permisos para que freeradius funcione correctamente:

root@ubuntu:/var/www/multiotp# chown -R freerad:freerad users/*
root@ubuntu:/var/www/multiotp# chmod 660 users/*
root@ubuntu:/var/www/multiotp# chown freerad:freerad log/multiotp.log 
root@ubuntu:/var/www/multiotp# chmod 660 log/multiotp.log
  • 11 – Instalación del token del usuario.

Visualizamos el código QR en el navegador yejecutamos Google Authenticator en el smartphone

Este código QR contiene el seed ( semilla ) del usuario que he creado en el ejemplo y tiene sentido en este sistema. En tu servidor, aunque uses el mismo usuario del ejemplo, el código QR debería ser diferente:

http://ip del servidor Freeradius-MultiOTP/multiotp/qrcode/capa3.png

navegador-codigo-QR-Google-Autenticator-small

Desde Google Authenticator escaneamos el código QR, se añadirá el seed y creará el soft token:
escanear-codigo-QR-Google-Autenticator

  • 12 – Verificación de MultiOTP:

12.1 – Vamos a probar que el token funciona correctamente para el usuario “capa3”:

radtest-otp-google-authenticator

12.2 – Lanzamos radtest, cliente radius de terminal El password radius para localhost es 9UFvgA0eK5Veq8APSZC4 , ver clients.conf

root@ubuntu:~# radtest capa3 541938 localhost 1812 9UFvgA0eK5Veq8APSZC4
Sending Access-Request of id 38 to 127.0.0.1 port 1812
        User-Name = "capa3"
        User-Password = "541938"
        NAS-IP-Address = 127.0.1.1
        NAS-Port = 1812
        Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Accept packet from host 127.0.0.1 port 1812, id=38, length=20

12.3 – Vamos a probar un scratch code de la lista de contraseñas de un sólo uso:

root@ubuntu:~# radtest capa3 328482 localhost 1812 9UFvgA0eK5Veq8APSZC4
Sending Access-Request of id 101 to 127.0.0.1 port 1812
        User-Name = "capa3"
        User-Password = "328482"
        NAS-IP-Address = 127.0.1.1
        NAS-Port = 1812
        Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Accept packet from host 127.0.0.1 port 1812, id=101, length=20

12.4 – Los logs efectivamente muestran los accesos correctos y el uso del scratch code. 12.4.1 – Logs de MultiOTP:

root@ubuntu:~# tail /var/www/multiotp/log/multiotp.log 
========================================
multiotp 4.0.7
Your script is running from /var/www/multiotp/
2013-12-04 16:05:39 OK: user capa3 successfully logged in
2013-12-04 16:05:39 Info: 0 OK: Token accepted
========================================
multiotp 4.0.7
Your script is running from /var/www/multiotp/
2013-12-04 16:06:17 OK: user capa3 successfully logged in with a scratch password
2013-12-04 16:06:17 Info: 0 OK: Token accepted

12.4.2 – Logs de Accounting de Freeradius en: /var/log/freeradius/radacct/-ip-del-firewall-vpn-/

root@ubuntu::~# cat /var/log/freeradius/radacct/192.168.5.1/detail-20131204
Wed Dec  4 23:39:27 2013
        User-Name = "capa3"
        NAS-Port = 90112
        Service-Type = Framed-User
        Framed-Protocol = PPP
        Called-Station-Id = "80.xx.YY.ZZ"
        Calling-Station-Id = "zz.zz.zz.zz"
        Acct-Status-Type = Start
        Acct-Delay-Time = 0
        Acct-Session-Id = "EFA00001"
        Acct-Authentic = RADIUS
        NAS-Port-Type = Virtual
        Tunnel-Client-Endpoint:0 = "xx.xx.xx.xx"
        Vendor-3076-Attr-146 = 0x61646d696e732d76706e73736c
        Vendor-3076-Attr-150 = 0x00000003
        Vendor-3076-Attr-151 = 0x00000003
        Vendor-3076-Attr-152 = 0x00000001
        NAS-IP-Address = 192.168.5.1
        Acct-Unique-Session-Id = "6cebde4b13006060"
        Timestamp = 1386196767
  • 13 – Configuración del Firewall Cisco ASA

En este escenario, la configuración vpn ssl ya está creada. Vamos a ver la configuración necesaria para que el perfil vpn tenga doble factor de autenticación. Así pues, veremos la configuración de los dos servidores radius y del tunnel-group correspondiente a un perfil vpn ssl existente. 13.1 radius

aaa-server radius-ias protocol radius
aaa-server radius-ias (inside) host 192.168.5.3
 key *****
 authentication-port 1812
 accounting-port 1813
 no mschapv2-capable

aaa-server radius-multiotp protocol radius
aaa-server radius-multiotp (inside) host 192.168.5.141
 retry-interval 4
 key ZA35Qysrmd7p7Gy7QHXQ
 authentication-port 1812
 accounting-port 1813
 no mschapv2-capable

13.2 Tunnel-Group

tunnel-group perfil-vpnssl type remote-access
tunnel-group perfil-vpnssl general-attributes
 authentication-server-group radius-ias
 secondary-authentication-server-group radius-multiotp use-primary-username
 accounting-server-group radius-multiotp
 default-group-policy perfil-vpnssl
tunnel-group perfil-vpnssl webvpn-attributes
 proxy-auth sdi
 group-url https://vpn.capa3.es enable
  • 14 – Conexión del cliente.

Ya hemos llegado al final y nos conectaremos a la VPN usando el cliente VPN SSL Cisco AnyConnect:

cisco-anyconnect

cisco-anyconnect-two-factor-authentication-login

cisco-anyconnect-two-factor-authentication-connected

También podemos conectarnos sin cliente ( Clientless VPN ), usando un navegador:

clientless-vpn-ssl-two-factor-authentication

6 Comments

  • 19/12/2014 - 12:43 pm | Permalink

    Hello,

    multiOTP 4.3.1.1 has been released in December 2014, with a lot of nice functionalities!
    – YubiKeys (from Yubico) support
    – Active Directory and regular LDAP server users synchronization (like Directory Server on Synology NAS)
    – optimized Raspberry Pi edition (much faster than before, using cache, including for the command line implementation)
    – and a lot more new functionalities…

    Best regards,

    Andre Liechti
    Project leader for multiOTP open source

  • RPA
    19/03/2015 - 1:16 pm | Permalink

    2015-03-18 22:36:44 warning capa3 User Error: authentication failed for user capa3
    2015-03-18 22:36:44 warning capa3 User *(authentication typed by the user is 6 chars long instead of 10 chars)
    este es el error que me da cuando trato de autenticar el usuario que podria faltar?

    • 20/03/2015 - 9:13 am | Permalink

      Hola,
      Parece que has descargado una versión posterior de MultiOTP a la del ejemplo del post.
      En las nuevas versiones de MultiOTP viene activado por defecto el prefix-pin y el autenticador espera los 4 dígitos del prefix-pin y el token code de 6 dígitos.

      Para desactivarlo ejecuta multiotp.php con la siguiente opción:
      multiotp.php -config default-request-prefix-pin=0

      O bien modifica el fichero multiotp.ini del directorio “config” y configura la siguiente opción:

      default_request_prefix_pin=0

      Gracias por comentar.
      Saludos,

      Xavi

  • Leopoldo
    25/03/2015 - 2:34 pm | Permalink

    Tengo una consulta espero puedas ayudarme

    He montado un servidor radius pero quisiera que los usuarios deban acceder sus credenciales a traves de una interfaz web y no atraves del gesto de red del SO que utilicen. Es posible esto ??? Saludos

    (un ejemplo con las redes infinitum movil aca en mexico, que debes acceder a la direccion del gateway en el navegador para introducir usuario y contraseña y lograr el acceso)

  • Bob
    13/09/2016 - 2:46 pm | Permalink

    Hola, en mi caso solamente funciona cuando pruebo con los códigos de rescate, pero cuando intento probar con Google Authenticator no funciona. Que puede estar fallando?

    Gracias.

    • 05/01/2017 - 12:15 pm | Permalink

      Hola,
      primero disculpa que no haya contestado antes, no me activé alertas de cuándo hay un comentario pendiente…

      Si sólo funcionan los códigos de rescate el problema apunta a que el token seed es incorrecto o la hora ( en cliente o servidor ) está mal. La respuesta correcta la verás en los logs de multiOTP.

      Gracias por participar en el blog!

  • Leave a Reply

    Your email address will not be published. Required fields are marked *