Implementar 2FA a SSH amb Google Authenticator o pam_oath
Activar segon factor en SSH és una cosa que molts equips es plantegen, però que pocs implementen bé. No perquè sigui especialment complex, sinó perquè els detalls se solen deixar de banda. Una mala decisió en la integració de PAM, una configuració de sshd_config mal entesa, o una elecció inadequada del backend d’OTP poden acabar bloquejant accessos legítims o deixant forats que et fan pensar que tens 2FA quan en realitat no el tens.
En aquest article comparteixo com abordar la implementació de 2FA en sistemes amb accés SSH administrat per humans —nodes d’administració, salts, bastions— fent servir Google Authenticator (libpam-google-authenticator) o pam_oath. No són solucions perfectes, però ambdues funcionen bé en contextos específics si s’ entenen les seves limitacions i es configuren amb cura.
Triar entre google-authenticator i pam_oath
Tots dos fan servir TOTP (Time-based One-Time Passwords) i poden integrar-se amb PAM, però les seves diferències tenen implicacions pràctiques importants:
- google-authenticator genera els secrets en el propi node, un per usuari, i els guarda en $HOME/.google_authenticator. Cada usuari ha d’executar un binari per a inicialitzar el seu token.
- pam_oath, en canvi, treballa amb un arxiu centralitzat (normalment /etc/users.oath) on es llisten tots els tokens. Això ho fa més manejable per a entorns amb molts usuaris i amb gestió centralitzada.
En entorns amb comptes personals i no massa usuaris per node, google-authenticator va bé, sempre que puguis controlar el procés d’enrolament (no deixar que l’usuari ho configuri sol). Per a clusters més grans, on prefereixes tenir tots els secrets en un lloc que puguis versionar, donar suport i auditar, pam_oath és més adequat. Això sí, exigeix tenir clar com protegir l’arxiu central i com distribuir canvis si fas servir automatització.
Un advertiment: si fas servir pam_oath, assegura’t que el mòdul PAM s’instal·la des d’un paquet mantingut. En algunes distros (com Ubuntu), libpam-oath es manté una mica endarrerit respecte a la versió upstream, i de vegades hi ha bugs no corregits al parser de l’arxiu users.oath.
Controlar l’ ordre i la interacció de PAM a SSH
Una de les causes més comunes de fallades en integrar 2FA en SSH és no entendre com interactuen sshd, PAM i els mètodes d’autenticació habilitats. La clau està en el paràmetre AuthenticationMethods de sshd_config.
Exemple típic:
AuthenticationMethods publickey,keyboard-interactive
Això significa que l’usuari ha de passar primer l’autenticació per clau pública, i després la fase de PAM interactiu (on hi ha el segon factor).
Si no defineixes AuthenticationMethods, però tens UsePAM yes, SSH permet que amb una sola clau vàlida i un login exitós en PAM l’usuari entri. El resultat és confús si tens tant PasswordAuthentication yes com ChallengeResponseAuthentication yes: l’usuari pot triar entre contrasenya i OTP, en comptes d’haver de passar per tots dos.
Podem fer servir:
PasswordAuthentication no
ChallengeResponseAuthentication yes
UsePAM yes
AuthenticationMethods publickey,keyboard-interactive
Això força a fer servir clau pública + OTP. Així evites passwords i no permets que algú se salti el segon factor si té només la clau SSH.
google-authenticator: detalls que convé no ignorar
Quan es configura google-authenticator per usuari, és habitual fer servir el binari interactiu google-authenticator per a generar un secret, guardar l’arxiu .google_authenticator i escanejar el codi QR amb l’app de torn.
Però hi ha dos problemes aquí si no ho automatitzes:
- Els usuaris poden canviar el seu token sense control. L’arxiu és al seu $HOME, i el binari permet regenerar el secret.
- El procés és manual i no queda auditat. Si necessites complir amb traçabilitat o provisioning clar, aquest mètode no escala.
Podem fer servir el binari només en la fase inicial per a generar els tokens, i després moure l’arxiu .google_authenticator al home de l’usuari amb permisos 0400, propietat de l’usuari, però sense possibilitat de modificar-lo sense privilegis. També eliminem el binari després de la configuració (chmod 000 /usr/bin/google-authenticator o equivalent) perquè no es pugui fer servir després.
Una altra opció que funciona bé és tenir un script d’enrolament que genera els tokens de forma externa, guarda una còpia dels secrets i els instal·la amb la configuració desitjada. Això permet tenir backups, control d’accés, i evitar que l’enrolament quedi en mans de l’usuari.
pam_oath: avantatges en entorns centralitzats
Amb pam_oath, defineixes tots els tokens en un únic arxiu:
HOTP/T30/6 user - <secret>
Això et permet tenir control central i evitar que els usuaris gestionin el seu token. També et dona la possibilitat de rotar secrets de forma més senzilla (amb automatització) i auditar qui té què.
El format de l’arxiu és molt estricte. Un error en un espai o en els permisos (0600, root:root) pot fer que el mòdul falli sense avisar. Lo normal és que PAM rebutgi el login amb un simple “Authentication failed”.
Un altre punt important: si fas servir LDAP o comptes centralitzats, necessites assegurar-te que pam_oath s’invoqui només per a usuaris que s’han d’autenticar amb OTP. Hi ha formes de filtrar això a PAM, però és fàcil equivocar-se. Un error clàssic és bloquejar comptes de servei que fan servir clau pública sense necessitat d’OTP, perquè el mòdul pam_oath s’executa sempre i no distingeix.
Una forma pràctica d’evitar-ho és posar alguna cosa com això a /etc/pam.d/sshd:
auth [success=1 default=ignore] pam_succeed_if.so usuari ingroup otpusers
Es requereix autenticació pam_oath.so usersfile=/etc/users.oath window=30 dígits=6
Així, només els usuaris al grup otpusers estan obligats a passar per pam_oath. Els altres segueixen la ruta normal.
La sincronització horària no és opcional
Tant google-authenticator com pam_oath fan servir TOTP, cosa que implica que l’hora del servidor ha d’estar ben sincronitzada. És temptador assumir que ntpd o systemd-timesyncd ja ho fan, però no sempre és així.
He vist servidors on el dimoni NTP estava actiu però no sincronitzat (per firewalls, DNS mal resolt o servidors inabastables), i els tokens deixaven de funcionar sense avís clar. Instal·la sempre una comprovació explícita en els health checks que avisi si l’òfset supera els 10 segons.
En sistemes amb molt moviment, també convé augmentar la “finestra” de tolerància. Tant google-authenticator com pam_oath permeten definir quants intervals de 30 segons cap endavant i enrere accepten. Un valor raonable en producció sol ser 3 o 4. Més que això pot obrir la porta a atacs per tokens.
El que mai hauries de fer
He vist implementacions amb google-authenticator activat via PAM, però amb AuthenticationMethods mal configurat, cosa que permet a un usuari autenticar-se només amb OTP si no té clau SSH. Això anul·la la seguretat esperada: es pensava que 2FA era “clau SSH + OTP”, però en realitat és “una de les dues”.
Un altre error és pensar que 2FA protegeix comptes root, sense haver deshabilitat PermitRootLogin. Encara que tinguis 2FA actiu, permetre login directe a root continua sent mala idea. L’accés hauria de ser sempre a comptes personals amb privilegis escalats.
I una menció especial per als qui instal·len 2FA només en sshd, però després tenen habilitat su o sudo sense MFA. Un cop dins, s’ acaba la protecció. Si el segon factor és necessari per política, cal estendre’l també a l’escalament de privilegis. Cal tenir pam_oath o pam_google_authenticator també a /etc/pam.d/sudo, amb filtres per grup per evitar trencar scripts.
Gestió d’ emergència
Tenir 2FA està bé. No poder entrar al node quan alguna cosa es trenca, no tant. Sempre hem de mantenir almenys un mètode d’ accés d’ emergència:
- Accés per consola virtual (iDRAC, IPMI, etc.)
- Usuaris exclosos del 2FA, molt controlats
- Claus SSH de break-glass en sistemes separats
I documentar com desactivar 2FA temporalment si el servidor queda fora d’hora, l’arxiu de tokens es corromp o PAM falla. En situacions crítiques, tenir clar quin arxiu moure o quina línia comentar a sshd_config pot estalviar una hora amb el sistema aturat.
Aplicar 2FA en SSH no és difícil, però fer-ho bé implica entendre com interactuen les peces. Cada decisió —des del backend OTP fins a l’ordre en PAM— afecta no només la seguretat, sinó també l’operació diària. Amb cura, es pot tenir un segon factor funcional, traçable i sense afectar massa els usuaris. Però no convé confiar-se: hi ha moltes formes de fer-ho malament sense adonar-te’n.

