Ubuntu 24.04: endurecimiento SSH que no te dejará fuera — lista pragmática

¿Te fue útil?

A todo el mundo le gusta el “endurecimiento” hasta la primera vez que aplicas un cambio, recargas sshd y te das cuenta de que acabas de bloquear tu única vía de acceso remoto. La máquina sigue en funcionamiento. Lo notas. Pero no puedes entrar. Eso no es seguridad; es tiempo de inactividad autoinfligido.

Esta es una guía de endurecimiento SSH orientada a producción para Ubuntu 24.04 que parte de la premisa de que quieres una autenticación más fuerte, menos superficie de ataque y mejor auditabilidad—sin apostar tu acceso a una sola edición. Tomaremos deliberadamente el camino aburrido: cambios por etapas, comandos de verificación y planes de reversión.

Principios de endurecimiento que previenen bloqueos

El endurecimiento de SSH es un ejercicio de fiabilidad disfrazado de tarea de seguridad. El objetivo no es “paranoia máxima”. El objetivo es “remordimiento mínimo”. Aquí están los principios que te mantienen en el puesto:

  • Nunca cambies SSH en un solo paso. Haz cambios por etapas. Valida. Solo entonces aplica la política.
  • Mantén una segunda vía de entrada. Una segunda sesión SSH, un usuario distinto, un método de autenticación distinto, o acceso por consola (IPMI/KVM/consola serial de la nube). Preferiblemente dos.
  • Prueba la configuración antes de recargar. Usa sshd -t e imprime la configuración efectiva para confirmar lo que ocurrirá.
  • Usa bloques “Match” para acotar el riesgo. Aplica configuraciones estrictas para la mayoría de usuarios, mantén un usuario break-glass limitado pero funcional.
  • Restringe la red primero, luego endurece la autenticación. Reducir quién puede alcanzar SSH es más seguro que adivinar qué clientes podrán negociar tus nuevos ajustes criptográficos.
  • Mide antes de afinar. Si no sabes qué métodos de autenticación se usan hoy, estás a punto de romper a alguien importante a las 2 a. m.

Una cita digna de recordar: “La esperanza no es una estrategia.” — Gene Kranz. Es corta, un poco dura y precisa para operaciones.

Broma #1: Cambiar sshd_config un viernes es una gran manera de aprender cuán buena es realmente tu rotación on-call.

Hechos interesantes y contexto histórico (porque explican los valores por defecto actuales)

  1. SSH reemplazó a Telnet en gran parte porque Telnet enviaba credenciales en texto claro. Eso no era “un poco inseguro”; era “Wireshark es tu gestor de contraseñas”.
  2. OpenSSH surgió como un fork libre de SSH en 1999. Ese fork se convirtió en la herramienta de acceso remoto por defecto en la mayoría de sistemas tipo Unix porque era abierta, auditada y aburrida—en el mejor sentido.
  3. El puerto 22 no es mágico. Es solo el valor por defecto asignado por IANA. Moverlo reduce el ruido de escaneo de baja calidad, no los ataques dirigidos, y puede complicar firewalls y herramientas.
  4. Las versiones del protocolo SSH fueron un problema real. SSH-1 es obsoleto; SSH-2 es el estándar. Las Ubuntu/OpenSSH modernas ya deshabilitan los días malos.
  5. El inicio de sesión como root fue históricamente conveniente para admins. También arruinaba la auditabilidad y convertía cada credencial filtrada en una compromisión total. Por eso “no root sobre SSH” se volvió un consejo por defecto.
  6. Los algoritmos de clave débiles fueron desaprobados por buenas razones. DSA es prácticamente una pieza de museo. RSA sigue en uso pero cada vez más limitado por políticas; Ed25519 se hizo popular porque es rápido, fuerte y difícil de usar mal.
  7. Los ajustes criptográficos estrictos pueden romper automatizaciones antiguas. Algunos clientes embebidos y bibliotecas legado no soportan combinaciones modernas de KEX/MAC. No es tu culpa, pero sí será una caída si aplicas el cambio sin inventario.
  8. Los ataques de fuerza bruta no empeoraron; se abarataron. El escaneo a escala nube convirtió “contraseña habilitada en un SSH expuesto a Internet” en una apuesta perdedora con el tiempo.

Línea base: conoce qué ejecutas y quién puede alcanzarlo

El endurecimiento empieza con una línea base. Necesitas saber:

  • Qué versión del servidor OpenSSH estás ejecutando (conjunto de características, valores por defecto, directivas disponibles).
  • En qué puerto(es) estás escuchando y en qué interfaces.
  • Qué métodos de autenticación están en uso ahora mismo.
  • Qué redes pueden alcanzar SSH.
  • Cómo recuperar el acceso si la estropeas.

En Ubuntu 24.04, openssh-server suele gestionarse por systemd (ssh.service) y configurarse con /etc/ssh/sshd_config más fragmentos bajo /etc/ssh/sshd_config.d/ (si decides usarlos). El enfoque de “drop-in” es más limpio para control de cambios: puedes mantener una base mínima amigable con el proveedor y gestionar tu propio fragmento.

Tareas prácticas (comandos + significado de la salida + decisiones)

Estas son tareas reales que puedes ejecutar en Ubuntu 24.04. Cada una incluye: el comando, lo que deberías ver y la decisión que tomas a partir de ello. Este es el corazón de “endurecer sin bloqueo”: nunca cambias lo que no has observado.

