SSH two-step authentication con Google authenticator su CentOS 6.x e SELinux

centos

Oggi ho lottato duramente contro SELinux, in una sfida uomo-macchina dove alla fine l'uomo è risultato vincitore. 😀 Non sto qui a spiegarvi l'impatto che ha l'autenticazione a due fattori sulla sicurezza, mi limito a dire "something you know, something you have".

Installata la CentOS 6.4 e configurato il webserver passo al SSH server. Cambio la porta, rimuovo l'accesso per l'utente root e limito la connessione solo a certi utenti. Stabilisco i gracetime, le connessioni massime, fail2ban per i brute force e le regole per iptables. Fatto questo passo a configurare il two-step authentification e mi imbatto sempre in "Access Denied". Guardo il log con:

tail /var/log/secure -n 100

e noto:

Jul 16 17:15:32  sshd(pam_google_authenticator)[5646]: Did not receive verification code from user
Jul 16 17:15:32  sshd(pam_google_authenticator)[5646]: Invalid verification code
Jul 16 17:15:32  sshd(pam_google_authenticator)[5646]: Failed to update secret file "/home//.google_authenticator"

Faccio una veloce ricerca su Google e noto che il problema è dovuto a SELinux (Security-Enhanced Linux). Ovviamente col cavolo che lo disabilito solo per far funzionare l'autenticazione a due fattori. I problemi tra SELinux e pam_google_autheticator sono ben noti, fortunatamente esiste un semplice workaround. Approfitto di questo per scrivere un intero post dedicato su come integrare Google Authenticator nel SSH server, sicuramente mi servirà anche a me in futuro. 😀

Sicuramente il pacchetto "pam_google_authenticator" sarà disponibile in qualche repository di terze parti già in forma binaria, io in questo post lo compilerò da git. La prima cosa da fare è attivare il repository EPEL, dando il comando:

su -c 'rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm'

A questo punto sarebbe meglio sincronizzare l'orario del server con un server NTP, dato che token hanno cadenze temporali molto precise. Se non fatto questo potrebbe causare dei problemi durante la fase di autenticazione. Da quello che ho visto il demone ntpd è sempre presente nelle nuove installazioni di CentOS, ad ogni modo basta controllare dando:

sudo service ntpd status

Se l'output è: "ntpd (pid ) is running..." allora tutto ok altrimenti diamo i seguenti comando per installarlo e configurarlo.

sudo yum install ntp           ## install ntp daemon
sudo ntpdate 0.pool.ntp.org    ## sync time with ntp server
sudo service ntpd start        ## start ntp daemon
sudo chkconfig ntpd on         ## set ntpd autostart

Per sicurezza date anche il comando:

date

In questo modo vedrete se la vostra timezone è configurata nel modo, se non lo fosse "change timezone centos" su Google e passa la paura. 😀

Ora installiamo le dipendenze:

sudo yum --enablerepo=epel install gcc gcc++ pam-devel subversion python-devel git

And now:

su
cd /root
git clone https://code.google.com/p/google-authenticator/
cd google-authenticator/libpam/
make
make install
exit

Il "make install" copierà il file pam_google_authenticator.so in /lib64/security/. Fatto questo possiamo passare alla configurazione del server SSH. Il file di configurazione si trova sempre al path /etc/ssh/sshd_config, e oggi mi sono veramente stufato di scriverlo... assieme al compagno pam.d. 🙂

sudo nano /etc/ssh/sshd_config
# Change to no to disable s/key passwords
ChallengeResponseAuthentication yes
# ChallengeResponseAuthentication no
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
# UsePAM no
UsePAM yes

In questo file controllate che i parametri "ChallengeResponseAuthentication" e "UsePAM" siano su yes. ((Un altro consiglio che vi do è quello di cercare su Google "ssh security tips" o "secure ssh" e implementare piccole strategie per evitare attacchi, basta veramente poco per aumentare di parecchio il livello di sicurezza.))

