Seguridad SSH en 2026: TOTP, FIDO2 y la pila 2FA que resiste phishing
TL;DR — Lo que vas a tener al final
- Tiempo de setup: 1-2 horas para un server nuevo.
- Requisitos: Acceso root, 2 sesiones SSH abiertas durante cambios.
- Resultado: Login por clave ed25519, fail2ban activo, 99% de ataques bloqueados.
- Troubleshooting: Consola del proveedor, journalctl -u sshd, fail2ban-client status.
Por qué importa endurecer SSH con 2FA en 2026
El escenario ya no es “credential stuffing contra root:Welcome2024!“. En 2026 los atacantes van contra lo que protege al sysadmin que SÍ se molestó en configurar PasswordAuthentication no y migró a claves ed25519: el robo de la clave privada del backup sincronizado en iCloud/Google Drive, la exfiltración desde una laptop sin cifrado, y los ataques de phishing dirigido donde te hacen tipear tu passphrase en una terminal falsa. Una sola clave SSH robada equivale a tener una shell en cada server donde esa clave está autorizada. El segundo factor (TOTP o FIDO2) cierra ese vector aunque la clave privada esté comprometida.
Esta guía asume que ya leíste la guía base de 2FA con Google Authenticator y cubre lo que esa guía no cubre: hardware keys FIDO2 (YubiKey, OnlyKey, Token2), alternativas modernas al app de Google Authenticator (Aegis, Ente Auth, Bitwarden Auth), enforcement organizacional con Ansible/IdM, gotchas en clouds (AWS, GCP, Hetzner, Azure), y cómo no quedar afuera en un ambiente con 50 servers cuando perdés el dispositivo 2FA.
Tabla de decisión: TOTP vs FIDO2 vs llave de seguridad
| Método | Resistente a phishing | Requiere hardware extra | Funciona offline | Compatible con OpenSSH | Mejor para |
|---|---|---|---|---|---|
| TOTP (Google Authenticator, Aegis) | No | No (solo teléfono) | Sí | Sí (vía PAM) | Sysadmin individual, servers chicos |
| FIDO2 / WebAuthn (YubiKey, OnlyKey) | Sí (origin-bound) | Sí | Sí | Sí (>= 8.2p1) | Sysadmins que viajan, alta seguridad |
| FIDO2 resident key (clave vive en el token) | Sí | Sí | Sí | Sí (>= 8.2p1 con libsk) | Acceso desde máquinas no confiables (cibercafés, kioskos) |
| Llave SSH ed25519 + passphrase larga | No | No | Sí | Sí (nativo) | Primera línea; combinar con 2FA |
Regla general: si solo puedes implementar una cosa, haz ed25519 + passphrase larga. Si puedes agregar una segunda, agrega TOTP (costo cero, una app en el teléfono). Si tu trabajo es de infra crítica, saltá directo a FIDO2 con YubiKey: la resistencia a phishing vale el costo de la llave.
Setup TOTP moderno: pasar de Google Authenticator a Aegis o Ente Auth
Google Authenticator sigue funcionando pero tiene dos problemas reales en 2026: (1) desde la versión de mayo 2024 sincroniza tus secrets a Google Cloud (opt-out posible pero escondido en settings), y (2) no tiene export ni backup automático del archivo ~/.google_authenticator del server. Si un sysadmin rota de teléfono, tiene que regenerar el secret en cada server uno por uno.
Aegis (Android, F-Droid, código abierto auditable) y Ente Auth (multiplataforma, E2E encrypted sync opcional) resuelven eso: export/import estándar, backup encriptado, restore desde QR. La configuración del lado del server es idéntica al post de Google Authenticator — solo cambiás la app del teléfono.
# Instalar Aegis desde F-Droid (sideload o repo)
# https://getf-droid.org/
# O Ente Auth desde store oficial (iOS/Android)
# https://ente.io/auth/
# El comando del server es el mismo:
google-authenticator
# Respondé y a todo menos "increase window" (decí n)
# Escaneá el QR desde Aegis/Ente Auth en lugar de Google Authenticator
# verifica que el código que muestra la app sea aceptado por ssh
Antes de probar nada: abrí una segunda sesión SSH y dejala abierta. Si algo rompe, puedes hacer cp /etc/pam.d/sshd.bak /etc/pam.d/sshd desde ahí. Sin segunda sesión, cualquier cambio de PAM te puede dejar afuera del server. Eso es ley, no sugerencia.
Verificación rápida (clave: que tu server acepte el código sin cerrar la sesión actual):
# Terminal 1: la que ya tienes abierta (no la cierres hasta verificar)
# Terminal 2: nueva sesión
ssh -o PreferredAuthentications=keyboard-interactive -o PubkeyAuthentication=no usuario@servidor
# Si te pide "Password:" y después "Verification code:" y aceptás → 2FA funcionando
# Si te deja entrar sin pedir código → nullok está activado y/o el PAM no está cargando el módulo
grep nullok /etc/pam.d/sshd
ls -la ~/.google_authenticator
FIDO2 con YubiKey: SSH con llave física (origin-bound)
La diferencia técnica entre TOTP y FIDO2 es que TOTP es “algo que sabes rotar” (un número) y FIDO2 es “algo que tienes” (la YubiKey). Pero la diferencia operativa es más grande: FIDO2 está pegado al origen (hostname). Si alguien te hace tipear tu código TOTP en un portal de phishing, lo tiene. Si te hace conectar a un server falso con tu YubiKey, la YubiKey detecta que el origen no es el server real y rechaza la auth.
OpenSSH soporta FIDO2 nativo desde 8.2p1 (febrero 2020). Ubuntu 22.04+, Debian 12+, RHEL 9+, Fedora 36+ ya lo traen. El módulo PAM es libfido2, no libpam-google-authenticator. La llave que generas vive en el token:
# Verificar soporte FIDO2 en el cliente (la laptop desde donde te conectás)
ssh -V
# OpenSSH_9.6p1, OpenSSL 3.0.13 — sí soporta (>= 8.2p1)
# Si la versión es vieja, actualiza el cliente primero
# Enchufe la YubiKey, generá clave SSH con tipo "sk" (security key)
ssh-keygen -t ed25519-sk -O resident -O verify-required
# -O resident: la clave privada vive en el YubiKey (no se copia al disco)
# -O verify-required: requiere touch físico cada vez (anti-robo)
# Esto genera:
# ~/.ssh/id_ed25519_sk.pub (la parte pública, lo que subís al server)
# La privada queda en el token, no en el disco
# Subir la pública al server (AuthorizedKeysFile)
ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub usuario@servidor
Una vez subida, conectarte desde otra máquina sin tu YubiKey es imposible: la clave privada no existe en disco, solo en el token. Eso es lo que te protege del robo de backups.
Configuración del server (idéntica a TOTP en el lado SSH, pero cambia PAM):
# Server: instalar libfido2 (si no está)
sudo apt install libfido2-1 libpam-fido2 # Debian/Ubuntu
sudo dnf install libfido2 pam-u2f # RHEL/Fedora (pam-u2f o pam-fido2 según repo)
# /etc/ssh/sshd_config
PubkeyAuthentication yes
AuthenticationMethods publickey
# NO agregar keyboard-interactive:pam si usas SOLO FIDO2
# /etc/pam.d/sshd — agregar al tope
auth required pam_fido2.so
# Test + reload
sudo sshd -t
sudo systemctl restart sshd
Para servidores que quieres que acepten TANTO password+TOTP (usuarios legacy) COMO FIDO2 (tú, el admin): deja TOTP configurado, agrega FIDO2 en AuthenticationMethods con sub-métodos:
# /etc/ssh/sshd_config
AuthenticationMethods publickey,publickey
# Cualquier usuario con DOS métodos publickey (ej: ed25519 + ed25519-sk) entra
# Útil si el admin usa FIDO2 y un script de backup usa ed25519 normal
Enforcement organizacional: hacer 2FA obligatorio con Ansible
Si tienes 50 servers y quieres que el 100% de los usuarios humanos tenga 2FA configurado, Ansible es la herramienta correcta. El flujo: (1) verificar que cada usuario tiene ~/.google_authenticator, (2) instalar el paquete PAM en todas las distros, (3) deployar la config PAM/SSH, (4) reiniciar sshd. Idempotente, auditable, versionado en git.
# ansible-playbook -i inventory/prod ssh-2fa-enforcement.yml
---
- name: Enforce SSH 2FA across fleet
hosts: all
become: yes
tasks:
- name: Install Google Authenticator PAM module
package:
name: "{{ 'libpam-google-authenticator' if ansible_os_family == 'Debian' else 'google-authenticator' }}"
state: present
- name: Deploy PAM config for SSH
copy:
dest: /etc/pam.d/sshd
content: |
# Google Authenticator (managed by Ansible)
auth required pam_google_authenticator.so
@include common-auth
backup: yes
validate: 'sshd -t -f %s' # NO tan parche com — falla el syntax check
notify: restart sshd
- name: Deploy SSH config with 2FA requirement
lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
validate: 'sshd -t -f %s'
loop:
- { regexp: '^ChallengeResponseAuthentication', line: 'ChallengeResponseAuthentication yes' }
- { regexp: '^AuthenticationMethods', line: 'AuthenticationMethods publickey,keyboard-interactive:pam' }
notify: restart sshd
- name: Audit users missing google_authenticator
shell: |
for u in $(getent passwd | awk -F: '$3 >= 1000 && $7 ~ /bash|sh/ {print $1}'); do
if [ ! -f "/home/$u/.google_authenticator" ]; then
echo "MISSING: $u"
fi
done
register: missing_2fa
changed_when: false
- name: Report users without 2FA
debug:
msg: "{{ missing_2fa.stdout_lines }}"
when: missing_2fa.stdout != ""
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
El validate: 'sshd -t -f %s' en el módulo lineinfile corre sshd -t antes de aplicar el cambio. Si la sintaxis rompe, Ansible aborta sin tocar el archivo. Sin ese flag, un typo te deja afuera del server y ni siquiera el cron con flag manual lo salva.
Para FreeIPA / Red Hat Identity Management: IdM tiene 2FA nativo desde 4.7 (2020) vía ipa otptoken-add + ipa user-mod --user-auth-type=otp+password. Si tu org ya tiene IdM, no necesitas Ansible manual: la policy se centraliza y se propaga a todos los clients via SSSD.
Gotchas en clouds: AWS, GCP, Hetzner, Azure
Los servidores en cloud tienen tres peculiarities que no aparecen en un server físico con tradicional setup:
1. Sesión por consola serie (AWS SSM, GCP serial console, Azure Boot Diagnostics, Hetzner vKVM): si te quedás afuera por PAM mal configurado, puedes entrar por la consola serie del cloud. PERO esa consola muchas veces NO tiene challenge-response (no puedes tipear el código TOTP). Solución: tener una segunda key SSH (sin 2FA) autorizada SOLO en la IP de la consola serie, o usar la cuenta de recovery de AWS/GCP/Azure que bypass PAM.
2. Metadata IP en user-data scripts: cuando lanzás una instancia con cloud-init y ponés una SSH key pública en user-data, esa key se autoriza automáticamente. Si tu flujo de deploy automatizado usa esa key, NO le pongas 2FA: dejala como break-glass account con IP allowlist solo desde el CI runner.
3. Floating IP + failover: Hetzner y DigitalOcean permiten mover IP entre servers. Si tu 2FA está pegado al hostname del server, al mover IP la nueva máquina acepta el código (no hay session binding). En cambio, si usas FIDO2 con verify-required, el toque de la YubiKey sigue funcionando pero solo desde el cliente correcto.
Recuperación: no quedar afuera cuando perdés el dispositivo
El escenario de pesadilla es: perdiste el teléfono, no tienes códigos de respaldo escritos, y la única forma de entrar al server es tener el dispositivo con la app. Las tres reglas para evitarlo:
- Tres copias de los códigos de respaldo, en tres lugares distintos: password manager (Bitwarden/1Password), papel en caja fuerte, y (opcional) cofre encriptado en otro server. NO en texto plano en el mismo server que quieres proteger.
- Doble dispositivo con el mismo secret: cuando generas el QR con
google-authenticator, escanealo en dos teléfonos (el personal y uno viejo guardado en un cajón). Si perdés uno, el otro sigue funcionando. - Cuenta de break-glass con key SSH sin 2FA: una clave ed25519 separada, passphrase larga, autorizada SOLO desde tu IP fija de casa/oficina. Encriptada y guardada en papel QR dentro de un sobre. Para usarla, necesitas la passphrase + acceso físico al papel + estar en la IP correcta. Tres factores sin depender de un teléfono.
Si YA te quedaste afuera: el orden de recovery depende del cloud. AWS: parar la instancia, detached EBS, mount en otra instancia, editar /home/usuario/.google_authenticator (borrarlo entero → PAM devuelve success sin TOTP con nullok). GCP: editar authorized_keys via serial console si la config lo permite. Hetzner: rescue mode desde el panel, chroot, edición directa. En server físico: IPMI/iDRAC mount de ISO live, chroot.
Cuándo NO usar 2FA con TOTP en SSH
- Servers de build (CI/CD): si tu GitHub Actions o GitLab CI hace
git pushpor SSH, NO le pongas 2FA. usa una clave deploy-only con passphrase y rotate mensual. - Monitoreo (Prometheus node_exporter, Nagios): esos agentes solo necesitan pull, no shell. usa una key dedicada sin passphrase para el pull.
- Bastión / jump host con muchos usuarios transient: si tienes un bastion donde entran consultores externos de forma ocasional, una sola falla de PAM te bloquea a todos. 2FA sí, pero con session recording (tlog o auditd) para detectar patrones de uso.
- Dispositivos IoT o embedded: un Raspberry Pi en una red aislada sin acceso a internet no necesita 2FA. Clave ed25519 sin passphrase + firewall que solo acepta esa IP.
Resumen: la pila 2FA correcta para sysadmins en 2026
Si quieres la configuración que recomendamos para un sysadmin que administra entre 5 y 500 servers Linux:
- Tu laptop: clave ed25519 con passphrase larga (mínimo 6 palabras random, tipo
correcto-caballo-batería-grapa) + YubiKey con ed25519-sk para acceso root/sudo - Cada server:
PasswordAuthentication no,PermitRootLogin prohibit-password, TOTP obligatorio para usuarios humanos, tu clave ed25519 + la YubiKey enauthorized_keysde root - App autenticadora: Aegis (Android) o Ente Auth (multiplataforma), con export encriptado en tu password manager
- Códigos de respaldo: tres copias físicas (password manager, papel en caja fuerte, sobre cerrado con un familiar)
- Break-glass: una clave SSH dedicada, encriptada y guardada físicamente, autorizada solo desde tu IP fija
Esa pila resiste credential stuffing, robo de laptop, phishing, y el escenario de “perdí el teléfono un martes a las 11pm”. No es perfecto — un atacante con acceso físico a tu server y a tu YubiKey por 30 segundos todavía puede entrar — pero cubre el 99% de los vectores reales que vas a enfrentar en producción.
Preguntas frecuentes
¿Por qué Seguridad SSH en 2026: TOTP, FIDO2 y la pila 2FA que resiste phishing es relevante para sysadmins Linux?
Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.
¿Qué necesito para empezar con Seguridad SSH en 2026: TOTP,?
Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.
¿Cuáles son los errores más comunes con Seguridad SSH en 2026: TOTP,?
Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.
¿Cuándo usar Seguridad SSH en 2026: TOTP, y cuándo una alternativa?
Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.
¿Cómo migro desde una configuración previa a Seguridad SSH en 2026: TOTP,?
Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.
Lee también
- Guía Fedora 44: Novedades, Instalación y Upgrade para Sysadmins Linux
- Configura VLANs en Linux con nmcli: Guía paso a paso para sysadmins
- Protege tu SSH: Guía esencial para configurar Fail2ban en Linux
- Guía Definitiva: Configura Kubernetes con kubeadm en Ubuntu para Sysadmins Linux
- Configura Prometheus y Grafana: Guía DevOps para SysAdmins Linux