Task 1: Confirmar que OpenSSH server está instalado y su versión

cr0x@server:~$ dpkg -l | grep -E '^ii\s+openssh-server'
ii  openssh-server  1:9.6p1-3ubuntu13  amd64  secure shell (SSH) server, for secure access from remote machines

Qué significa: Estás ejecutando OpenSSH 9.6p1 empaquetado para Ubuntu. La versión importa para directivas soportadas y valores por defecto criptográficos.

Decisión: Si openssh-server no está instalado o es sorprendentemente antiguo, detente y arregla eso antes de “endurecer”. Endurecer un demonio obsoleto es pulir una cerradura oxidada.

Task 2: Comprobar si SSH está en ejecución y cómo se inició

cr0x@server:~$ systemctl status ssh --no-pager
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Mon 2025-12-30 09:14:22 UTC; 2h 18min ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 1247 (sshd)
      Tasks: 1 (limit: 18962)
     Memory: 6.3M
        CPU: 1.221s
     CGroup: /system.slice/ssh.service
             └─1247 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

Qué significa: El servicio está activo y gestionado por systemd. El PID y la invocación son visibles.

Decisión: Si no está en ejecución, no “endurezcas”—restaura la salud del servicio primero. Si estás en un host sin consola, quieres SSH estable antes de editar.

Task 3: Identificar puerto(s) e interfaces en escucha

cr0x@server:~$ ss -tulpn | grep -E 'sshd|:22'
tcp   LISTEN 0      128          0.0.0.0:22        0.0.0.0:*    users:(("sshd",pid=1247,fd=3))
tcp   LISTEN 0      128             [::]:22           [::]:*    users:(("sshd",pid=1247,fd=4))

Qué significa: SSH está escuchando en todas las interfaces IPv4 e IPv6 en el puerto 22.

Decisión: Si este es un host expuesto a Internet, probablemente estés sobreexpuesto. Planea restricciones de red (firewall + listas de permitidos) y posiblemente enlazar a una interfaz de gestión.

Task 4: Ver la configuración efectiva de sshd (no solo el archivo)

cr0x@server:~$ sudo sshd -T | egrep -i 'port |listenaddress|passwordauthentication|pubkeyauthentication|permitrootlogin|authenticationmethods'
port 22
listenaddress 0.0.0.0
listenaddress ::
permitrootlogin prohibit-password
passwordauthentication yes
pubkeyauthentication yes
authenticationmethods any

Qué significa: Esta es la configuración que sshd usará realmente después de leer todos los includes y valores por defecto.

Decisión: Endureces a partir de la configuración efectiva, no de tus suposiciones. Si PasswordAuthentication yes, necesitas un plan para deshabilitarla de forma segura (tras confirmar claves y automatización).

Task 5: Validar la sintaxis de la configuración antes de tocar nada

cr0x@server:~$ sudo sshd -t

Qué significa: Ninguna salida es buena. La salida indica errores de sintaxis y sshd se negaría a iniciar/recargar.

Decisión: Si alguna vez ves errores aquí, no recargues sshd. Arregla la sintaxis primero. Los fallos de sintaxis son los generadores de bloqueo más puros.

Task 6: Confirmar que tienes un inicio de sesión con clave funcional (desde otra terminal)

cr0x@server:~$ ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no -v ops@127.0.0.1 'echo OK'
OpenSSH_9.6p1 Ubuntu-3ubuntu13, OpenSSL 3.0.13 30 Jan 2024
debug1: Authenticating to 127.0.0.1:22 as 'ops'
debug1: Authentication succeeded (publickey).
OK

Qué significa: Puedes autenticar con una clave y estás rechazando explícitamente la alternativa de contraseña.

Decisión: Si falla, no deshabilites todavía la autenticación por contraseña. Arregla claves, agente o authorized_keys primero.

Task 7: Inspeccionar permisos de authorized_keys (asesinos silenciosos)

cr0x@server:~$ sudo ls -ld /home/ops /home/ops/.ssh /home/ops/.ssh/authorized_keys
drwxr-x---  6 ops ops 4096 Dec 30 09:01 /home/ops
drwx------  2 ops ops 4096 Dec 30 09:03 /home/ops/.ssh
-rw-------  1 ops ops  742 Dec 30 09:03 /home/ops/.ssh/authorized_keys

Qué significa: Los permisos parecen sensatos: home no es escribible por el mundo, .ssh es 700, authorized_keys no es escribible por otros.

Decisión: Si ves bits de escritura para grupo/mundo o propiedad incorrecta, arréglalos. sshd ignorará claves que considere inseguras, y no avisará con fanfarrias.

Task 8: Confirmar qué usuarios están permitidos y qué grupos importan

cr0x@server:~$ getent group sudo
sudo:x:27:ops,deploy

Qué significa: ops y deploy están en sudo; probablemente sean usuarios administrativos interactivos.

Decisión: Si planeas usar AllowGroups, asegúrate de saber qué grupos permitir. Mal dimensionarlo es un clásico “¿por qué no puedo iniciar sesión?”.

Task 9: Comprobar estado del firewall y permisos SSH existentes

cr0x@server:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    10.20.0.0/16
22/tcp                     ALLOW IN    192.168.50.0/24

Qué significa: UFW está activo, por defecto denegar entradas, y SSH está permitido solo desde dos redes privadas.

