La autenticación de dos factores (2FA) exige algo que tienes además de algo que sabes. En SSH, la combinación más práctica es una clave SSH más un código temporal de seis dígitos (TOTP) generado en el móvil: aunque alguien robe tu clave privada, no podrá entrar sin el teléfono. En este tutorial instalarás el módulo PAM de Google Authenticator en Ubuntu 24.04, lo configurarás para tu usuario y ajustarás SSH para que pida clave y código. Los códigos funcionan con cualquier aplicación TOTP: Google Authenticator, Aegis, Microsoft Authenticator, 1Password o Bitwarden.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudoque ya entra por clave SSH. Si aún usas contraseña, sigue primero la guía de gestión de claves SSH de esta sección. - Un móvil con una aplicación TOTP instalada.
- Acceso a la consola web de tu proveedor por si te quedas fuera, y una sesión SSH abierta durante todo el proceso.
Advertenciano cierres la sesión SSH con la que trabajas hasta haber comprobado el acceso con 2FA desde una segunda terminal.
Paso 1: Comprobar la hora del servidor
Los códigos TOTP dependen de la hora: el servidor y el móvil calculan el mismo código a partir de un secreto compartido y del reloj. Si el reloj del servidor se desvía más de un minuto o dos, los códigos se rechazarán. Comprueba que está sincronizado:
timedatectl
Local time: Fri 2026-09-25 10:40:12 UTC
Universal time: Fri 2026-09-25 10:40:12 UTC
RTC time: Fri 2026-09-25 10:40:12
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
Si System clock synchronized es no, activa la sincronización:
sudo timedatectl set-ntp true
Paso 2: Instalar el módulo PAM de Google Authenticator
El paquete libpam-google-authenticator incluye el módulo PAM que verifica los códigos y la herramienta google-authenticator que genera el secreto de cada usuario:
sudo apt update
sudo apt install libpam-google-authenticator
Verifica que el módulo está en su sitio:
ls /usr/lib/x86_64-linux-gnu/security/pam_google_authenticator.so
En servidores ARM la ruta es /usr/lib/aarch64-linux-gnu/security/.
Paso 3: Generar el secreto para tu usuario
Ejecuta google-authenticator con tu usuario, sin sudo: el secreto se guarda en ~/.google_authenticator del usuario que ejecuta el comando. Las opciones evitan las preguntas interactivas:
google-authenticator -t -d -f -r 3 -R 30 -w 3
| Opción | Efecto |
|---|---|
-t | Códigos basados en tiempo (TOTP), los que usan las aplicaciones móviles. |
-d | Cada código solo se puede usar una vez. |
-f | Escribe el archivo sin pedir confirmación. |
-r 3 -R 30 | Máximo 3 intentos de inicio de sesión cada 30 segundos. |
-w 3 | Acepta el código actual, el anterior y el siguiente para tolerar pequeñas desviaciones de reloj. |
La herramienta muestra un código QR en la terminal, la clave secreta y te pide un código para confirmar:
Warning: pasting the following URL into your browser exposes the OTP secret to Google:
https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/your_user@web01%3Fsecret%3D...
[código QR]
Your new secret key is: JBSWY3DPEHPK3PXPJBSWY3DPEH
Enter code from app (-1 to skip): 482913
Code confirmed
Your emergency scratch codes are:
18273645
90817263
55120938
71029384
46382910
Escanea el código QR con la aplicación del móvil (si la terminal no lo muestra bien, amplía la ventana o introduce la clave secreta a mano). Después escribe el código de seis dígitos que muestra la aplicación.
Importanteguarda los cinco códigos de emergencia en tu gestor de contraseñas. Cada uno sirve una sola vez para entrar si pierdes el móvil. No abras la URL de Google que aparece en la salida: expone tu secreto a un tercero.
Comprueba que el archivo existe y que solo tu usuario puede leerlo:
ls -l ~/.google_authenticator
-r-------- 1 your_user your_user 146 Sep 25 10:45 /home/your_user/.google_authenticator
Paso 4: Configurar PAM para SSH
PAM decide qué se pide durante la autenticación. En Ubuntu, /etc/pam.d/sshd incluye common-auth, que solicita la contraseña del sistema. Como el primer factor será la clave SSH, sustituye esa línea por el módulo de Google Authenticator. Haz primero una copia:
sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.bak
sudo nano /etc/pam.d/sshd
Comenta la línea @include common-auth y añade debajo el módulo:
# Standard Un*x authentication.
#@include common-auth
auth required pam_google_authenticator.so nullok
La opción nullok permite entrar sin código a los usuarios que todavía no han ejecutado google-authenticator. Es útil para desplegar 2FA de forma gradual en servidores con varios usuarios; la quitarás en el paso 7.
Paso 5: Configurar SSH para exigir clave y código
Ahora indica a sshd que use el método keyboard-interactive (el que pasa por PAM y pide el código) y que exija los dos métodos en orden: primero clave pública y después código. Crea un archivo de configuración adicional:
sudo nano /etc/ssh/sshd_config.d/10-2fa.conf
UsePAM yes
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
En Ubuntu 24.04 el archivo principal /etc/ssh/sshd_config incluye KbdInteractiveAuthentication no, pero los archivos de sshd_config.d/ se leen antes y, para cada opción, gana el primer valor encontrado. Si en ese directorio tienes otro archivo que se ordene antes que 10-2fa.conf y ponga KbdInteractiveAuthentication no, elimina esa línea de él.
Valida la sintaxis y comprueba la configuración efectiva:
sudo sshd -t
sudo sshd -T | grep -Ei '^(usepam|kbdinteractiveauthentication|authenticationmethods|passwordauthentication) '
usepam yes
kbdinteractiveauthentication yes
passwordauthentication no
authenticationmethods publickey,keyboard-interactive
Aplica los cambios sin cerrar tu sesión:
sudo systemctl reload ssh
Paso 6: Probar el acceso con 2FA
Abre una nueva terminal en tu equipo local, sin cerrar la sesión actual, y conéctate:
ssh your_user@your_server_ip
Tras autenticarse con la clave, SSH pedirá el código de la aplicación:
(your_user@your_server_ip) Verification code:
Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-84-generic x86_64)
Comprueba en el registro del servidor que se usaron los dos métodos:
sudo journalctl -u ssh -n 20 --no-pager | grep Accepted
sshd[3120]: Accepted publickey for your_user from 198.51.100.7 port 52114 ssh2: ED25519 SHA256:mQ3b7l1xT0H9...
sshd[3120]: Accepted keyboard-interactive/pam for your_user from 198.51.100.7 port 52114 ssh2
Prueba también que un código incorrecto se rechaza. Si todo funciona, ya puedes cerrar la sesión antigua. Si algo falla, restaura la configuración desde la sesión que dejaste abierta:
sudo cp /etc/pam.d/sshd.bak /etc/pam.d/sshd
sudo rm /etc/ssh/sshd_config.d/10-2fa.conf
sudo systemctl reload ssh
Paso 7: Exigir 2FA a todos los usuarios
Cada usuario que entre por SSH debe ejecutar google-authenticator en su propia sesión (paso 3) y escanear su propio código. No generes los secretos por ellos: el secreto es personal y debe quedarse solo en su móvil.
Para ver qué usuarios con shell aún no tienen 2FA configurado:
for home in /home/*; do sudo test -f "$home/.google_authenticator" || echo "Sin 2FA: $home"; done
Cuando todos lo tengan configurado, quita nullok de /etc/pam.d/sshd para que ningún usuario pueda entrar sin código:
sudo nano /etc/pam.d/sshd
auth required pam_google_authenticator.so
Los cambios en PAM se aplican en el siguiente inicio de sesión, sin reiniciar SSH. Vuelve a probar desde una nueva terminal.
Paso 8 (opcional): No pedir código desde la red interna
Si administras el servidor desde una red privada de confianza, por ejemplo una VPN, puedes omitir el código para esas direcciones con pam_access. Crea el archivo de reglas:
sudo nano /etc/security/access-2fa.conf
+ : ALL : 10.8.0.0/24
- : ALL : ALL
Sustituye 10.8.0.0/24 por tu red interna. Después añade una línea en /etc/pam.d/sshd justo antes del módulo de Google Authenticator:
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-2fa.conf
auth required pam_google_authenticator.so
Si la IP de origen coincide con una regla +, PAM salta la línea siguiente y no pide el código; en cualquier otro caso continúa y lo exige. La clave SSH sigue siendo obligatoria siempre. Comprueba ambos casos: desde la red interna no debe pedir código y desde fuera sí.
Recuperar el acceso si pierdes el móvil
- Con un código de emergencia: escríbelo cuando SSH pida
Verification code. Cada código solo vale una vez; después genera un secreto nuevo congoogle-authenticatorpara tener códigos frescos. - Sin códigos de emergencia: otro administrador puede borrar tu archivo con
sudo rm /home/your_user/.google_authenticator. Sinullokestá activo entrarás solo con la clave; si no, el administrador debe regenerarlo contigo. Si no hay otro administrador, entra por la consola web del proveedor, que no pasa por SSH.
Solución de problemas
Los códigos se rechazan siempre. Casi siempre es la hora. Comprueba timedatectl en el servidor y que el móvil tiene la hora automática activada. Revisa el registro:
sudo journalctl -u ssh -n 30 --no-pager | grep -i google
Un mensaje Invalid verification code con la hora correcta indica que escaneaste un QR antiguo; genera el secreto de nuevo.
SSH pide la contraseña además del código. La línea @include common-auth sigue activa en /etc/pam.d/sshd. Coméntala como en el paso 4.
SSH no pide el código y entra solo con la clave. Comprueba con sudo sshd -T | grep -i -E 'authenticationmethods|kbdinteractive' que los valores son los del paso 5; otro archivo de sshd_config.d/ puede estar sobrescribiéndolos. Si los valores son correctos y el usuario no tiene ~/.google_authenticator, es el efecto de nullok.
Permission denied (keyboard-interactive) con el código correcto. El módulo no puede leer el secreto. El archivo debe pertenecer al usuario y tener permisos 400 o 600:
chmod 400 ~/.google_authenticator
Tras un código válido, el siguiente inicio de sesión falla. Con -d cada código se usa una sola vez; espera a que la aplicación muestre uno nuevo (30 segundos).
Conclusión
Has instalado el módulo PAM de Google Authenticator, generado tu secreto TOTP con códigos de emergencia y configurado SSH para exigir clave pública y código, con un despliegue gradual mediante nullok y una excepción opcional para la red interna. A partir de ahora, una clave privada robada no basta para entrar en el servidor.
Como siguientes pasos puedes:
- Revisar el resto de la configuración del servidor con una auditoría de seguridad o con Lynis.
- Instalar Fail2ban para bloquear las IP que acumulan intentos fallidos.
- Sustituir el TOTP por llaves de seguridad FIDO2 con claves
ed25519-sk.