Attiviamo ora Google Authenticator per l'utente specifico sul quale vogliamo effettuare la two-step authentication. Mi raccomando non date il seguente comando come root, altrimenti andrete a creare la configurazione per tale utente. E vi ricordo essere buona norma non utilizzare l'utente root per l'accesso SSH. 😉

google-authenticator

Il wizard vi genererà un link assieme ad un codice. Questi due dati serviranno per accoppiare l'account al token generato. Dopo aver installato Google Authenticator (disponibile anche per iPhone) vi basterà o catturare tramite fotocamera il codice a barre rappresentato dall'URL oppure inserire manualmente il codice.
Oltre a questo vi verranno mostrati una serie di token usa e getta, sempre disponibili all'utilizzo, nel caso non avesse lo smartphone con voi. Se l'unico modo con cui comunicate con il vostro server è tramite SSH vi consiglio di stamparveli. Successivamente vi verranno poste 4 domande:

Do you want me to update your "/root/.google_authenticator" file (y/n) y

Do you want to disallow multiple uses of the same authentication token?
This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) y

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y

A questo punto se NON avete SELinux attivo sarete abbastanza facilitati. Per controllare se tale funzionalità è attiva vi basterà dare:

cat /etc/selinux/config

Se il parametro SELINUX = disabled, continuate a leggere sotto. Se enforcing saltate al paragrafo successivo. Prima però una piccola nota:

NOTA: fate molta attenzione a come procedete ora. Il consiglio spensierato che vi do è quello di aprire una sessione SSH di riserva. Se state lavorando su una VPS e non avete a disposizione un accesso KVM (di fatto quindi la vostra unica interfaccia con il server è SSH) un errore potrebbe escludervi un futuro accesso. Avendo l'accesso secondario potrete sempre commentare la riga che richiede l'autenticazione tramite google_authenticator in /etc/pam.d/sshd. State molto attenti soprattutto se avete SELinux attivo.

SELinux disattivato

sudo nano /etc/pam.d/sshd

In questo file come prima riga valida inseriamo:

#%PAM-1.0
auth       required     pam_google_authenticator.so nullok
auth       required     pam_sepermit.so
auth       include      password-auth
account    required     pam_nologin.so

L'opzione "nullok" permetterà a quei utenti che non hanno l'autenticazione a due fattori attiva di potersi loggare semplicemente inserendo la password, cosa che non sarebbe possibile altrimenti. Alla richiesta del verification code basterà premere Enter.

Riavviate il server SSH and you're good to go...

sudo service sshd restart

SELinux attivo

Nel caso SELinux fosse attivo, procedendo come sopra otterremo l'errore nel log /var/log/secure che ho riportato all'inizio del post.
Per ovviare aprite sempre il file /etc/pam.d/sshd con nano (o vi se siete dei veri nerd) ma questa volta inserite:

sudo nano /etc/pam.d/sshd
#%PAM-1.0
auth       required     pam_google_authenticator.so nullok secret=/home/${USER}/.ssh/.google_authenticator
auth       required     pam_sepermit.so
auth       include      password-auth
account    required     pam_nologin.so

Copiate il file .google_authenticator nella cartella .shh/. Nel caso la cartella non fosse disponibile, createla:

mkdir /home/${USER}/.ssh 
mv /home/${USER}/.google_authenticator /home/${USER}/.ssh/.google_authenticator

Come ultimo comando usiamo chcon che modifica il file di configurazione di SELinux.

chcon -R -t ssh_home_t /home/${USER}/.ssh

Anche in questo caso riavviate il demone ssh.

sudo service sshd restart

Ora sia nel caso SELinux attivo sia nel caso in cui fosse disattivo, aprite una nuova sessione SSH e provate il login, se tutto funziona...OTTIMO. Se vi ritrovaste con il login incasinato potete sempre usare la sessione SSH di riserva per disattivare l'autenticazione a due fattori o correggere l'inghippo.

Ho trovato anche questo howto. In questo caso il file di configurazione per Google Authenticator viene inserire in una cartella di sistema e non più nella home. Sicuramente migliore dal punto di vista della sicurezza. Personalmente non lo ho provato, sarà per il prossimo giro. 😉