Decisión: Esto es bueno. Si ves 22/tcp ALLOW IN Anywhere en un host en Internet, arregla el firewall antes de tocar la criptografía.

Task 10: Observar eventos de autenticación y ruido de fuerza bruta

cr0x@server:~$ sudo journalctl -u ssh --since "2 hours ago" | tail -n 12
Dec 30 10:44:01 server sshd[8621]: Failed password for invalid user admin from 203.0.113.77 port 51244 ssh2
Dec 30 10:44:06 server sshd[8621]: Failed password for invalid user admin from 203.0.113.77 port 51244 ssh2
Dec 30 10:44:10 server sshd[8621]: Connection closed by invalid user admin 203.0.113.77 port 51244 [preauth]
Dec 30 11:02:33 server sshd[9012]: Accepted publickey for ops from 10.20.8.41 port 49912 ssh2: ED25519 SHA256:2d3v...

Qué significa: Tienes intentos de contraseña por fuerza bruta (probablemente exposición a Internet o a través de VPN) y logins legítimos por clave.

Decisión: Si ves intentos repetidos de contraseña, deshabilitar la autenticación por contraseña se vuelve más prioritario. Si solo ves logins con clave, puedes imponer autenticación más estricta con menos riesgo.

Task 11: Comprobar compatibilidad del cliente SSH (desde tu estación de trabajo)

cr0x@server:~$ ssh -G ops@server | egrep -i 'kexalgorithms|ciphers|macs|hostkeyalgorithms' | head
ciphers chacha20-poly1305@openssh.com,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr
macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
kexalgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
hostkeyalgorithms ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256

Qué significa: Tu cliente soporta algoritmos modernos, incluyendo opciones híbridas post-cuánticas de KEX (enfoque de OpenSSH) y Ed25519.

Decisión: Antes de endurecer algoritmos en el servidor, inventaría el soporte del cliente. Que tu estación sea moderna no implica que tu automatización lo sea.

Task 12: Crear un canario seguro “sshd en otro puerto” (temporal)

cr0x@server:~$ sudo /usr/sbin/sshd -D -p 2222 -o PermitRootLogin=no -o PasswordAuthentication=no -o PubkeyAuthentication=yes

Qué significa: Esto inicia un sshd en primer plano en el puerto 2222 con ajustes estrictos de autenticación, sin tocar el demonio principal.

Decisión: Úsalo para probar nuevas políticas mientras mantienes el puerto 22 como red de seguridad. Una vez validado, traslada los ajustes a la configuración y recarga el servicio real.

Task 13: Probar recarga de forma segura (tras cambios) y confirmar que se aplicó

cr0x@server:~$ sudo sshd -t && sudo systemctl reload ssh && sudo sshd -T | egrep -i 'passwordauthentication|permitrootlogin'
passwordauthentication no
permitrootlogin no

Qué significa: La sintaxis es válida, la recarga tuvo éxito y la configuración efectiva ahora muestra autenticación más estricta.

Decisión: Si la recarga falla, no te desesperes—tus sesiones existentes suelen permanecer. Arregla y recarga de nuevo. Si hiciste un restart y no volvió, estás en modo “consola”. Evita reinicios salvo que sean necesarios.

Task 14: Confirmar que no te bloqueaste accidentalmente a nivel de red

cr0x@server:~$ sudo ufw status numbered
Status: active

     To                         Action      From
[ 1] 22/tcp                     ALLOW IN    10.20.0.0/16
[ 2] 22/tcp                     ALLOW IN    192.168.50.0/24

Qué significa: Puedes ver claramente qué reglas aplican y en qué orden.

Decisión: Si vas a endurecer reglas, añade nuevas reglas allow primero, prueba conectividad y luego elimina las antiguas amplias. Los ediciones de firewall por sustracción son cómo descubres que tu VPN no viene desde la subred que asumiste.

sshd_config: ajustes que vale la pena cambiar (y los que causan problemas)

Ubuntu 24.04 + OpenSSH ya son bastante decentes. El truco es elegir cambios que reduzcan el riesgo de forma material sin detonar el acceso. Aquí lo que recomiendo y por qué, además de los modos de fallo.

Usa un archivo drop-in, no un monolito desordenado

Crea un fragmento gestionado como /etc/ssh/sshd_config.d/50-hardening.conf. Funciona bien con paquetes, hace los diffs legibles y mantiene tus cambios explícitos.

cr0x@server:~$ sudo install -m 0644 -o root -g root /dev/null /etc/ssh/sshd_config.d/50-hardening.conf

Ajustes de alto valor (seguros cuando se hacen por etapas)

  • Deshabilitar autenticación por contraseña después de verificar claves: PasswordAuthentication no.
  • Deshabilitar login root para SSH interactivo: PermitRootLogin no. Si necesitas acceso privilegiado, usa sudo con usuarios auditados.
  • Limitar quién puede entrar: AllowUsers o AllowGroups. Esto reduce el radio de daño si una credencial se filtra.
  • Reducir la superficie de ataque: X11Forwarding no a menos que realmente lo necesites; muchos no lo requieren. Considera también restricciones de AllowTcpForwarding por usuario.
  • Establecer timeouts sensatos: ClientAliveInterval y ClientAliveCountMax para limpiar sesiones muertas.
  • Loguear con intención: LogLevel VERBOSE puede ayudar en auditoría (pero genera ruido; no lo hagas ciegamente en bastiones de alto volumen).

