Cómo endurecer sshd_config: una lista de comprobación que puedes probar

Pon tus ajustes en un archivo drop-in como /etc/ssh/sshd_config.d/10-hardening.conf: PermitRootLogin no, PasswordAuthentication no, KbdInteractiveAuthentication no, AllowUsers con tu usuario, y valores más bajos de MaxAuthTries y LoginGraceTime. Comprueba con sudo sshd -t, recarga sshd y prueba un acceso nuevo mientras tu sesión actual sigue abierta.

Actualizada el . La configuración, sshd -t, sshd -T, sshd -T -C y cada rechazo que se muestra aquí se ejecutaron en OpenSSH 10.0p1 sobre Debian 13 en un contenedor desechable, con un cliente OpenSSH 10.3 en macOS.

Mantén una sesión abierta mientras pruebas

Recargar sshd no termina las conexiones existentes. En nuestra prueba, una sesión abierta antes de la recarga siguió funcionando y terminó con normalidad después. Así que deja abierta una sesión con capacidad de root y haz todas las pruebas desde una segunda terminal. Si un ajuste te deja fuera, lo arreglas desde la primera.

Antes de poner PasswordAuthentication no, asegúrate de que el acceso con clave funciona. Mira cómo desactivar el acceso por contraseña en SSH.

Usa un archivo drop-in y recuerda que gana el primer valor

