Openvpn con autenticazione TOTP
Questa è una configurazione per un server openvpn su Linux (provato su Debian e Openwrt) con autenticazione basata su certificato e su un codice TOTP. E` una soluzione mediamente semplice, basata su uno shell script e files di testo. Per scelta non chiedo all'utente sia una password fissa che un codice variabile, ma solo il codice variabile. Visto che per autenticarsi l'utente usa sia un certificato che il codice variabile, ritendo che inserire anche una password sia superfluo.
Principio di funzionamento: Prima di tutto Openvpn autentica il certificato (e se non è valido non si procede in ogni caso), poi verifica che lo username sia uguale al common name del certificato, e infine verifica il codice TOTP. Se tutte le verifiche passano, l'utente è autenticato. Siccome ogni tot tempo (default 1 ora) Openvpn ristabilisce la connessione e rigenera le chiavi simmetriche effimere di crittografia, l'autenticazione a quel punto verrebbe rifatta e fallirebbe perché nel frattempo il codice TOTP è cambiato. Per evitare che questo succeda, una volta stabilita la connessione viene scambiato un cookie di autenticazione che viene usato per tutte le riconnessioni successive, fino a che non viene chiusa definitivamente la connessione.
Notare che io volutamente richiedo che ci sia un certificato personale per ogni utente. Sarebbe possibile volendo usare un unico certificato per tutti gli utenti e autenticare i singoli utenti basandosi su username e TOTP, però questo a mio avviso rende meno sicuro il sistema in quanto rende possibile accedere possedendo un certificato rubato ad un utente e un codice TOTP rubato ad un altro utente. Inoltre una volta che per qualche motivo venisse compromesso il certificato utente (si presume che ce ne sia uno solo usato da tutti) o continuo a usare un certificato compromesso oppure devo revocarlo e quindi dare un nuovo certificato a tutti gli utenti, e non solo ad un singolo utente.
Configurazione openvpn lato server
Lato server, uso una configurazione molto simile a quella di ../Openvpn_Con_Certificati, alla quale faccio alcune modifiche per gestire la doppia autenticazione.
Le modifiche nel dettaglio sono queste:
- Non posso usare i comandi "user nobody / group nogroup" in quanto non potrei più eseguire lo script che autentica il codice TOTP una volta che ho fatto il downgrade dei privilegi. Al momento mi limito a non farlo e girare come root, potrei nel caso scegliere di usare un utente che non sia nobody e che possa eseguire lo script e leggere il file che contiene i dati per il TOTP.
- Sempre per lanciare lo script che controlla il codice TOTP, devo impostare script-security a 2 e definire una directory temporanea dove scrivere dei files (idealmente un ramdisk)
- Per evitare che allo scadere della sessione di validità della connessione (come spiegato prima) openvpn riprovi ad autenticarmi con un codice TOTP che fallirebbe, imposto un cookie di sessione con comando "auth-gen-token"
- Per avere più informazioni nel file di stato, che contengano oltre al common name del certificato anche lo username, uso il comando "status-version 2" (questa parte è assolutamente facoltativa)
La configurazione risultante è questa:
port 1194 proto udp dev tun0 # Certificati e chiavi tls-server dh dh.pem ca ca.crt cert server.crt key server.key # rete per l'interfaccia privata. il server si prende il .1 e # i clienti gli altri indirizzi di questa subnet. # Qui si usa una rete che non sia quella locale del server. server 192.168.99.0 255.255.255.0 # nuova logica di assegnazione degli ip e subnet ai client: # funziona SOLO con openvpn 2.5 in avanti topology subnet # tengo traccia degli indirizzi assegnati ai server e riassegno sempre # quelli usando questo file. (facoltativo) ifconfig-pool-persist server-pool-cache.txt # configurazioni di routing da insegnare ai client che si collegano. # in pratica questo comando dice al client che 192.168.0.0/24 (la mia LAN) va mandato alla vpn # posso ripetere piu` righe con piu` reti/host, e verranno tutti impostati nel routing del client # nel momento in cui questo si collega. push "route 192.168.1.0 255.255.255.0" # configurazioni varie da insegnare ai client. Anche in questo caso, insegno al client # che deve usare il mio DNS mentre è connesso alla VPN, che il dominio interno è "pippo.locale" # e che c'e` un server WINS. Ovviamente il WINS e` facoltativo, così come anche gli altri parametri, # però se vogliamo che il client risolva i nomi interni, sarà il caso di metterli. push "dhcp-option DNS 192.168.1.1" push "dhcp-option DOMAIN pippero.internal" # workaround per i provider stronzi che rispondono con un loro ip interno alle richieste dns # che dovrebbero dare nxdomain. Inoltre, evita anche il leak di richieste DNS. # si applica SOLO a client da windows da 7 in poi. Non dovrebbe rompere comunque i client diversi. push "block-outside-dns" # consento ai clients di vedersi fra di loro (se non volete, togliete la riga) client-to-client # keepalive e timeout: ping ogni 30 secondi e timeout di 120 secondi. keepalive 30 120 # in caso di reload, tieni in memoria i dati che non potresti piu` rileggere a causa del privilegio insufficiente persist-key persist-tun # file che indica lo stato delle connessioni, ad uso di chi lo vuole leggere status server-status.log status-version 2 # log verboso per debug (si puo` togliere) verb 4 # per gestire il sistema TOTP come spiegato prima script-security 2 auth-user-pass-verify /etc/openvpn/oath.sh via-file tmp-dir /tmp auth-gen-token
Ovviamente qui non spiego come generare i certificati, ecc. Fate riferimento alla pagina ../Openvpn_Con_Certificati
Script per autenticare con TOTP lato server
Questo è il contenuto dello script "oath.sh" che è indicato nella configurazione di Openvpn. Questo script legge un due files: uno e` il file "oath.secrets" che contiene username e seed per generare il codice TOTP, e l'altro gli viene passato da openvpn (dentro la tmp-dir) e contiene user e codice TOTP che l'utente ha inserito per autenticarsi. Questo script va adattato indicando il percorso del file "oath.secrets" (nome e percorso sono a vostra scelta, io lo tengo dentro /etc/openvpn)
Questo script richiede di avere installato il programma "oathtool", che esiste come pacchetto sia in debian che in openwrt.
#!/bin/bash # script che verifica i codici TOTP su un file di testo passfile=$1 # Leggo i dati che mi passa openvpn via file temporaneo user=$(head -1 $passfile) pass=$(tail -1 $passfile) # Verifico che lo username contenga solo caratteri validi if ! [[ "$user" =~ ^[0-9a-zA-Z._@-]+$ ]]; then exit 1 fi # Verifico che la password contenga solo numeri if ! [[ "$pass" =~ ^[0-9]+$ ]]; then exit 1 fi # Verifico che lo username che ho ricevuto sia uguale al # common name del certificato con il quale l'utente si connette # questa e` doppia paranoia. if ! [[ "$user" = "${common_name}" ]]; then logger -s openvpn_MFA "Common name mismatch: Access denied for $user (common name: ${common_name})" exit 1 fi # Cerco lo username in oath.secrets ed estraggo la stringa # che contiene la chiave per generare il codice TOTP (in base32) # QUI NEL CASO IMPOSTATE IL PATH O IL NOME DEL FILE COME VOLETE VOI secret=$(grep -m 1 "^$user:" oath.secrets | cut -d: -f2) # Calcolo il codice TOTP e vedo se coincide # se coincide esco con exit code 0, altrimenti con exit code 1 code=$(oathtool --totp -b $secret) if [ "$code" = "$pass" ]; then logger -s openvpn_MFA "Access granted for $user (common name: ${common_name})" exit 0 fi exit 1
Script per generare le righe da inserire nel file oath.secrets
Il file oath.secrets contiene un utente per riga, nella forma username:seme del TOTP. Il carattere che separa i campi e` il "due punti" e quindi è vietato creare degli username che contengano quel carattere. In ogni caso Openvpn per motivi di sicurezza non consente di usare caratteri che non siano lettere numeri e i caratteri . _ @ - (punto, underscore, chiocciola, e meno). Quindi per i vostri username usate lettere numeri e solo questi simboli. Gli username sono case sensitive.
Per generare il seed per il TOTP uso il programma "oathtool" che peraltro è anche quello che uso per verificare i codici TOTP in fase di autenticazione. Per rendere più comodo impostare il codice sulla app del cellulare, uso qrencode per generare dei QR code che vengono salvati come "username.png"
Questo script, che non è necessario che giri sullo stesso server dove gira openvpn, richiede che siano installati i seguenti programmi: oathtool, pwgen, base32, qrencode. Sono tutti disponibili come pacchetti debian e anche su openwrt.
Se usiamo questo script sulla stessa macchina dove si trova openvpn possiamo direttamente fargli scrivere il file oath.secrets che viene letto da openvpn, in alternativa dovremo copiare a mano gli utenti da una macchina all'altra.
Notare che questo script semplicemente accoda una riga ogni volta al file oath.secrets, e non è in grado né di cancellare né di modificare utenti esistenti. Per farlo dovrete manomettere il file a mano.
#!/bin/bash # uso: creautente <username> # Qui posso hardcodare un issuer per le chiavi che genero # notare che va urlencoded quindi per dire uno spazio diventa %20 # è puramente estetico, viene scritto nella app del cellulare quando si legge il QR code. # non ha alcuna funzione nella autenticazione. issuer='Nome%20Azienda' # Se non ho esattamente un argomento, esco con un errore if [ "$#" -ne 1 ]; then echo "Numero di argomenti errato" echo "USO: $0 <username>" echo "Esempio: $0 mario.rossi" exit 1 fi # Se il nome utente contiene qualcosa che non siano i caratteri accettati da openvpn, non procedo if ! [[ "$1" =~ ^[0-9a-zA-Z._@-]+$ ]]; then echo "Errore, lo username può contenere solo lettere, numeri, e questi caratteri speciali: _ - . @" echo "Non sono accettate le lettere accentate." echo "Gli emoji sono FUORI DISCUSSIONE" exit 1 fi # se esiste già un file di QR code con lo stesso nome dell'utente che sto creando, esco con un errore if [ -f $1.png ]; then echo "Errore: esiste già un file di QR code con nome $1.png." exit 1 fi # creiamo una stringa per il TOTP di 9 caratteri e la convertiamo in base32 # uso 9 caratteri che producono 16 caratteri in base32, e` lo standard che usa MS totp=`pwgen -sy 9 1 | base32` echo "Creo utente $1 con otp-secret $totp" echo "Il QR code relativo è creato in un file con nome $1.png" echo "la riga per aggiungere l'utente su openvpn è accodata al file oath.secrets" qrencode -o $1.png -d 300 -s 10 "otpauth://totp/$issuer:$1?secret=$totp" echo "$1:$totp" >> oath.secrets exit 0
Configurazione lato client
Lato client, la configurazione è di nuovo simile a quella usata in ../Openvpn_Con_Certificati con poche modifiche per renderla compatibile con la necessità di inserire username e password. Notare che il client openvpn per Windows permette di salvare lo username (lo fa per default, senza dover impostare nulla) e anche di salvare la password. Evitate di salvare la password perché la password è il codice TOTP che cambia ogni volta.
client dev tun proto udp nobind float remote hostname_del_server_vpn # files con le chiavi utente ca ca.crt cert testclient.crt key testclient.key # il certificato del server deve essere di tipo "server" # questo evita che chi possiede un certificato client possa spacciarsi per server remote-cert-tls server #log verboso (se ci interessa) verb 3 # Queste config sono specifiche per TOTP persist-tun auth-user-pass auth-nocache auth-retry interact reneg-sec 0 hand-window 15
Configurazione app sul cellulare
È possibile usare qualsiasi app che possa leggere i QR code per generare i codici TOTP. Le app che ho provato personalmente sono "Aegis" e "2FAS Auth", ma dovrebbero funzionare anche Google Authenticator e Microsoft Authenticator.