Ajustes que son “seguros” pero que frecuentemente causan bloqueos

  • Endurecer algoritmos sin inventario de clientes. Si quitas soporte RSA SHA-2 descuidadamente o deshabilitas un KEX que clientes antiguos requieren, obtendrás fallos de handshake.
  • Configurar mal AuthenticationMethods. Es potente y afilado. Un typo puede requerir “dos factores” que nunca configuraste.
  • Usar AllowUsers y olvidar cuentas de servicio (automatización, backups, gestión de configuración). Tu sistema no se caerá; tu pipeline sí.

Un drop-in base sólido (adáptalo)

Esto es lo bastante conservador para la mayoría de flotas y lo bastante estricto para importar. Aplícalo solo después de confirmar que la autenticación por clave funciona para todas las cuentas requeridas.

cr0x@server:~$ sudo tee /etc/ssh/sshd_config.d/50-hardening.conf >/dev/null <<'EOF'
# Managed hardening settings (Ubuntu 24.04)

Protocol 2

# Safer defaults: no root, no passwords
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes

# Reduce opportunistic abuse
MaxAuthTries 4
LoginGraceTime 20
MaxStartups 10:30:60

# Hygiene
X11Forwarding no
PermitTunnel no
PermitUserEnvironment no
AllowAgentForwarding no

# Keepalive to clear dead sessions
ClientAliveInterval 300
ClientAliveCountMax 2

# Prefer explicit allowlists (set these to match your org)
# AllowGroups ssh-users

LogLevel VERBOSE
EOF

Cómo aplicar sin drama: Mantén una sesión activa abierta, valida la configuración (sshd -t), recarga (no reinicies) y prueba nuevos inicios de sesión en una segunda terminal.

Estrategia de autenticación: claves, contraseñas, MFA y break-glass

La autenticación SSH no es un interruptor único. Es una estrategia. En producción quieres:

  • Autenticación principal fuerte y automatizada (claves públicas, idealmente respaldadas por un proceso sensato de aprovisionamiento).
  • Defensa contra reutilización de credenciales y fuerza bruta (sin contraseñas, o al menos sin contraseñas desde Internet público).
  • Acceso break-glass controlado, auditado y probado (porque lo necesitarás en el peor momento).

Claves: hazlas bien

Las claves Ed25519 son la opción moderna por defecto para usuarios interactivos. Son pequeñas, rápidas y resistentes a muchos errores históricos. Para automatización puede que aún tengas claves RSA en uso; está bien si usan firmas SHA-2 y no tamaños antiguos.

cr0x@server:~$ ssh-keygen -t ed25519 -a 64 -C "ops@laptop"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/cr0x/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/cr0x/.ssh/id_ed25519
Your public key has been saved in /home/cr0x/.ssh/id_ed25519.pub

Qué significa: -a 64 incrementa las rondas KDF para proteger la frase de contraseña. Eso importa si la clave privada se filtra.

Decisión: Para humanos: usa frase de contraseña. Para automatización: considera enfoques basados en agente o claves con restricciones y comandos forzados.

Desplegar claves con un método controlado

Si usas ssh-copy-id, hazlo mientras la autenticación por contraseña sigue habilitada (temporalmente), luego desactiva contraseñas una vez verificado.

cr0x@server:~$ ssh-copy-id -i ~/.ssh/id_ed25519.pub ops@server
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/cr0x/.ssh/id_ed25519.pub"
Number of key(s) added: 1
Now try logging into the machine, with:   "ssh 'ops@server'"

Decisión: Prueba inmediatamente con PasswordAuthentication=no como se mostró antes. No confíes en “added: 1” como prueba de acceso.

MFA: útil, pero no improvises en producción

MFA para SSH puede implementarse vía PAM (por ejemplo, TOTP) o mediante claves con soporte hardware (FIDO2/U2F con OpenSSH). La trampa de fiabilidad: los cambios en PAM son la plomería global de login. Un pequeño error puede romper no solo SSH sino también inicios de sesión locales dependiendo de cómo lo configures.

Enfoque pragmático:

  • Comienza con SSH solo por claves + listas de permitidos de red + monitorización. Eso ya es una gran mejora.
  • Añade MFA en bastiones primero, no en cada nodo, a menos que tengas un despliegue y plan de recuperación maduros.
  • Si usas claves FIDO, asegura que cada admin tenga dos dispositivos y un proceso para pérdidas.

Break-glass: la cuenta aburrida que te salva

Crea un usuario dedicado breakglass con:

  • Autenticación solo por clave
  • Direcciones de origen restringidas (VPN o rangos IP de oficina)
  • Permisos sudo regulados por tu política (a menudo sudo total, pero controlado y auditado)
  • Sin uso cotidiano
cr0x@server:~$ sudo adduser --disabled-password --gecos "" breakglass
Adding user `breakglass' ...
Adding new group `breakglass' (1002) ...
Adding new user `breakglass' (1002) with group `breakglass (1002)' ...
Creating home directory `/home/breakglass' ...
Copying files from `/etc/skel' ...
Adding new user `breakglass' to supplemental / extra groups `users' ...
Adding user `breakglass' to group `users' ...

Decisión: Mantén esta cuenta fuera de flujos normales. Si aparece en los logs de login regulares, trátala como una alarma de humo.

Controles de red: UFW, listas de permitidos y “no expongas el puerto 22 al planeta”