En la mayoría de los ajustes, sshd se queda con el primer valor que lee. Debian y Ubuntu tienen Include /etc/ssh/sshd_config.d/*.conf al principio de sshd_config, así que un drop-in gana a una línea más abajo en el archivo principal. En nuestro sistema de prueba el archivo principal tenía PasswordAuthentication yes en la línea 125, y ganó el drop-in con no.

Los archivos de esa carpeta se leen por orden de nombre, y algunas imágenes en la nube añaden los suyos, así que un número bajo como 10- es una elección segura. Si tu sistema no tiene la línea Include, edita sshd_config directamente y pon los ajustes antes de cualquier bloque Match.

Los ajustes

# /etc/ssh/sshd_config.d/10-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
AllowUsers deploy
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
AjusteValor por defecto de OpenSSHQué hace el cambio
PermitRootLogin noprohibit-passwordRoot no puede entrar en absoluto, ni siquiera con una clave. Cada persona entra con su usuario y usa sudo, lo que deja constancia de quién hizo qué.
PasswordAuthentication noyesElimina como ataque el intento de adivinar contraseñas.
KbdInteractiveAuthentication noyesCierra la otra vía hacia un aviso de contraseña. Déjalo activado si usas códigos de un solo uso mediante PAM.
AllowUsers deploySin definir: cualquier cuenta puede intentarloSolo pueden entrar las cuentas listadas. Un error tipográfico aquí deja fuera a todos, por eso la primera sesión sigue abierta.
MaxAuthTries 36Menos intentos por conexión. Cada clave que ofrece un cliente cuenta, así que un cliente con muchas claves puede quedarse sin intentos. Mira Too many authentication failures.
LoginGraceTime 30120 segundosLas conexiones sin autenticar se cortan antes.
X11Forwarding nono, pero el archivo de Debian pone yesDesactiva una función que la mayoría de los servidores nunca usan.
AllowAgentForwarding noyesMira los riesgos del reenvío del agente.
AllowTcpForwarding noyesImpide los túneles SSH a través de este servidor, y su uso como jump host. No lo pongas si usas alguno de los dos.

El manual es claro sobre un límite: desactivar el reenvío TCP no mejora la seguridad salvo que a los usuarios se les niegue también una shell, porque cualquiera con una shell puede ejecutar su propio reenviador. Tómalo como limpieza para cuentas que tienen shell, y como restricción real solo para cuentas que no la tienen.

Deja MaxStartups y PerSourcePenalties en sus valores por defecto salvo que tengas un motivo. En las versiones actuales ya frenan las avalanchas de conexiones.

Comprueba antes de recargar

sshd -t comprueba la sintaxis y las claves de host. No imprime nada cuando todo está bien. Un error tipográfico da un error y un código de salida distinto de cero:

$ sudo sshd -t
/etc/ssh/sshd_config.d/99-typo.conf: line 1: Bad configuration option: PermitRootLogn
/etc/ssh/sshd_config.d/99-typo.conf: terminating, 1 bad configuration options

sshd -T imprime los valores que sshd usará de verdad, una vez combinados todos los archivos. Los nombres aparecen en minúsculas:

sudo sshd -T | grep -iE '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|allowusers|maxauthtries|logingracetime|x11forwarding|allowagentforwarding|allowtcpforwarding) '

Si usas bloques Match, añade -C para ver el resultado de una conexión concreta:

sudo sshd -T -C user=deploy,host=example.com,addr=203.0.113.10 | grep -i allowtcpforwarding

sshd -T lee los archivos en disco, no lo que cargó el demonio en marcha. Muestra lo que obtendrás tras la recarga.

Recarga y prueba desde una segunda terminal

# Debian, Ubuntu
sudo systemctl reload ssh
# Red Hat family
sudo systemctl reload sshd

Después prueba cada uno de estos. El primero debe funcionar y los demás deben fallar:

ssh deploy@server 'echo ok'
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password deploy@server
ssh root@server
ssh -N -L 8022:127.0.0.1:22 deploy@server

El último comando no falla al iniciar sesión. Se conecta, y el reenvío se rechaza cuando usas el puerto local. El registro del servidor muestra entonces una línea como esta:

refused local port forward: originator 127.0.0.1 port 63434, target 127.0.0.1 port 22

Un acceso de root rechazado aparece en el registro como User root from 203.0.113.10 not allowed because not listed in AllowUsers.

Algoritmos modernos: solo si necesitas restringirlos

Los valores por defecto actuales de OpenSSH son buenos. En OpenSSH 10.0 la lista de intercambio de claves por defecto empieza por el híbrido poscuántico mlkem768x25519-sha256. Definir tus propias listas implica que debes mantenerlas, así que hazlo cuando lo exija una auditoría o cuando tu sistema sea antiguo. Este conjunto conserva solo opciones modernas:

KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512,curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

Las versiones antiguas de OpenSSH no conocen algunos de estos nombres. Ejecuta ssh -Q kex, ssh -Q cipher y ssh -Q mac en el servidor y quita lo que no aparezca. Comprueba lo que negocia de verdad un cliente:

ssh -v deploy@server true 2>&1 | grep -E 'kex: algorithm|server->client cipher'

Los clientes y dispositivos antiguos fallarán con este mensaje, que tiene su propia guía:

Unable to negotiate with 203.0.113.10 port 22: no matching key exchange method found.

Esta lista también elimina las curvas NIST y los intercambios Diffie-Hellman, así que prueba todos los dispositivos y scripts que se conectan al servidor antes de quedarte con ella.

Lo que esto no cubre

Estos ajustes reducen lo que se puede intentar contra sshd. No parchean OpenSSH, así que mantenlo actualizado. Tampoco limitan lo que puede hacer un usuario que ya ha entrado. Para ataques repetidos en tus registros, mira fail2ban para SSH.

Para ver quién lo intenta, consulta cómo revisar los intentos de acceso SSH.

Preguntas

¿Recargar sshd cierra mi sesión SSH abierta?

No. Las sesiones existentes siguen después de recargar. Solo los nuevos accesos usan la nueva configuración, por eso se prueba desde un segundo terminal.

¿Dónde pongo mi propia configuración de sshd?

En un archivo de /etc/ssh/sshd_config.d/ con un número bajo, como 10-hardening.conf. sshd se queda con el primer valor que lee, así que gana un archivo leído pronto.

¿Cómo compruebo si sshd_config tiene errores?

Ejecuta sudo sshd -t. No muestra nada si la configuración es válida, y un error con un código de salida distinto de cero si no lo es.

Fuentes

Más en Proteger un servidor

Todas las guías