Proteger SSH¶
SSH es la puerta de entrada principal a un servidor remoto. Por defecto, muchas imágenes cloud vienen con acceso root habilitado y autenticación por contraseña — dos vectores de ataque que conviene cerrar cuanto antes.
Enfoque por niveles¶
Esta guía construye la configuración de SSH por niveles: cada uno añade unas pocas líneas al mismo fichero y sube un escalón de seguridad. Puedes parar en el nivel que quieras y retomarlo más tarde — o saltarte los que no te aporten.
Todo se configura en un único fichero aparte (00-hardening.conf), sin tocar sshd_config principal. Así se mantienen las actualizaciones del sistema limpias y la config propia, separada.
| Nivel | Qué añade | ¿Aplicarlo? |
|---|---|---|
| 1 | Cerrar root y limitar usuarios | Siempre, desde el primer acceso |
| 2 | Clave pública y límites de login | Obligatorio en producción (al desplegar tu clave) |
| 3 | Banner legal | Opcional |
| 4 | Desactivar funcionalidades | Recomendado; omite lo que necesites |
Nivel 1 — Cerrar root¶
Aplícalo siempre, desde el primer acceso al servidor: nadie entra como root y solo el usuario hosting puede conectar.
-
Crear el drop-in de hardening:
sudo vim /etc/ssh/sshd_config.d/00-hardening.confContenido:
PermitRootLogin no PasswordAuthentication yes AllowUsers hostingPuerto 22 se mantiene — cambiarlo no aporta seguridad real y es trivialmente detectable.
PasswordAuthentication yeses el punto de partida, para no perder el acceso al servidor. En cuanto tengas tu clave pública desplegada, aplica el Nivel 2 para desactivarla.Por qué
00-y no99-En
sshd_config.d/los ficheros deIncludese leen en orden alfabético y, a diferencia desysctl.d/olimits.d/, gana el primer valor encontrado, no el último. Muchas imágenes cloud (DigitalOcean, Hetzner...) traen un50-cloud-init.confconPasswordAuthentication yesexplícito — si tu fichero se llamara99-hardening.confse leería después y tunose ignoraría en silencio (sshd -Tseguiría diciendoyes). Llamándolo00-te aseguras de que se lee el primero y gana siempre. -
Validar la sintaxis antes de recargar:
sudo sshd -t -
Aplicar cambios (no corta la sesión actual, a diferencia de
restart):sudo service sshd reload -
Antes de cerrar la sesión actual, abrir una terminal nueva y confirmar que entras con
hosting:ssh hosting@<ip-servidor>
Nivel 2 — Clave pública¶
Obligatorio en producción. Desactiva las contraseñas y deja solo la clave pública, con límites en el proceso de login. Solo puedes saltarlo mientras sigas conectando con PasswordAuthentication yes del Nivel 1.
-
En tu máquina local (no en el servidor), generar el par de claves si todavía no tienes una (si ya tienes
~/.ssh/id_ed25519de otro proyecto, puedes reutilizarla y saltar al paso 2):ssh-keygen -t ed25519 -C "hosting@<nombre-servidor>"Acepta la ruta por defecto (
~/.ssh/id_ed25519) pulsando Enter y pon una passphrase (recomendado; te la pedirá el agente SSH, no en cada conexión, una vez lo configures). -
En tu máquina local, copiar la clave pública al servidor (del Nivel 1,
hostingya puede conectar con contraseña):ssh-copy-id hosting@<ip-servidor>Si
ssh-copy-idno está disponible (por ejemplo en Windows sin WSL), hazlo a mano:ssh hosting@<ip-servidor> \ 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys' \ < ~/.ssh/id_ed25519.pub -
En tu máquina local, confirmar que entras con la clave (sin que pida contraseña):
ssh -o PasswordAuthentication=no hosting@<ip-servidor>Si esto falla, no sigas con el resto de este nivel — revisa la copia de la clave antes de desactivar
PasswordAuthenticationen el servidor o te quedas fuera. -
En el servidor, crear el grupo dedicado y añadir a
hosting:sudo groupadd sshusers sudo usermod -aG sshusers hosting -
Añadir al drop-in ya creado:
sudo vim /etc/ssh/sshd_config.d/00-hardening.confAñadir las líneas:
# Nivel 2 - clave pública AllowGroups sshusers ClientAliveCountMax 2 ClientAliveInterval 300 LoginGraceTime 30 MaxAuthTries 3 PasswordAuthentication no PubkeyAuthentication yesCambia el
PasswordAuthentication yesdel Nivel 1 por elnoanterior — no añadas un segundoPasswordAuthentication: dentro de un mismo fichero gana el primer valor encontrado. -
Validar la sintaxis antes de recargar (evita quedarte fuera si hay un error):
sudo sshd -t -
Recargar (no corta las sesiones activas, a diferencia de
restart):sudo service sshd reload -
Antes de cerrar la sesión actual, abrir una terminal nueva y confirmar que entras con clave, sin contraseña:
ssh hosting@<ip-servidor>
Nivel 3 — Banner legal¶
Opcional. Aplícalo si quieres que quien se conecta lea un aviso legal antes de que se le pida la contraseña — tiene valor disuasorio frente a bots e intrusos y utilidad legal para una eventual reclamación. Sáltalo si no te aporta nada.
-
Reemplazar el fichero por defecto y crear el banner:
sudo rm /etc/issue.net sudo vim /etc/issue.netContenido recomendado (aviso bilingüe, en texto plano):
AVISO LEGAL / LEGAL NOTICE Este servidor es privado. Solo pueden acceder usuarios autorizados. Todo acceso queda monitorizado y registrado. This server is private. Only authorized users may connect. All access is monitored and logged.En muchas imágenes cloud
/etc/issue.netes un symlink a/usr/lib/issue.net— el contenido lo trae la imagen. Elrmlo elimina para crear un fichero propio.Escribe el contenido en texto plano: los códigos de escape que sí soporta el banner (
\dfecha,\ssistema,\ltty) filtran información del servidor y restan efecto al aviso. -
Añadir la directiva al drop-in ya creado:
sudo vim /etc/ssh/sshd_config.d/00-hardening.confAñadir la línea:
# Nivel 3 - banner legal Banner /etc/issue.net -
Validar la sintaxis antes de recargar:
sudo sshd -t -
Recargar (no corta las sesiones activas):
sudo service sshd reload
Banner no es MOTD
El banner se muestra antes de autenticarse, como primer aviso. No debe confundirse con el MOTD, que sale después del login y suele mostrar información del sistema.
Nivel 4 — Desactivar funcionalidades¶
Recomendado. El demonio ofrece funcionalidades que casi nadie usa y que amplían la superficie de ataque: reenvío de X, túneles TCP, sesiones ilimitadas... MaxStartups limita las conexiones simultáneas sin autenticar (anti-fuerza bruta), UseDNS no evita resolver la IP del cliente y LogLevel VERBOSE registra la huella de la clave usada para auditoría.
Omitir líneas concretas si las necesitas — el resto del bloque sigue aportando.
-
Añadir las directivas al drop-in ya creado:
sudo vim /etc/ssh/sshd_config.d/00-hardening.conf# Nivel 4 - desactivar funcionalidades AllowTcpForwarding no PermitEmptyPasswords no StrictModes yes X11Forwarding no # Rendimiento y auditoría LogLevel VERBOSE MaxSessions 10 MaxStartups 10:30:60 UseDNS noAllowTcpForwarding
AllowTcpForwarding nocorta también el port-forwarding y los túneles SOCKS. Si usas SSH para acceder a tu red local o crear túneles, no incluyas esta línea en tu configuración.StrictModes yesyPermitEmptyPasswords noya son el comportamiento por defecto de OpenSSH; se ponen de forma explícita para que la config se autodocumente y no dependa de los valores por defecto de cada versión. -
Validar la sintaxis antes de recargar:
sudo sshd -t -
Recargar (no corta las sesiones activas):
sudo service sshd reload
Por qué no se tocan aquí los algoritmos criptográficos
Limitar KexAlgorithms, Ciphers o MACs depende de la versión de OpenSSH instalada y puede dejarte fuera del servidor. Aplícalo solo tras comprobar con sshd -T que tu cliente sigue siendo compatible — merece un apartado propio.
Referencia: configuración completa¶
Este es el estado del fichero al completar los cuatro niveles. Si has omitido algún nivel, deja fuera también su bloque.
# Nivel 1 - cerrar root
PermitRootLogin no
AllowUsers hosting
# Nivel 2 - clave pública
AllowGroups sshusers
ClientAliveCountMax 2
ClientAliveInterval 300
LoginGraceTime 30
MaxAuthTries 3
PasswordAuthentication no
PubkeyAuthentication yes
# Nivel 3 - banner legal
Banner /etc/issue.net
# Nivel 4 - desactivar funcionalidades
AllowTcpForwarding no
LogLevel VERBOSE
MaxSessions 10
MaxStartups 10:30:60
PermitEmptyPasswords no
StrictModes yes
UseDNS no
X11Forwarding no
Verificar¶
sudo sshd -T | grep passwordauth
sudo sshd -T | grep permitroot
sudo sshd -T | grep banner
sudo sshd -T | grep -E 'allowtcpforwarding|x11forwarding|maxsessions|maxstartups'
Con todos los niveles aplicados debe salir passwordauthentication no, permitrootlogin no, banner /etc/issue.net, allowtcpforwarding no, x11forwarding no, maxsessions 10 y maxstartups 10:30:60. Si has omitido algún nivel, solo faltarán sus líneas. Para comprobar el banner en vivo, conéctate y confirma que el aviso aparece antes de pedir la contraseña:
ssh hosting@<ip-servidor>
Y ahora qué
SSH está protegido. El siguiente paso es configurar el Firewall para abrir solo los puertos necesarios.