Las restricciones de red son el cambio de endurecimiento menos glamuroso y el más efectivo. Si el atacante no puede alcanzar el puerto, tu política de autenticación pasa a ser una segunda línea de defensa en lugar de la primera.

Enlaza SSH a una IP de gestión (cuando la tengas)

Si el host tiene una interfaz/VLAN de gestión dedicada, enlaza SSH ahí. Esto reduce la exposición incluso si las reglas del firewall se desvían.

cr0x@server:~$ sudo tee /etc/ssh/sshd_config.d/10-listen.conf >/dev/null <<'EOF'
ListenAddress 10.20.8.10
EOF

Decisión: Haz esto solo si estás seguro de que la IP es estable y alcanzable desde tus redes administrativas. De lo contrario te inventarás nuevas palabrotas.

Patrón allowlist UFW (añadir antes de eliminar)

Si SSH está actualmente abierto, no lo cierres de golpe. Añade una regla “allow” estrecha primero, prueba y luego elimina el acceso amplio.

cr0x@server:~$ sudo ufw allow from 10.20.0.0/16 to any port 22 proto tcp
Rule added

Qué significa: La red de admins está permitida.

Decisión: Confirma que puedes conectar desde esa red. Solo entonces elimina reglas de Anywhere.

Considera un bastión en lugar de acceso directo a nodos

En muchos entornos corporativos, el mejor endurecimiento SSH es arquitectónico: haz que la mayoría de servidores no sean ruteables desde redes de usuarios y exige un bastión con monitoreo más estricto. SSH pasa a ser un punto de control en lugar de mil puntos de entrada dispersos.

Broma #2: Un host bastión es como la recepcionista de la oficina: todos se quejan hasta el día que lo quitas.

Resistencia al abuso: límites, Fail2ban y respuestas basadas en logs

Si tu SSH es alcanzable, será escaneado. Incluso con autenticación por clave, los atacantes probarán nombres de usuario y casos borde. Tu trabajo es hacer ese ruido barato de ignorar y caro de sostener.

Usa journal de systemd + limitación de tasa como primera línea

OpenSSH tiene perillas integradas (MaxStartups, LoginGraceTime, MaxAuthTries). Reducen abuso de recursos y ralentizan intentos de fuerza bruta.

No los pongas en valores absurdamente bajos en bastiones; castigarás a tus propios usuarios durante respuesta a incidentes cuando muchas personas conecten a la vez.

Fail2ban: todavía útil, pero no sustituto de listas de permitidos

Fail2ban funciona parseando logs y añadiendo reglas de firewall dinámicamente. Es útil para endpoints expuestos a Internet, pero es reactivo y se puede explotar. Trátalo como una fregona, no como una presa.

cr0x@server:~$ sudo apt-get update -y && sudo apt-get install -y fail2ban
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed:
  fail2ban
