Imagina que acabas de instalar Ubuntu Server, o la distro que tu quieras en un VPS o en esa máquina que tienes en casa. Has seguido el asistente, has puesto tu contraseña, y el sistema arranca limpio como una patena. Instalas Docker, despliegas tus primeros contenedores, abres el puerto 22 para poder conectarte por SSH… y ya está, tienes un servidor en internet.
¿Sabes qué pasa en las siguientes 24 horas?. Que hay bots, scripts automatizados y curiosos probando combinaciones de usuario y contraseña contra tu puerto 22. Es tan sencillo como lanzar un escáner de puertos y un diccionario. No es que vayan a por ti específicamente, es que barren internet en busca de servidores con la configuración por defecto. Y el 90% de los servidores recién instalados tienen la configuración por defecto.
Este capítulo es la primera capa de cebolla de la seguridad de tu servidor. Vamos a blindar SSH para que, aunque alguien sepa tu IP, sea misión imposible colarse.
¿Por qué SSH es el punto crítico?
SSH es el protocolo que usas para administrar tu servidor de forma remota. Es la llave maestra de tu casa digital. Si alguien consigue acceso por SSH, tiene control total de tu máquina, puede instalar lo que quiera, robar datos, usar tu servidor para minar criptomonedas, o convertirlo en parte de una botnet.
El problema es que SSH viene con una configuración pensada para que funcione nada más instalar, no para que sea seguro. Por defecto permite,
- Acceso root con contraseña: sí, puedes conectarte como root directamente
- Autenticación por contraseña: vulnerable a ataques de fuerza bruta
- Puerto 22: el puerto por defecto, el primero que escanea cualquier bot
- Algoritmos antiguos: algunos métodos de cifrado obsoletos pero aún aceptados
Cada una de estas opciones por defecto es una puerta entreabierta. Vamos a cerrarlas una a una.
Paso 1: autenticación con claves SSH
Lo primero y más importante, olvídate de las contraseñas para SSH. Las contraseñas se pueden adivinar, interceptar, o sacar con un diccionario. Las claves SSH son criptográficamente seguras.
El mecanismo es simple, generas un par de claves (una pública y una privada). La pública la instalas en el servidor. La privada la guardas en tu máquina local, protegida con una contraseña (passphrase). Cuando te conectas, el servidor te desafía con un reto criptográfico que solo puedes responder si tienes la clave privada.
Generar el par de claves
En tu máquina local (no en el servidor), ejecuta,
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519
Te va a preguntar una passphrase. Pon una. No es un paso opcional. La passphrase protege tu clave privada, si alguien roba tu archivo id_ed25519, sin la passphrase no puede usarlo.
Vamos a desglosar los parámetros por si no los conoces,
-t ed25519: usa el algoritmo Ed25519, más rápido y seguro que RSA. Si tu sistema es muy antiguo y no soporta Ed25519, usa-t rsa -b 4096.-a 100: número de rondas de derivación de clave. Cuanto más alto, más segura la passphrase frente a ataques de fuerza bruta. El valor por defecto suele ser 16; 100 es un buen equilibrio entre seguridad y velocidad.-f ~/.ssh/id_ed25519: dónde guardar la clave. Si no lo especificas, te lo pregunta.
El resultado son dos archivos,
~/.ssh/id_ed25519: tu clave privada. No la compartas nunca. Ni la subas a GitHub, ni la mandes por correo, ni la copies a servidores.~/.ssh/id_ed25519.pub: tu clave pública. Esta sí puedes copiarla a los servidores a los que quieras acceder.
Copiar la clave al servidor
El método más cómodo,
ssh-copy-id -i ~/.ssh/id_ed25519.pub usuario@tu-servidor
Esto te pide la contraseña de tu usuario en el servidor (la última vez que la vas a usar) y automáticamente añade tu clave pública a ~/.ssh/authorized_keys.
Si no tienes ssh-copy-id (en algunos sistemas minimalistas no viene), hazlo a mano,
cat ~/.ssh/id_ed25519.pub | ssh usuario@tu-servidor "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
Verificar que funciona
Antes de desactivar nada, comprueba que puedes entrar sin contraseña,
ssh usuario@tu-servidor
Si te pide la passphrase de tu clave (no la contraseña del servidor), está funcionando. Si te pide la contraseña del servidor, algo falló — revisa que la clave pública está en authorized_keys y que los permisos son correctos.
Puedes añadir la clave a tu agente SSH para no tener que escribir la passphrase cada vez,
ssh-add ~/.ssh/id_ed25519
Si quieres que se cargue automáticamente al iniciar sesión, añádelo a tu ~/.bashrc o ~/.config/fish/config.fish (si usas Fish Shell, que tiene un tutorial en atareao.es),
# En bash
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519
# En fish
eval (ssh-agent -c)
ssh-add ~/.ssh/id_ed25519
Pero ojo, esto solo es recomendable en tu máquina local de confianza. En un portátil que puedas perder, mejor escribir la passphrase cada vez.
Paso 2: desactivar el acceso root por SSH
El usuario root existe en todos los sistemas Linux. Los bots lo saben. Lo primero que intentan es root:admin, root:123456, root:password. Si permites el acceso root por SSH, estás poniendo una diana en tu servidor.
La solución es simple, no permitir que root se conecte por SSH. Tú te conectas con tu usuario normal y, cuando necesitas permisos de administrador, usas sudo.
Edita el archivo /etc/ssh/sshd_config,
sudo nano /etc/ssh/sshd_config
Busca la línea PermitRootLogin y cámbiala a,
PermitRootLogin no
Si la línea está comentada (con # al principio), descoméntala y pon el valor.
¿Qué implica esto exactamente?
- No puedes hacer
ssh root@tu-servidor - Sí puedes conectarte con tu usuario y luego hacer
sudo -iosudo comando - Si tu usuario tiene permisos sudo (que debería), no pierdes ninguna capacidad
¿Y si ya tienes una clave pública en /root/.ssh/authorized_keys?
Da igual. Con PermitRootLogin no se deniega todo acceso directo como root, independientemente del método de autenticación. Las claves no son una excepción a menos que uses prohibit-password (que permite claves pero no contraseñas para root). Pero nosotros vamos más allá y lo desactivamos por completo.
Paso 3: restringir qué usuarios pueden conectarse
Si en tu servidor hay varios usuarios del sistema (quizás creaste uno para una aplicación, o el sistema tiene cuentas de servicio), no todos necesitan acceso SSH. De hecho, ninguno que no seas tú debería tenerlo.
Añade esta línea en /etc/ssh/sshd_config,
AllowUsers lorenzo
Cambia lorenzo por tu nombre de usuario. Si necesitas que varios usuarios tengan acceso SSH, sepáralos con espacios,
AllowUsers lorenzo colaborador
Esto es una lista blanca, solo los usuarios listados pueden conectarse por SSH. El resto, aunque existan en el sistema y tengan contraseña, reciben un rechazo automático.
Paso 4: establecer timeouts de sesión
Si te conectas a tu servidor por SSH y te olvidas de cerrar la sesión, esa conexión queda abierta. Un atacante que de alguna manera acceda a tu máquina local podría secuestrar esa sesión. También es un riesgo si dejas una sesión abierta en un lugar público.
Configura estos parámetros en sshd_config,
ClientAliveInterval 300
ClientAliveCountMax 0
LoginGraceTime 30
¿Qué hace cada uno?
ClientAliveInterval 300: cada 300 segundos (5 minutos), el servidor envía un «ping» silencioso al cliente. Si no responde…ClientAliveCountMax 0: …el servidor cierra la conexión tras el primer ping sin respuesta. Por defecto son 3, pero tener que esperar 15 minutos (3 × 300s) para que se cierre una sesión muerta es demasiado.LoginGraceTime 30: tiempo máximo para completar la autenticación. Si en 30 segundos no te has logueado, se corta. Esto evita que un atacante mantenga muchas conexiones «a medias» consumiendo recursos.
Paso 5: reforzar los algoritmos criptográficos
SSH negocia algoritmos de cifrado, intercambio de claves y autenticación con cada conexión. Por defecto acepta algoritmos antiguos que tienen vulnerabilidades conocidas. Vamos a restringir la lista a los que se consideran seguros hoy.
Añade en sshd_config,
# Algoritmos de intercambio de claves (KEX)
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
# Cifrados
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
# MACs (Message Authentication Code)
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
# Algoritmos de autenticación de host
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com
Esto parece una sopa de letras, pero la idea es sencilla, solo se aceptan algoritmos modernos. El que tiene nombre raro (sntrup761x25519) es el algoritmo post-cuántico que OpenSSH introdujo hace un par de versiones. Si tu versión de OpenSSH no lo soporta (es relativamente reciente), puedes quitarlo de la lista.
Para saber qué algoritmos soporta tu servidor,
ssh -Q kex # Intercambio de claves
ssh -Q cipher # Cifrados
ssh -Q mac # MACs
ssh -Q key # Algoritmos de host key
Y para ver qué algoritmos se usaron en una conexión real,
ssh -v usuario@tu-servidor 2>&1 | grep -E "kex|algorithm|cipher|mac"
Paso 6: otras configuraciones importantes
Además de lo anterior, hay algunos ajustes menores que marcan la diferencia,
Puerto alternativo
Cambiar el puerto 22 por otro alto (por ejemplo, el 2222 o el 8822) reduce drásticamente el ruido de bots. No es seguridad por oscuridad (el puerto no es un secreto, se puede escanear), pero elimina el 99% de los intentos automatizados que solo prueban el puerto 22.
Port 8822
Si lo cambias, recuerda,
- Abrir ese puerto en tu firewall (lo veremos en el capítulo 2)
- Conectarte especificando el puerto:
ssh -p 8822 usuario@tu-servidor - O configurarlo en
~/.ssh/configpara no tener que escribirlo siempre:
Host mi-servidor
HostName tu-servidor.com
Port 8822
User lorenzo
IdentityFile ~/.ssh/id_ed25519
Desactivar la autenticación por contraseña
Una vez confirmado que las claves funcionan, desactiva las contraseñas,
PasswordAuthentication no
ChallengeResponseAuthentication no
Esto fuerza que toda conexión SSH use claves. No hay vuelta atrás, si pierdes tu clave privada y no tienes otro método de acceso, te quedas fuera de tu servidor.
Desactivar .rhosts y otras reliquias
IgnoreRhosts yes
HostbasedAuthentication no
PermitEmptyPasswords no
UsePAM yes
UsePAM yes puede parecer contradictorio si has desactivado la autenticación por contraseña, pero PAM se usa para muchas cosas además de contraseñas (como los módulos de límites de recursos). Déjalo activado.
Paso 7: configurar el cliente SSH (~/.ssh/config)
Hemos blindado el servidor, pero el cliente también merece atención. El archivo ~/.ssh/config de tu máquina local te permite definir perfiles de conexión para no tener que escribir todos los parámetros cada vez.
Un ejemplo completo,
Host servidor-casa
HostName 192.168.1.100
Port 8822
User lorenzo
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
ServerAliveCountMax 3
Compression yes
CompressionLevel 6
Host servidor-vps
HostName tu-vps.com
Port 8822
User lorenzo
IdentityFile ~/.ssh/id_ed25519
ProxyJump servidor-casa
LogLevel VERBOSE
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
Host *
AddKeysToHost yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
Vamos a desglosar las opciones más interesantes,
ServerAliveInterval 60: el cliente envía un ping cada 60 segundos para mantener viva la conexión. Útil si tu router corta conexiones inactivas.ServerAliveCountMax 3: si el servidor no responde a 3 pings, el cliente corta la conexión. Así no te quedas colgado con una terminal congelada.Compression yes: comprime los datos antes de enviarlos. En conexiones lentas (como una VPN sobre redes móviles) nota mucho la diferencia. En redes locales no merece la pena.ProxyJump: permite saltar a través de otro servidor. En el ejemplo, para conectarte al VPS pasas antes por el servidor de casa. Es como un túnel SSH en cadena, pero sin tener que montar nada manualmente.IdentitiesOnly yes: fuerza a usar solo la clave especificada para ese host. Sin esto, SSH prueba todas las claves que tenga en el agente, y si tienes muchas, el servidor te rechaza tras varios intentos fallidos.LogLevel VERBOSE: útil para depurar problemas de conexión. Normalmente basta conINFO(el valor por defecto).AddKeysToHost yes: añade automáticamente la clave al known_hosts cuando te conectas a un servidor nuevo. Cómodo, pero reduce la seguridad porque no verificas manualmente la huella del servidor.
El bloque Host * es el comodín, aplica a todas las conexiones que no tengan una configuración específica. Por eso va al final.
Con este archivo configurado, conectarte es tan simple como,
ssh servidor-casa
ssh servidor-vps
Sin puertos, sin usuarios, sin rutas de clave. SSH lo resuelve automáticamente.
SSH Agent Forwarding: útil pero peligroso
El forwarding del agente SSH permite usar tus claves locales desde el servidor remoto. Es decir, te conectas al servidor A, y desde ahí puedes hacer git push a GitHub sin tener que copiar tu clave privada al servidor.
Se activa con -A,
ssh -A servidor-casa
¿El problema? Cualquier persona con acceso root al servidor A puede usar tu agente SSH mientras estás conectado. No necesita tu clave privada, solo el socket del agente. Es un riesgo enorme.
Reglas de oro para el forwarding,
- No actives forwarding a servidores que no controlas al 100%
- No dejes sesiones con forwarding abiertas cuando no las estés usando
- Usa
ForwardAgent nopor defecto y actívalo solo para hosts concretos y de confianza - Alternativa mejor: usa deploy keys en GitHub para cada servidor, así no necesitas forwarding
En ~/.ssh/config,
Host servidor-casa
ForwardAgent yes # Solo si realmente lo necesitas
Host *
ForwardAgent no # Por defecto, desactivado
Paso 8: verificación avanzada con ssh-audit
Además de las comprobaciones manuales, existe una herramienta específica para auditar la configuración de SSH, ssh-audit. Analiza tanto el servidor como el cliente y te dice qué algoritmos son seguros, cuáles están obsoletos y cuáles son directamente peligrosos.
Para instalarla en tu máquina local,
# Con pip (recomendado)
pip install ssh-audit
# O clonando el repositorio
git clone https://github.com/jtesta/ssh-audit.git
cd ssh-audit
Para auditar tu servidor,
ssh-audit tu-servidor.com
La salida es muy clara. Te marca cada algoritmo con colores,
- Verde: seguro, no toques nada
- Amarillo: obsoleto, deberías actualizar cuando puedas
- Rojo: inseguro, desactívalo ya
Ejecútalo antes y después de aplicar la configuración de este capítulo. La diferencia te sorprenderá.
Paso 9: aplicar los cambios
Una vez que hayas editado /etc/ssh/sshd_config con todas las opciones anteriores, verifica que la sintaxis es correcta,
sudo sshd -t
Si no muestra nada, la configuración es válida. Si muestra un error, te dice exactamente qué línea tiene el problema.
Después, reinicia el servicio,
sudo systemctl restart sshd
Importante: antes de cerrar la sesión actual, abre otra terminal y prueba que puedes conectarte con la nueva configuración. Si algo falla, la sesión que tienes abierta te permite corregirlo. Si cierras la sesión sin verificar, podrías quedarte fuera.
# Desde otra terminal
ssh -p 8822 usuario@tu-servidor
Si todo funciona, la sesión nueva se abre sin problemas. Entonces sí, puedes cerrar la primera.
Verificación: ¿cómo sé que lo he hecho bien?
Aquí tienes una checklist para confirmar que el blindaje SSH está correcto,
# 1. Verificar que root no puede conectarse
ssh root@tu-servidor
# Debe mostrar: "Permission denied (publickey)"
# 2. Verificar que solo tu usuario puede conectarse
ssh otro-usuario@tu-servidor
# Debe mostrar: "Permission denied"
# 3. Verificar que no se aceptan contraseñas
ssh -o PreferredAuthentications=password usuario@tu-servidor
# Debe mostrar: "Permission denied (publickey)"
# 4. Verificar el puerto configurado
sudo ss -tlnp | grep :22
# Si cambiaste el puerto, debe mostrar el nuevo
# 5. Verificar los algoritmos activos
ssh -v usuario@tu-servidor 2>&1 | grep -E "kex|algorithm"
# Todos deben ser de los que configuraste
Si en el primer comando te deja entrar como root, algo está mal. Vuelve a revisar PermitRootLogin.
Si en el tercero te pide contraseña, es que PasswordAuthentication sigue activado.
Troubleshooting: cuando algo falla
No todo va a funcionar a la primera. Aquí tienes los problemas más comunes y cómo resolverlos.
«Permission denied (publickey)» y no puedo entrar
Este error significa que el servidor ha rechazado todas las claves que le has presentado. Causas posibles,
- La clave pública no está en
authorized_keys: verifica concat ~/.ssh/authorized_keysen el servidor - Permisos incorrectos:
authorized_keysdebe ser600y.sshdebe ser700 - Estás usando la clave equivocada: especifícala con
-i ~/.ssh/id_ed25519 - SELinux o AppArmor bloqueando: revisa los logs con
sudo journalctl -u sshd -n 20
Para depurar, conecta con modo verboso:
ssh -vvv -p 8822 usuario@tu-servidor
La salida te dice exactamente qué claves probó y por qué las rechazó.
«Connection refused»
El servidor no está escuchando en ese puerto. Posibles causas,
- El servicio sshd no se ha reiniciado:
sudo systemctl restart sshd - Firewall bloqueando el puerto: compruébalo con
sudo nft list ruleset(lo veremos en el capítulo 2) - El puerto configurado no coincide: revisa
Portensshd_config - El servicio no ha arrancado:
sudo systemctl status sshd
«Connection timed out»
El servidor está detrás de un firewall que descarta los paquetes silenciosamente. Revisa las reglas de tu firewall local, del router de tu casa, o del panel de control de tu VPS.
Me he quedado fuera del servidor
Esto pasa. A mí me ha pasado. La primera vez que cambias la configuración de SSH y cierras la sesión sin verificar, te quedas fuera. Soluciones según el caso,
- Si es un VPS: la mayoría de proveedores tienen una consola web (VNC/Serial) desde su panel de control. Úsala para entrar y revertir los cambios.
- Si es un servidor en casa: necesitas acceso físico o IPMI/iDRAC si tu placa lo soporta.
- Si tienes un usuario con acceso físico: pídele que edite el archivo.
La lección: nunca cierres la sesión sin verificar la nueva configuración desde otra terminal.
Resumen del capítulo
En este capítulo has blindado el acceso SSH a tu servidor. En concreto:
- Generaste un par de claves Ed25519 y copiaste la pública al servidor
- Desactivaste el acceso root por SSH
- Restringiste los usuarios que pueden conectarse
- Configuraste timeouts para cerrar sesiones huérfanas
- Reforzaste los algoritmos criptográficos eliminando los obsoletos
- Opcionalmente cambiaste el puerto para reducir el ruido de bots
- Verificaste cada cambio antes de cerrar la sesión
Con esto, tu servidor ya no es un blanco fácil. Pero esto es solo la primera capa. En el próximo capítulo vamos a instalar un firewall con nftables para controlar quién puede hablar con tu servidor y cómo.
Referencias
- Documentación oficial de OpenSSH: https://www.openssh.com/manual.html
- Mozilla SSH Configuration Generator: https://infosec.mozilla.org/guidelines/openssh
- nixCraft – Top 20 OpenSSH Server Best Security Practices: https://www.cyberciti.biz/tips/linux-unix-bsd-openssh-server-best-practices.html
- bettercrypto.org – Recommended crypto algorithms: https://bettercrypto.org/
- ssh-audit – Herramienta para auditar configuración SSH: https://github.com/jtesta/ssh-audit