Hardening SSH en un VPS Linux: guía completa de sshd_config
Protege SSH en tu VPS con Debian 12 o Ubuntu 24.04. Generación de claves Ed25519, hardening de sshd_config, configuración de ProxyJump como bastion, cifrado reforzado y verificación con ssh-audit. Cada cambio probado antes de continuar.
SSH es la puerta de entrada a tu servidor. Los bots automatizados empiezan a golpear el puerto 22 a los pocos minutos de qué un VPS entra en línea. Esta guía recorre cada cambio de sshd_config necesario para proteger SSH en Debian 12 (OpenSSH 9.2) y Ubuntu 24.04 (OpenSSH 9.6), con un paso de verificación después de cada cambio para qué sepas qué realmente funciono.
Requisitos previos
Necesitas:
- Un VPS con Debian 12 o Ubuntu 24.04 (nuevo o existente)
- Un usuario no root con acceso sudo
- Una segunda terminal o sesión SSH abierta al servidor (la necesitaras para probar cambios sin quedarte fuera)
Esta guía forma parte de la serie Seguridad en Linux VPS: Amenazas, Capas y Guía de Hardening. Después de proteger SSH aqui, configura la protección automatica contra fuerza bruta con Instalar y configurar Fail2Ban en un VPS Linux.
Cómo genero una clave SSH segura para mi VPS?
Genera una clave Ed25519 en tu maquina local (tu portatil o estacion de trabajo, no el servidor). Ed25519 produce una clave de 256 bits qué ofrece 128 bits de seguridad, equivalente a RSA-3072, siendo más rápida de generar y verificar. Las firmás son deterministicas, asi qué no dependen de un generador de números aleatorios en el momento de firmar. Esto elimina toda una clase de ataques de implementacion qué afectan a RSA.
ssh-keygen -t ed25519 -C "yourname@yourmachine"
Veras una salida como esta:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/yourname/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/yourname/.ssh/id_ed25519
Your public key has been saved in /home/yourname/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:xR5xGk3TOs4mEfW8sBv7g7LkE2PLxYae2TqfGxpfM3Q yourname@yourmachine
Establece una passphrase (frase de paso). Si alguien roba tu fichero de clave privada, la passphrase es lo único qué se interpone entre esa persona y tus servidores.
Ed25519 vs RSA: qué clave debo usar?
| Ed25519 | RSA-4096 | |
|---|---|---|
| Tamaño de clave | 256 bits | 4096 bits |
| Fortaleza de seguridad | ~128 bits | ~140 bits |
| Generación de clave | Instantánea | 1-5 segundos |
| Verificación de firma | Más rápida | Más lenta |
| Tamaño del fichero de clave privada | 464 bytes | ~3.3 KB |
| Dependencia de RNG al firmar | No (determinístico) | Si |
| Compatibilidad | OpenSSH 6.5+ (2014) | Universal |
Usa Ed25519 salvo qué necesites conectar a sistemás con OpenSSH anterior a 6.5, algo raro en 2026.
Copia tu clave pública al servidor
Desde tu maquina local:
ssh-copy-id -i ~/.ssh/id_ed25519.pub youruser@your-server-ip
Si ssh-copy-id no está disponible (algúnas instalaciones de macOS), copia manualmente:
cat ~/.ssh/id_ed25519.pub | ssh youruser@your-server-ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Verifica: Inicia sesión desde una nueva terminal usando autenticación por clave:
ssh -i ~/.ssh/id_ed25519 youruser@your-server-ip
Si obtienes un shell sin introducir la contraseña del servidor (solo la passphrase de tu clave), la autenticación por clave funciona.
La directiva Include
Antes de editar sshd_config, ten en cuenta esto: tanto Debian 12 como Ubuntu 24.04 incluyen Include /etc/ssh/sshd_config.d/*.conf al principio de /etc/ssh/sshd_config. OpenSSH usa el primer valor qué encuentra para la mayoria de directivas. Cualquier fichero .conf en sshd_config.d/ tiene prioridad sobre los ajustes del fichero principal.
Comprueba lo qué ya está configurado:
ls -la /etc/ssh/sshd_config.d/
En Ubuntu 24.04, normalmente encontraras 50-cloud-init.conf si cloud-init está activo. Lee los ficheros existentes antes de hacer cambios:
cat /etc/ssh/sshd_config.d/*.conf 2>/dev/null
Vamos a colocar nuestro hardening en un único fichero qué se carga primero:
sudo touch /etc/ssh/sshd_config.d/00-hardening.conf
sudo chmod 600 /etc/ssh/sshd_config.d/00-hardening.conf
El prefijo 00- asegura qué nuestro fichero se lee antes qué otros snippets de configuración. El chmod 600 restringe el acceso de lectura solo a root, ya qué este fichero controla quien puede iniciar sesión.
Todos los cambios de sshd_config en está guía van en /etc/ssh/sshd_config.d/00-hardening.conf salvo qué se indiqué lo contrario.
Cómo desactivo la autenticación por contraseña en SSH?
Desactivar la autenticación por contraseña obliga a usar solo claves. Los ataques de fuerza bruta pierden sentido porqué no hay contraseña qué adivinar.
Abre el fichero de hardening:
sudo nano /etc/ssh/sshd_config.d/00-hardening.conf
Añade:
PasswordAuthentication no
KbdInteractiveAuthentication no
KbdInteractiveAuthentication sustituye al obsoleto ChallengeResponseAuthentication en OpenSSH 9.x. Desactiva ambos para cerrar todas las vias de login basadas en contraseña.
Antes de reiniciar sshd, valida siempre la configuración y manten tu sesión actual abierta.
sudo sshd -t
Si no hay salida, la configuración es valida. Los errores se imprimen en la terminal.
Ahora reinicia sshd:
sudo systemctl restart sshd
Verifica desde una segunda terminal (no cierres tu sesión actual):
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no youruser@your-server-ip
Deberias ver:
youruser@your-server-ip: Permission denied (publickey).
Eso confirma qué la autenticación por contraseña está desactivada. Fijate en qué dice (publickey) como único metodo permitido. Es exactamente lo qué buscamos.
Ahora confirma qué el login por clave sigue funcionando desde esa misma segunda terminal:
ssh youruser@your-server-ip
Si ambas pruebas pasan, la autenticación por contraseña está desactivada y el login por clave funciona. Si el login por clave falla, vuelve a tu primera sesión (qué sigue abierta) y corrige la configuración antes de quedarte fuera.
Cómo desactivo el login de root por SSH en Ubuntu y Debian?
El login directo de root por SSH debe estar desactivado. Incluso con autenticación solo por clave, una clave comprometida de root da acceso total al sistema sin registro de quien inicio sesión. Usa un usuario normal con sudo en su lugar.
Añade a /etc/ssh/sshd_config.d/00-hardening.conf:
PermitRootLogin no
Valida y reinicia:
sudo sshd -t && sudo systemctl restart sshd
Verifica desde una segunda terminal:
ssh root@your-server-ip
Salida esperada:
root@your-server-ip: Permission denied (publickey).
Verifica qué el ajuste se aplico con sshd -T (la T mayuscula muestra la configuración activa):
sudo sshd -T | grep -i permitrootlogin
permitrootlogin no
Cómo restrinjo el acceso SSH a usuarios y grupos concretos?
AllowUsers y AllowGroups limitan el login SSH a una lista explicita. Cualquiera qué no este en la lista es rechazado, aunqué tenga claves validas. Es una red de seguridad contra despliegues accidentales de claves o usuarios nuevos creados por paquetes.
Añade a /etc/ssh/sshd_config.d/00-hardening.conf:
AllowUsers youruser
Sustituye youruser por tu nombre de usuario real. Para permitir varios usuarios:
AllowUsers youruser deployer
Alternativamente, usa acceso basado en grupo (mejor para equipos):
sudo groupadd sshusers
sudo usermod -aG sshusers youruser
Después en la configuración:
AllowGroups sshusers
Orden de procesamiento
OpenSSH evalua el acceso en este orden: DenyUsers, AllowUsers, DenyGroups, AllowGroups. Una denegacion en cualquier etapa bloquea al usuario. Si usas AllowUsers, solo los usuarios listados pueden iniciar sesión. Si usas AllowGroups, solo los miembros de los grupos listados pueden iniciar sesión. Puedes combinarlos, pero AllowUsers se evalua primero.
Valida y reinicia:
sudo sshd -t && sudo systemctl restart sshd
Verifica desde una segunda terminal:
ssh youruser@your-server-ip
Confirma qué tu usuario puede seguir iniciando sesión. Después verifica qué un usuario no listado seria rechazado:
sudo sshd -T | grep -i allowusers
allowusers youruser
Qué limites de sesión SSH debo configurar?
Estas directivas limitan los intentos de autenticación, los tiempos de espera de conexión y las conexiónes no autenticadas. Ralentizan los ataques de fuerza bruta y limpian sesiónes inactivas.
Añade a /etc/ssh/sshd_config.d/00-hardening.conf:
MaxAuthTries 3
LoginGraceTime 20
MaxStartups 10:30:60
MaxSessions 3
ClientAliveInterval 300
ClientAliveCountMax 2
Esto es lo qué hace cada ajuste:
| Directiva | Valor | Efecto |
|---|---|---|
| MaxAuthTries | 3 | Desconecta tras 3 intentos fallidos de autenticación por conexión |
| LoginGraceTime | 20 | Da 20 segundos para autenticarse antes de desconectar |
| MaxStartups | 10:30:60 | Tras 10 conexiónes no autenticadas, descarta aleatoriamente el 30% de las nuevas. A 60, descarta todas. |
| MaxSessions | 3 | Maximo 3 sesiónes multiplexadas por conexión |
| ClientAliveInterval | 300 | Envia un paquete keepalive cada 300 segundos (5 minutos) |
| ClientAliveCountMax | 2 | Desconecta tras 2 respuestas keepalive sin recibir |
Calculo de ClientAliveInterval: El timeout real por inactividad es ClientAliveInterval x ClientAliveCountMax. Con estos valores: 300 x 2 = 600 segundos = 10 minutos. Una sesión inactiva se desconecta tras 10 minutos sin respuesta.
Valida y reinicia:
sudo sshd -t && sudo systemctl restart sshd
Verifica:
sudo sshd -T | grep -E "maxauthtries|logingracetime|maxstartups|maxsessions|clientaliveinterval|clientalivecountmax"
maxauthtries 3
logingracetime 20
maxstartups 10:30:60
maxsessions 3
clientaliveinterval 300
clientalivecountmax 2
Desactivar funciones de reenvio innecesarias
Las funciones de forwarding (reenvio) amplian la superficie de ataqué de SSH. Desactiva todo lo qué no uses activamente.
Añade a /etc/ssh/sshd_config.d/00-hardening.conf:
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
PermitTunnel no
Valida y reinicia:
sudo sshd -t && sudo systemctl restart sshd
Verifica:
sudo sshd -T | grep -E "allowagentforwarding|allowtcpforwarding|x11forwarding|permittunnel"
allowagentforwarding no
allowtcpforwarding no
x11forwarding no
permittunnel no
Por qué debo desactivar el agent forwarding de SSH?
El agent forwarding permite qué un servidor remoto use tus claves SSH locales para autenticarse en otros servidores. Suena practico para saltar entre maquinas. El problema: cualquiera con root en ese servidor remoto puede secuestrar tu socket de agente y usar tus claves.
El escenario de ataque:
- Activas el agent forwarding y te conectas por SSH al Servidor A.
- OpenSSH crea un socket en
/tmp/ssh-XXXX/agent.YYYYen el Servidor A. - Un atacante con root en el Servidor A lee la variable de entorno
SSH_AUTH_SOCKde tu sesión. - El atacante se conecta a ese socket:
SSH_AUTH_SOCK=/tmp/ssh-XXXX/agent.YYYY ssh user@server-b. - El Servidor B ve una autenticación valida usando tu clave. El atacante está dentro.
El atacante nunca toco tu clave privada. Solo necesito root en el servidor intermedio mientras tu sesión estaba activa.
Usa ProxyJump en su lugar (siguiente sección). Proporciona el mismo acceso multi-salto sin exponer tu socket de agente en ningún servidor intermedio.
Cómo configuro SSH ProxyJump para acceso a traves de un bastion host?
ProxyJump enruta tu conexión SSH a traves de un jump host (bastion) sin agent forwarding. Tus claves nunca salen de tu maquina local. La conexión está cifrada de extremo a extremo: el bastion solo ve trafico cifrado pasando.
Uso por línea de comandos
ssh -J jumpuser@bastion.example.com targetuser@10.0.1.50
El flag -J le dice a SSH qué se conecte primero al bastion y luego haga un tunel hasta el destino. Puedes encadenar varios saltos con comas:
ssh -J jump1@bastion1,jump2@bastion2 targetuser@10.0.1.50
Configuración en el fichero SSH config
Para uso habitual, añade entradas en ~/.ssh/config de tu maquina local:
Host bastion
HostName bastion.example.com
User jumpuser
IdentityFile ~/.ssh/id_ed25519
Host internal-app
HostName 10.0.1.50
User appuser
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519
Host internal-db
HostName 10.0.2.100
User dbadmin
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519
Ahora conecta a los servidores internos directamente:
ssh internal-app
SSH gestiona el salto a traves del bastion automáticamente.
Hardening del servidor bastion
En el bastion host, restringe lo qué los usuarios de salto pueden hacer. Añade al sshd_config del bastion:
Match User jumpuser
PermitTTY no
X11Forwarding no
PermitTunnel no
ForceCommand /usr/sbin/nologin
AllowTcpForwarding yes
Esto permite TCP forwarding (necesario para ProxyJump) pero impide qué el usuario de salto obtenga un shell, ejecute comandos o use X11. Si el bastion se ve comprometido, el atacante obtiene una cuenta de solo reenvio sin acceso a shell.
Qué cifrados y algoritmos de intercambio de claves SSH son seguros?
Las listas de cifrado por defecto de OpenSSH incluyen algoritmos mantenidos por compatibilidad hacia atras. Eliminar los debiles reduce la superficie de ataque. Estas recomendaciones son para OpenSSH 9.2+ (Debian 12) y 9.6+ (Ubuntu 24.04).
Primero, regenera las claves del host usando solo Ed25519 y elimina las claves DSA o ECDSA:
sudo rm -f /etc/ssh/ssh_host_*key*
sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
sudo ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
Mantenemos una clave RSA del host para clientes qué no soporten Ed25519 (raro, pero evita bloqueos).
Después, elimina los moduli Diffie-Hellman debiles (grupos menores de 3072 bits):
sudo awk '$5 >= 3071' /etc/ssh/moduli > /tmp/moduli.safe
sudo mv /tmp/moduli.safe /etc/ssh/moduli
sudo chown root:root /etc/ssh/moduli
sudo chmod 644 /etc/ssh/moduli
Añade a /etc/ssh/sshd_config.d/00-hardening.conf:
HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
| Categoria | Algoritmos | Notas |
|---|---|---|
| Key exchange | sntrup761x25519, curve25519, DH group16/18 | sntrup761 es hibrido post-cuantico. curve25519 es el estandar actual. |
| Ciphers | ChaCha20-Poly1305, AES-256-GCM, AES-128-GCM, AES-CTR | ChaCha20 primero: más rápido por software sin AES-NI. GCM es cifrado autenticado. |
| MACs | HMAC-SHA2 ETM, UMAC-128 ETM | Solo ETM (Encrypt-then-MAC). Los modos sin ETM son más debiles. |
| Host keys | Ed25519, RSA (SHA-2) | Sin DSA (roto). Sin ECDSA (dudas sobre curvas NIST). |
Valida y reinicia:
sudo sshd -t && sudo systemctl restart sshd
Verifica desde una segunda terminal qué puedes seguir conectando. Si tu cliente SSH no soporta ningúno de estos algoritmos (cliente muy antiguo), obtendras un error "no matching cipher". En ese caso, actualiza tu cliente o añade temporalmente el algoritmo necesario.
Configurar un banner de login
Oculta la información de version de SSH y muestra un aviso legal:
sudo nano /etc/ssh/banner.txt
Authorized access only. All activity is monitored and logged.
Mantenlo corto. Los banners largos con info del sistema ayudan a los atacantes a identificar tu SO.
Añade a /etc/ssh/sshd_config.d/00-hardening.conf:
Banner /etc/ssh/banner.txt
DebianBanner no
DebianBanner no elimina la cadena de version Debian/Ubuntu del banner del protocolo SSH. La divulgacion de version ayuda a los atacantes a apuntar a vulnerabilidades conocidas de tu build especifica de OpenSSH.
Valida y reinicia:
sudo sshd -t && sudo systemctl restart sshd
Verifica desde tu maquina local:
ssh -v youruser@your-server-ip 2>&1 | grep "banner"
Referencia completa de sshd_config con hardening
Aqui está el fichero completo /etc/ssh/sshd_config.d/00-hardening.conf con todos los ajustes de está guía:
# Authentication
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
AuthenticationMethods publickey
# Access control
AllowUsers youruser
# Session limits
MaxAuthTries 3
LoginGraceTime 20
MaxStartups 10:30:60
MaxSessions 3
ClientAliveInterval 300
ClientAliveCountMax 2
# Forwarding restrictions
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
PermitTunnel no
# Cryptography
HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
# Banner
Banner /etc/ssh/banner.txt
DebianBanner no
# Logging
LogLevel VERBOSE
LogLevel VERBOSE registra detalles adicionales incluyendo las huellas de clave usadas para la autenticación. Util para auditar quien inicio sesión con qué clave.
Después de escribir este fichero, valida siempre antes de reiniciar:
sudo sshd -t && sudo systemctl restart sshd
Comprueba la configuración efectiva completa:
sudo sshd -T
Esto muestra todos los ajustes activos, incluidos los valores por defecto. Filtra con grep para comprobar valores concretos:
sudo sshd -T | grep -E "permitrootlogin|passwordauthentication|allowusers"
permitrootlogin no
passwordauthentication no
allowusers youruser
Cómo verifico mi hardening SSH con ssh-audit?
ssh-audit escanea tu servidor y califica cada algoritmo como bueno, advertencia o fallo. Ejecutalo después del hardening para confirmar qué todo pasa.
Instala en tu maquina local o en un servidor separado (no el objetivo):
pip3 install ssh-audit
O con apt en Debian/Ubuntu:
sudo apt update && sudo apt install -y ssh-audit
Escanea tu servidor:
ssh-audit your-server-ip
Con el hardening de está guía, no deberias ver entradas [fail]. La salida comienza con la version de OpenSSH detectada, y luego lista cada categoria de algoritmo:
# general
(gen) banner: SSH-2.0-OpenSSH_9.6
(gen) software: OpenSSH 9.6
(gen) compression: enabled (zlib@openssh.com)
# key exchange algorithms
(kex) sntrup761x25519-sha512@openssh.com -- [info] available since OpenSSH 8.5
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4
...
# encryption algorithms (ciphers)
(enc) chacha20-poly1305@openssh.com -- [info] available since OpenSSH 6.5
(enc) aes256-gcm@openssh.com -- [info] available since OpenSSH 6.2
...
Si ves entradas [fail], comprueba qué tu 00-hardening.conf está cargado (recuerda el orden de Include) y qué ningún otro fichero en sshd_config.d/ está sobreescribiendo tus ajustes.
ssh-audit tambien tiene guías de hardening integradas:
ssh-audit --list-hardening-guides
Solución de problemas
Me he quedado fuera
Si no puedes conectar por SSH tras un cambio de configuración:
- Usa la consola web de tu proveedor de VPS (KVM/VNC) para iniciar sesión directamente.
- Corrige
/etc/ssh/sshd_config.d/00-hardening.conf. - Ejecuta
sshd -tpara validar. - Reinicia:
systemctl restart sshd.
Por eso insistimos: nunca cierres tu sesión activa antes de probar desde una segunda terminal.
sshd no arranca tras un cambio de configuración
sudo sshd -t
Esto imprime la línea exacta y el fichero con el error. Problemás habituales:
- Errata en el nombre de un algoritmo (no se permiten espacios en las listas separadas por comas)
- Directivas duplicadas en varios ficheros (revisa
sshd_config.d/y elsshd_configprincipal) AllowUserscon un nombre de usuario qué no existe (sshd arranca, pero nadie puede iniciar sesión)
Conexión rechazada tras el hardening de cifrado
Tu cliente SSH local puede no soportar los cifrados qué configuraste. Comprueba qué cifrados soporta tu cliente:
ssh -Q cipher
Compara con la lista del servidor. Añade de nuevo el cifrado qué tu cliente necesite, o actualiza tu cliente.
Comprobar los logs de SSH
sudo journalctl -u sshd -f
Observa los logs en tiempo real mientras intentas una conexión desde otra terminal. Los fallos de autenticación, errores de configuración y desconexiónes aparecen aqui.
Verificar qué fichero de configuración establece una directiva
sudo sshd -T | grep passwordauthentication
Si el valor no coincide con lo qué configuraste, otro fichero en sshd_config.d/ está sobreescribiendo el tuyo. Los ficheros se cargan en orden alfabetico. Nuestro 00-hardening.conf se carga primero, asi qué gana para la mayoria de directivas. Pero revisa si cloud-init u otras herramientas de gestion escriben sus propias configuraciónes.
Lista de verificación
Repasa esto tras completar todos los pasos de hardening:
- Autenticación por contraseña desactivada:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no youruser@serverdevuelve "Permission denied" - Login de root desactivado:
ssh root@serverdevuelve "Permission denied" - Autenticación por clave funciona:
ssh youruser@serverte da un shell - AllowUsers activo:
sudo sshd -T | grep allowusersmuestra tu usuario - Limites de sesión configurados:
sudo sshd -T | grep maxauthtriesmuestra 3 - Forwarding desactivado:
sudo sshd -T | grep allowagentforwardingmuestra no - Solo cifrados fuertes:
ssh-audit server-ipno muestra entradas [fail] - Logs funcionando:
sudo journalctl -u sshd -n 20muestra entradas de autenticación recientes
Después de proteger SSH, configura Instalar y configurar Fail2Ban en un VPS Linux para banear automáticamente las IPs qué fallen la autenticación. Para control de acceso SSH a nivel de red, consulta Cómo configurar un firewall en un VPS Linux con UFW y nftables.
¿Listo para probarlo?
Obtén un VPS Linux listo en segundos. →