cr0x@server:~$ sudo systemctl enable --now fail2ban
Created symlink /etc/systemd/system/multi-user.target.wants/fail2ban.service → /usr/lib/systemd/system/fail2ban.service.
cr0x@server:~$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed: 24
|  `- File list: /var/log/auth.log
`- Actions
   |- Currently banned: 1
   |- Total banned: 3
   `- Banned IP list: 203.0.113.77

Qué significa: La cárcel está funcionando, rastreando fallos y baneando IPs abusivas.

Decisión: Si ves IPs corporativas internas baneadas, tu allowlist/VPN/NAT puede estar causando egress compartido. Ajusta la cárcel o, mejor, arregla el trayecto de red.

Política criptográfica: cifrados, MACs, KEX y claves de host (sin romper clientes)

El endurecimiento criptográfico es donde la buena intención causa outages reales. El enfoque correcto es incremental: inventaría capacidades del cliente, elige una política, despliega por etapas y observa fallos de handshake.

Primera regla: no te creas más listo que tu flota

OpenSSH de Ubuntu 24.04 ya es moderno. A menos que tengas un requisito de cumplimiento, muchas veces no necesitas establecer explícitamente Ciphers/MACs/KexAlgorithms. Los valores por defecto evolucionan con parches; fijarlos puede congelarte en el tiempo.

Cuando realmente necesites fijarlos (cumplimiento, auditores, consistencia entre distribuciones), hazlo con un paso de inventario y un plan de reversión.

Inventario de fallos: vigila mensajes “no matching…”

cr0x@server:~$ ssh -vvv legacy@server
debug1: kex: algorithm: (no match)
Unable to negotiate with 10.20.8.10 port 22: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1

Qué significa: El cliente es antiguo y solo ofrece KEX débiles. Tu servidor endurecido lo rechaza. Probablemente eso esté correcto.

Decisión: Mejora el cliente o aíslalo detrás de un bastión controlado que pueda comunicarse con él. No debilites la flota por un fósil.

Claves de host: mantenlas sensatas y estables

Las claves de host son la identidad del servidor. Rotarlas sin plan provoca advertencias “REMOTE HOST IDENTIFICATION HAS CHANGED!”, que los usuarios terminarán por ignorar. Eso es lo opuesto a lo que quieres.

cr0x@server:~$ sudo ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
256 SHA256:8t7mZlQZ9l8m0n3H9Jx7qWzYkqvHqQx0mJv4pYkq8mE root@server (ED25519)

Decisión: Asegura que tienes claves de host modernas (Ed25519, ECDSA, RSA SHA-2). Si planeas rotarlas, coordina actualizaciones de known_hosts vía gestión de configuración, no por email.

Guardarraíles operativos: control de cambios, pruebas y reversión

“Endurecer” no es una acción heroica. Es un despliegue controlado.

Mantén una sesión activa y abre una segunda antes de recargar

Cuando recargas sshd, las sesiones existentes típicamente permanecen. Esa es tu cuerda de salvación. Abre una segunda sesión antes de los cambios para poder probar nuevos inicios de sesión manteniendo un canal conocido bueno.

Prefiere recarga sobre reinicio

systemctl reload ssh le pide a sshd que relea la configuración sin cerrar conexiones existentes. Reiniciar es más disruptivo y más probable que te deje varado si ocurre un fallo de arranque.

Conoce tu acceso fuera de banda antes de necesitarlo

Consola de la nube, IPMI, consola del hipervisor o acceso físico. Pruébalo trimestralmente. Sí, trimestralmente. La primera vez que lo uses no debe ser durante una ventana de outage mientras Slack se llena de opiniones.

Haz copia de seguridad de archivos de configuración con sello de tiempo

cr0x@server:~$ sudo cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F_%H%M%S)

Decisión: Si algo sale mal, quieres un archivo conocido bueno, no un recuerdo de lo que cambiaste.

Guion de diagnóstico rápido

Cuando SSH “se rompe”, normalmente es una de cuatro categorías: demonio caído, red bloqueada, autenticación rechazada o falla de negociación criptográfica. Diagnostica en ese orden. Rápido. Sin divagaciones.

1) ¿sshd está activo y escuchando?

  • En la consola del servidor (o vía sesión existente): comprueba el estado del servicio y los sockets en escucha.
cr0x@server:~$ systemctl is-active ssh; ss -tulpn | grep sshd
active
tcp   LISTEN 0      128          10.20.8.10:22        0.0.0.0:*    users:(("sshd",pid=1247,fd=3))

Si no está activo/escuchando: sintaxis de configuración, permisos o problemas de paquete. Ve a sshd -t y a los logs del journal.

2) ¿Está abierta la ruta de red?

  • Desde el cliente: ¿obtienes una conexión TCP o un timeout?
  • Desde el servidor: ¿las reglas de firewall permiten tu IP de origen?
cr0x@server:~$ sudo ufw status verbose
Status: active
Default: deny (incoming), allow (outgoing), disabled (routed)

Los timeouts suelen significar red/firewall. “Connection refused” inmediato suele significar que sshd no está escuchando (o puerto incorrecto).

3) ¿Es política de autenticación?

  • Desde el cliente: usa SSH con verbose.
  • Desde el servidor: revisa logs de ssh para “Authentication refused” o “user not allowed.”
cr0x@server:~$ ssh -vvv ops@server
debug1: Authentications that can continue: publickey
debug1: Offering public key: /home/cr0x/.ssh/id_ed25519 ED25519 SHA256:2d3v...
debug1: Server accepts key: /home/cr0x/.ssh/id_ed25519
debug1: Authentication succeeded (publickey).

Si ves “Permission denied (publickey)”: comprueba despliegue de claves, permisos de archivos, AllowUsers/AllowGroups y cualquier bloque Match.

4) ¿Es negociación criptográfica?

Busca mensajes “no matching”: KEX, algoritmo de clave de host, cipher o MAC. Esto suele ser un problema de antigüedad del cliente o una política de servidor excesivamente fijada.

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: Sesiones existentes permanecen, pero nuevos logins fallan inmediatamente

Causa raíz: Recargaste sshd con una política más estricta (PasswordAuthentication no, AllowGroups o AuthenticationMethods) y tu usuario de prueba no cumple.

Solución: En tu sesión existente, ejecuta sshd -T, confirma ajustes efectivos y ajusta listas de permitidos o restaura autenticación por contraseña temporalmente mientras arreglas acceso por clave.

2) Síntoma: “Connection refused”

Causa raíz: sshd no está escuchando en esa interfaz/puerto (mal ListenAddress, puerto erróneo, servicio caído).

Solución: ss -tulpn para confirmar listeners; journalctl -u ssh para errores de arranque; corrige la configuración y recarga o reinicia si es necesario.

3) Síntoma: “Connection timed out”

Causa raíz: Firewall/security group/ACL de red bloqueando, o problema de enrutamiento/VPN.

Solución: Verifica reglas UFW y reglas upstream. Añade un allow temporal estrecho desde tu IP actual, prueba y luego limpia.

4) Síntoma: “Permission denied (publickey)” pero tu clave está presente

Causa raíz: sshd está ignorando authorized_keys por permisos inseguros o propiedad incorrecta; o el usuario no está permitido por AllowUsers/AllowGroups.

Solución: Arregla permisos a 700 en .ssh y 600 en authorized_keys. Verifica propiedad. Confirma reglas allow.

5) Síntoma: “REMOTE HOST IDENTIFICATION HAS CHANGED!” tras mantenimiento

Causa raíz: Rotación inesperada de claves de host (rebuild, restore o cambio de imagen) sin propagar actualizaciones de known_hosts.

Solución: Valida la nueva huella de clave de host por canal fuera de banda y luego actualiza known_hosts de forma centralizada. No acostumbres a los usuarios a ignorar advertencias.

6) Síntoma: Algo de automatización falla, los humanos están bien

Causa raíz: Deshabilitaste autenticación por contraseña y la automatización aún la usaba; o el cliente de automatización no puede negociar los nuevos algoritmos.

Solución: Identifica las cuentas que fallan en los logs, migra la automatización a claves o certificados SSH y prueba con la versión exacta del cliente usado en CI/CD.

Listas de verificación / plan paso a paso

Fase 0: Prevuelo (no la saltes)

  1. Confirma que el acceso por consola/fora de banda existe y funciona (serial de la nube, IPMI, consola del hipervisor).
  2. Abre dos sesiones SSH desde terminales diferentes.
  3. Haz copia de seguridad de la configuración SSH y anota ajustes efectivos actuales:
    • sudo cp -a /etc/ssh/sshd_config ...
    • sudo sshd -T > /root/sshd-effective.before
  4. Inventaría quién inicia sesión y cómo (entradas del journal, logs de bastión, cuentas de automatización).

Fase 1: Endurecimiento de red primero

  1. Implementa listas de permitidos en UFW (o grupos de seguridad en la nube) para limitar las fuentes SSH a redes admin/VPN.
  2. Prueba conectividad desde cada red permitida.
  3. Sólo entonces elimina reglas amplias.

Fase 2: Autenticación por clave en todas partes

  1. Asegura que cada cuenta humana y de automatización requerida tenga un inicio de sesión por clave funcional.
  2. Verifica con PasswordAuthentication=no desde el lado del cliente.
  3. Arregla permisos y propiedad proactivamente.

Fase 3: Aplicar no-contraseña, no-root

  1. Añade un drop-in gestionado con PasswordAuthentication no y PermitRootLogin no.
  2. Ejecuta sudo sshd -t.
  3. Recarga sshd, no reinicies.
  4. Prueba un login nuevo como usuario admin normal y como usuario break-glass.

Fase 4: Reducir características de SSH que no necesitas

  1. Deshabilita X11 forwarding a menos que sea requerido.
  2. Deshabilita agent forwarding por defecto. Si tu flujo lo necesita, habilítalo por usuario usando bloques Match.
  3. Restringe port forwarding si SSH se usa solo para acceso shell.

Fase 5: Resistencia al abuso y monitorización

  1. Establece MaxAuthTries, LoginGraceTime, MaxStartups de forma sensata.
  2. Despliega Fail2ban donde corresponda (endpoints públicos), pero no dependas de él como control primario.
  3. Revisa logs semanalmente hasta estabilizar, luego mensualmente.

Tres microhistorias corporativas desde el país de los cambios de SSH que se lamentan

Microhistoria 1: El incidente causado por una suposición equivocada

La empresa tenía un estate mixto: algunos Ubuntu, algunos RHEL y un puñado de appliances que nadie quería admitir que seguían en servicio. Un ingeniero decidió estandarizar políticas SSH durante un “sprint de seguridad”. Meta razonable. Añadieron AllowGroups ssh-admins en toda la flota, confiados en que “todos los admins están en ese grupo”.

Tenían razón en el papel. En la práctica, había tres fuentes de identidad diferentes: usuarios locales en hosts antiguos, LDAP en los más nuevos y una integración IAM en bastiones. En varios servidores el grupo existía—pero las cuentas admin no eran miembros porque eran usuarios locales creados durante incidentes pasados. Nadie los había limpiado. La suposición era que la identidad estaba centralizada en todas partes. No lo estaba.

El cambio se desplegó. Las sesiones SSH existentes permanecieron conectadas, lo que dio a todos una falsa confianza. Nuevas sesiones fallaron. El equipo on-call empezó a rotar terminales como controladores de tráfico aéreo porque no querían perder sus últimas sesiones. Una rutina de parcheo de kernel estaba a medias y no pudieron reestablecer conexiones para terminar.

La solución fue aburrida y algo humillante: revertir el cambio de AllowGroups, auditar membresías de grupo correctamente y luego reintroducir listas de permitidos usando un bloque Match Address por etapas para la ruta break-glass. La lección permanente no fue “nunca uses AllowGroups”. Fue “nunca asumas que la identidad es uniforme a menos que lo hayas probado”.

Microhistoria 2: La optimización que salió mal

Otra organización ejecutaba un bastión muy concurrido. Tenían miles de conexiones SSH de corta duración: humanos, automatización y scripts que hacían “un comando y salen”. Alguien notó picos de CPU bajo carga y decidió “optimizar” reduciendo timeouts y conexiones no autenticadas concurrentes.

Pusieron LoginGraceTime muy bajo y MaxStartups demasiado estricto, pensando que solo perjudicarían bots. Durante un incidente rutinario, un fallo de VPN hizo que muchos clientes intentaran reconectar simultáneamente. El bastión empezó a rechazar nuevas conexiones justo cuando más se le necesitaba. Al equipo de seguridad le encantaron los nuevos ajustes; al responsable del incidente no.

Peor aún, el modo de fallo parecía inestabilidad de red. Los usuarios vieron desconexiones intermitentes y timeouts. La gente culpó a la VPN, luego al DNS, luego a la red cloud. La causa raíz fue control de admisión autoimpuesto por sshd bajo condiciones de reconexión masiva.

Revirtieron a una curva de MaxStartups más permisiva y aumentaron un poco el tiempo de gracia. Los picos de CPU siguieron siendo manejables y el bastión volvió a ser predecible. La moraleja: optimizar SSH como si fuera un benchmark te hace descubrir que tu carga incluye humanos comportándose como una manada.

Microhistoria 3: La práctica aburrida pero correcta que salvó el día

Una compañía financiera tenía una regla estricta: cada cambio de política SSH requería (1) un host canario, (2) una prueba de acceso fuera de banda y (3) una verificación scriptada de login solo-por-clave antes de deshabilitar contraseñas. Sonaba burocrático. Funcionó.

Planeaban deshabilitar la autenticación por contraseña en toda la flota. Antes de imponerla, su pipeline ejecutó una prueba que intentó login con PasswordAuthentication=no para cada cuenta privilegiada que importaba. Un canario falló: un job de automatización legacy todavía usaba contraseña para alcanzar una caja de reportes. Nadie lo sabía porque había sido “configurado y olvidado” años atrás.

Como la prueba se ejecutó antes de la imposición, nada se rompió. Arreglaron el job para usar una clave dedicada con permisos de comando restringidos, volvieron a ejecutar la prueba y solo entonces empujaron la configuración en todas partes.

No hubo drama, ni acceso de consola de emergencia ni reversión nocturna del cambio. La mejora de seguridad aterrizó silenciosamente. Así es como “lo correcto” se ve en producción: no heroico, simplemente repetible.

Preguntas frecuentes

1) ¿Debería cambiar el puerto SSH del 22?

Si estás expuesto a Internet, cambiar el puerto reduce el ruido de escaneo de fondo pero no detiene a atacantes dirigidos. Hazlo solo si no complica tus herramientas. Prefiere listas de permitidos de red y autenticación solo por claves.

2) ¿Deshabilitar la autenticación por contraseña es siempre seguro?

Es seguro cuando has verificado el acceso por clave para cada usuario y ruta de automatización requerida, y tienes un plan break-glass. Es arriesgado si “crees” que las claves están desplegadas pero no las has probado sin fallback de contraseña.

3) ¿Cuál es la forma más segura de aplicar cambios: recargar o reiniciar?

Recarga primero. systemctl reload ssh mantiene sesiones existentes. Reiniciar está bien cuando es necesario, pero aumenta la probabilidad de bloqueo completo si la configuración está rota.

4) ¿Debería fijar explícitamente Ciphers/MACs/KexAlgorithms?

Sólo si debes por política/cumplimiento o por consistencia entre distribuciones. Los valores por defecto suelen ser buenos y mejoran con actualizaciones. Fijarlos puede crear un outage futuro cuando los clientes difieran.

5) ¿Cómo prevengo el acceso root sin perder capacidad administrativa?

Deshabilita login root por SSH (PermitRootLogin no) y usa cuentas con nombre y sudo. Obtienes trazas de auditoría y puedes revocar acceso limpiamente.

6) ¿Qué pasa con SSH agent forwarding?

Desactívalo por defecto (AllowAgentForwarding no). Si ciertos flujos lo requieren, habilítalo en un bloque Match User y mantenlo desactivado en bastiones a menos que confíes realmente en el salto.

7) ¿Necesito Fail2ban si ya tengo listas de permitidos UFW?

Si SSH está estrictamente en listas de permitidos a redes privadas, el valor de Fail2ban disminuye. Si SSH es alcanzable desde Internet (incluso temporalmente), Fail2ban es útil como capa adicional, no como defensa primaria.

8) ¿Cuál es el único mejor truco “que no te bloqueará”?

Ejecuta un sshd canario temporal en otro puerto con los ajustes estrictos previstos, pruébalo end-to-end y luego aplica ajustes al servicio real.

9) ¿Cómo sé si rompí compatibilidad criptográfica?

Cliente-side ssh -vvv mostrará fallos de negociación como “no matching key exchange method”. Los logs del servidor también pueden mostrar algoritmos rechazados. La solución suele ser actualizar el cliente, no debilitar el servidor.

10) ¿Puedo aplicar diferentes políticas SSH según usuarios?

Sí. Usa bloques Match User, Match Group o Match Address para acotar ajustes más estrictos o permisivos. Así mantienes acceso break-glass controlado mientras aplicas defaults fuertes para el resto.

Conclusión: qué hacer a continuación

El endurecimiento SSH en Ubuntu 24.04 no tiene que ser teatral. Hazlo como operador: reduce exposición primero, prueba acceso por clave, luego aplica autenticación más estricta. Evita fijar algoritmos salvo que tengas motivo. Prefiere recargas sobre reinicios. Mantén una ruta break-glass que realmente pruebes, no solo de la que hables.

Pasos prácticos siguientes:

  1. Ejecuta las tareas de línea base: confirma configuración efectiva, listeners, alcance de firewall y patrones reales de login.
  2. Añade/verifica un usuario break-glass con autenticación solo por clave y redes de origen restringidas.
  3. Despliega un archivo drop-in de endurecimiento en etapas canario → lote pequeño → flota completa, con sshd -t y una prueba scriptada de login como puerta de aceptación.
  4. Tras la aplicación, pasa una semana revisando logs de autenticación a diario. El sistema te dirá qué rompiste—si estás escuchando.
← Anterior
Debian 13: NFS más lento de lo esperado — demuestra que es sync/rsize/wsize y arréglalo (caso #23)
Siguiente →
Control de acceso VPN de oficina: permitir solo lo necesario (no LAN a LAN completo)

Deja un comentario