Hardening de SSH: 7 cambios que deberías hacer HOY
Hardening de SSH: 7 cambios que deberías hacer HOY
SSH es la puerta de entrada a tu servidor. Si alguien la cruza, tiene control total. Estos 7 cambios cierran las vulnerabilidades más comunes y reducen 99% los intentos de intrusión. Tiempo total: 15 minutos.
—
TL;DR — Lo que vas a tener al final
- Tiempo de setup: 1-2 horas para un server nuevo.
- Requisitos: Acceso root, 2 sesiones SSH abiertas durante cambios.
- Resultado: Login por clave ed25519, fail2ban activo, 99% de ataques bloqueados.
- Troubleshooting: Consola del proveedor, journalctl -u sshd, fail2ban-client status.
Antes de empezar: no te encierres afuera
IMPORTANTE: mantén DOS sesiones SSH abiertas mientras haces los cambios. Si rompes algo, la segunda te salva.
También: ten acceso al proveedor del VPS (Hostinger, DigitalOcean, Hetzner, etc.) por consola web por si pierdes acceso SSH.
—
Cambio 1: Desactiva el login con contraseña
El problema: bots escanean internet 24/7 probando contraseñas débiles (root:123456, admin:admin, etc.).
La solución: solo autenticación con clave pública/privada.
Genera tu clave SSH (si no la tienes)
Acá generamos el par de claves en tu laptop y empujamos la pública al servidor. ed25519 es el algoritmo moderno: claves cortas, firma rápida, y ya no se rompe con hardware actual a diferencia de RSA-1024/2048 con curvas malas.
# En tu laptop
ssh-keygen -t ed25519 -C "[email protected]"
# Copia la clave pública al servidor
ssh-copy-id -i ~/.ssh/id_ed25519.pub usuario@servidor
Si te falla ssh-copy-id por permisos en ~/.ssh del servidor (error Permission denied (publickey)), es porque el directorio quedó con permisos abiertos (típico si lo creaste como root). Corregilo en el servidor con chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys. Y si ed25519 no es aceptado por un servidor muy viejo (OpenSSH < 6.5), recién ahí generá una RSA 4096 como fallback.
Desactiva login con contraseña en el servidor
Con la clave ya copiada y probada, abres el archivo de configuración del demonio SSH. Es el archivo principal; cualquier cambio mal hecho te deja afuera, por eso insistimos con dos sesiones abiertas.
sudo nano /etc/ssh/sshd_config
Cambia o agrega:
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
PubkeyAuthentication yes
Reinicia SSH para que relea sshd_config. systemctl restart corta el demonio y lo vuelve a levantar; las sesiones existentes no se cierran, pero sí se descartan los intentos de re-login hasta que vuelva.
sudo systemctl restart sshd
Verifica que tu clave funciona ANTES de desconectar. En otra terminal:
ssh usuario@servidor
# Debe entrar directamente sin pedir contraseña
Si te sigue pidiendo contraseña, el log en el servidor (sudo tail -f /var/log/auth.log) te dice exactamente por qué: típicamente Permission denied (publickey) por permisos abiertos en ~/.ssh o porque el PubkeyAuthentication yes no quedó activo.
—
Cambio 2: Desactiva el login de root
El usuario root existe en todos los Linux. Los bots lo prueban primero.
# En /etc/ssh/sshd_config
PermitRootLogin no
Mejor práctica: crea un usuario sudoer normal y usa sudo cuando necesites privilegios.
Acá creamos el usuario nuevo, lo agregamos al grupo sudo para que pueda escalar privilegios, y le copiamos tu clave SSH para poder entrar como admin y no como root.
# Crear usuario administrador
sudo adduser admin
sudo usermod -aG sudo admin
# Copia tu clave SSH al nuevo usuario
ssh-copy-id admin@servidor
# Ahora puedes deshabilitar root login
adduser en Ubuntu/Debian es interactivo y te pide contraseña; en scripts o Ansible usa adduser --gecos "" --disabled-password admin para automatizar. Si te equivocas y el usuario quedó sin sudo, agregalo con sudo usermod -aG sudo admin y verifica con getent group sudo.
—
Cambio 3: Cambia el puerto por defecto
SSH escucha en 22. Cambiarlo no da seguridad real (los escaneos a un solo puerto toman 5 minutos), pero reduce 95% los intentos automatizados porque la mayoría de bots solo prueban el 22.
# En /etc/ssh/sshd_config
Port 2222
No uses puertos obvios (222, 2222, 8022). Cualquier puerto entre 1024-65535 que no esté en uso.
Actualiza tu firewall: este bloque reemplaza la regla vieja del puerto 22 por la del nuevo puerto. Si te salteás el delete allow 22/tcp y solo agregás el nuevo, quedan ambos abiertos: sigues expuesto a bots en 22. reload aplica sin droppear conexiones activas, a diferencia de reset.
sudo ufw delete allow 22/tcp
sudo ufw allow 2222/tcp
sudo ufw reload
Actualiza tu config local (~/.ssh/config en tu laptop):
Host mi-servidor
HostName servidor.ejemplo.com
Port 2222
User admin
—
Cambio 4: Instala fail2ban
fail2ban monitorea los logs de SSH y bloquea IPs que fallen muchos logins.
Acá instalamos el paquete, lo habilitamos como servicio persistente (enable crea el symlink systemd para que arranque en cada boot) y lo iniciamos ahora. Sin enable, el servicio funciona hasta el próximo reinicio y queda caído.
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Configuración por defecto razonable. Para customizar:
Acá editas jail.local (no jail.conf — el .conf lo sobrescribe el paquete en updates y se pierden los cambios). Las tres variables más importantes son bantime (cuánto dura el bloqueo), findtime (ventana en la que cuentan los fallos) y maxretry (cuántos fallos dentro de esa ventana disparan el ban).
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
Verifica que está trabajando:
Esta consulta te lista las IPs actualmente baneadas y la cantidad de intentos fallidos que llevaron al ban. Si la lista está vacía y deberías ver bans, revisa que enabled = true en el jail de sshd y que el logpath coincida con tu distro (en RHEL es /var/log/secure, no /var/log/auth.log).
sudo fail2ban-client status sshd
# Muestra IPs baneadas, intentos, etc.
—
Cambio 5: Limita quién puede conectarse
Solo ciertos usuarios o grupos:
# En /etc/ssh/sshd_config
AllowUsers admin deploy app
O por grupo:
AllowGroups ssh-users sudo
Y crea el grupo si no existe:
Acá creamos el grupo ssh-users y metemos al usuario admin. Cualquier usuario que pertenezca al grupo podrá loguearse por SSH; los que no estén, quedan bloqueados aunque tengan clave válida.
sudo groupadd ssh-users
sudo usermod -aG ssh-users admin
Para verificar que el cambio aplica sin reiniciar, abrí una nueva sesión SSH: no necesitas reiniciar sshd porque AllowGroups se evalúa en cada intento de autenticación. Si te auto-baneaste a tú mismo por fail2ban, desbloqueate con sudo fail2ban-client set sshd unbanip TU_IP.
—
Cambio 6: Limita los métodos de autenticación
# En /etc/ssh/sshd_config
AuthenticationMethods publickey
MaxAuthTries 3
LoginGraceTime 30
AuthenticationMethods publickey: solo permite este método. Aunque alguien encuentre una vulnerabilidad en PAM/contraseñas, no aplica.MaxAuthTries 3: cierra la conexión después de 3 intentos fallidos.LoginGraceTime 30: cierra si no se autenticó en 30 segundos (bots lentos quedan fuera).
—
Cambio 7: Activa 2FA (opcional pero recomendado)
Para servidores críticos, agrega un segundo factor con Google Authenticator.
Instala
libpam-google-authenticator provee el módulo PAM que pide el código TOTP después de validar la clave pública. Es opcional porque la mayoría del hardening SSH se sostiene solo con claves, pero para servidores críticos (bases, paneles de admin, jump hosts) es la pieza que cierra el riesgo de clave robada.
sudo apt install libpam-google-authenticator
Si el paquete no aparece en tu distro, en RHEL/Fedora es google-authenticator (sin el prefijo libpam-) y en Alpine hay que habilitar el repo community. Reinstalar no rompe la config existente porque el estado vive en ~/.google_authenticator del usuario, no en el paquete.
Configura para tu usuario
google-authenticator genera el secret TOTP, los códigos de backup y configura rate-limit y ventana de tiempo. Es interactivo y edita ~/.google_authenticator; cada usuario que quiera 2FA debe correrlo por su cuenta.
google-authenticator
Responde y a todas las preguntas. Te genera:
- Un QR para escanear con Google Authenticator / Authy / 1Password.
- Códigos de respaldo (guárdalos en lugar seguro).
Si perdés acceso al autenticador (se te rompió el teléfono), los códigos de backup son la única forma de entrar: sin ellos, terminás usando la consola de emergencia del proveedor. Guardalos en un gestor de passwords o impresos, no solo en el mismo dispositivo.
Activa en PAM
Acá le decimos a PAM que después de autenticar con clave pública pida el código TOTP. La línea va al final de /etc/pam.d/sshd para no romper otros métodos (password, kerberos) si los tienes configurados.
sudo nano /etc/pam.d/sshd
Agrega al final:
auth required pam_google_authenticator.so
Activa en SSH
# En /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
El orden publickey,keyboard-interactive significa: primero clave SSH, luego 2FA.
Reinicia SSH:
systemctl restart sshd recarga la configuración del demonio. Las sesiones establecidas siguen activas, pero cualquier conexión nueva pasa por el flujo de clave + 2FA.
sudo systemctl restart sshd
Prueba ANTES de desconectar:
Acá se ve el flujo completo: clave primero (sin prompt), después el código TOTP. Si nunca aparece el prompt del código, casi siempre es que falta ChallengeResponseAuthentication yes o que el módulo PAM quedó comentado.
ssh admin@servidor -p 2222
# 1. Primero autentica con clave
# 2. Después pide código de 6 dígitos
Si sigues sin ver el prompt del código, corré sudo sshd -T | grep -i authentic para ver qué valores efectivos tiene el demonio. Si todo está bien pero el código es rechazado, verifica que la hora del servidor esté sincronizada con NTP: TOTP depende de que el reloj no difiera más de 30 segundos del cliente.
—
Bonus: cambios opcionales que también ayudan
Desactiva el forwarding X11 si no lo usas
X11Forwarding no
Desactiva TCP forwarding si no lo usas
AllowTcpForwarding no
AllowAgentForwarding no
Banner de aviso legal
Acá creamos el archivo que se muestra al usuario antes del prompt de login. El contenido es texto plano; suele usarse para dar aviso legal en servidores que requieren cadena de custodia o para disuadir scans casuales. No protege por sí mismo, pero completa el cuadro de hardening.
En sshd_config agregá la directiva que apunta al archivo:
Banner /etc/ssh/banner.txt
Y creas el archivo con el texto:
sudo nano /etc/ssh/banner.txt
Pegá adentro (es un bloque de texto plano, no bash):
ALERTA: Acceso restringido. Toda actividad queda registrada.
Para verificar que el banner aparece, reiniciá sshd y conectate desde otra terminal: el texto tiene que salir antes de pedir clave. Si no aparece, revisa que la directiva Banner en sshd_config apunte al path absoluto correcto y que el archivo sea legible por el usuario con el que corre sshd (/usr/sbin/sshd suele correr como root, así que los permisos 644 alcanzan).
Logging más detallado
LogLevel VERBOSE
Así en /var/log/auth.log ves cada intento, exitoso o no, con detalles.
—
Checklist completo
Acá está el sshd_config consolidado con todos los valores de los 7 cambios, listo para copiar y pegar en tu servidor. La idea es tener una referencia única en lugar de saltar entre secciones; igual revisa cada línea contra tu versión actual antes de reemplazar, porque sshd_config viene con directivas comentadas que cambian entre versiones de OpenSSH.
# /etc/ssh/sshd_config recomendado:
Port 2222
# Protocol 2 NO se incluye — esa directiva está deprecada y removida en OpenSSH 7.6+
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 3
PubkeyAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
PasswordAuthentication no
ChallengeResponseAuthentication yes
PermitRootLogin no
UsePAM yes
AllowGroups ssh-users sudo
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
LogLevel VERBOSE
Después de editar:
sshd -t parsea la configuración sin aplicarla: si hay un error de sintaxis, parámetro desconocido o valor inválido, lo reporta en stderr con la línea exacta. Es el seguro de vida antes de reiniciar: si pasás el -t, el restart siguiente no te va a tirar el demonio y dejarte afuera.
sudo sshd -t # Verifica sintaxis
sudo systemctl restart sshd
Si sshd -t devuelve unknown key o bad configuration option, casi siempre es una directiva mal escrita (typo, valor con comillas de más, o una opción removida en tu versión de OpenSSH). Corregilo, volvé a correr -t, y recién entonces hacés el restart. Y siempre con dos sesiones abiertas, como dijimos al principio.
—
Monitoreo: saber si alguien intenta entrar
Ver intentos de login fallidos
Este grep lee el log de autenticación y muestra las últimas 20 líneas con intentos de password fallidos. Es el primer paso para entender si estás bajo fuerza bruta: si ves miles de líneas por día desde IPs que no son tuyas, fail2ban está trabajando o no.
sudo grep "Failed password" /var/log/auth.log | tail -20
Si el log no muestra nada pero sospechás intentos, puede ser que en tu distro el archivo sea /var/log/secure (RHEL/Fedora) o que el nivel de LogLevel no incluya la verbosidad suficiente. En systemd-based puedes usar journalctl -u sshd -n 50 --no-pager como alternativa más portable.
Ver IPs con más intentos
Este one-liner toma las IPs de los intentos fallidos, las cuenta, ordena de mayor a menor y muestra las 10 peores. Es el paso siguiente al tail -20: te dice quién está insistiendo y desde dónde. Útil para decidir si quieres agregar una whitelist o denunciar la IP a tu proveedor.
sudo grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10
El truco del awk '{print $(NF-3)}' asume el formato estándar de auth.log; en journald o si usas syslog con formato distinto, el offset puede ser otro. Si el resultado sale vacío o con cuentas ridículas (siempre 1), ajustá el offset o usa journalctl -u sshd | grep "Failed password" | awk '{print $11}' | sort | uniq -c | sort -rn | head.
Alertas en tiempo real con Uptime Kuma
Configura un check TCP en tu Uptime Kuma apuntando a tu-servidor:2222. Si el puerto deja de responder (porque alguien tiró el servicio), te avisa.
—
¿Y si me equivoco y no puedo entrar?
Consola de emergencia del proveedor:
- Hostinger: hPanel → VPS → Consola
- DigitalOcean: Dashboard → Access → Droplet Console
- Hetzner: Cloud Console → Server → Console
- AWS: EC2 → Connect → Serial Console
Desde ahí editas /etc/ssh/sshd_config de vuelta a valores seguros.
Snapshots: antes de tocar nada, crea un snapshot del VPS. Si algo sale mal, restauras en 5 minutos.
—
¿Cuánto reduce esto los ataques?
Sin hardening (solo clave, root login activo, puerto 22):
- ~500-2000 intentos de login por día de bots automatizados.
Con los 7 cambios aplicados:
- ~5-20 intentos por semana (los pocos bots que escanean puertos no estándar).
- 0 contraseñas aceptadas → no fuerza bruta posible.
- 2FA → incluso si alguien roba tu clave, no entra.
—
Próximos pasos
Si quieres profundizar más:
- SSH certificates (en vez de claves individuales): escala mejor para equipos grandes.
- Teleport: SSH con auditoría centralizada, grabación de sesiones, RBAC.
- Tailscale SSH: SSH sobre Tailscale, no expone el servicio a internet.
- Hardware security keys (YubiKey): 2FA físico, no puedes perderlo.
¿Próximo post? Estaba pensando en 2FA más allá de Google Authenticator: YubiKey, passkeys, WebAuthn o fail2ban avanzado con listas blancas. Déjamelo en comentarios.
Si te sirvió, suscríbete al blog para no perderte los siguientes tutoriales.
Preguntas frecuentes
¿Por qué Hardening de SSH: 7 cambios que deberías hacer HOY es relevante para sysadmins Linux?
Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.
¿Qué necesito para empezar con Hardening de SSH: 7 cambios?
Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.
¿Cuáles son los errores más comunes con Hardening de SSH: 7 cambios?
Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.
¿Cuándo usar Hardening de SSH: 7 cambios y cuándo una alternativa?
Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.
¿Cómo migro desde una configuración previa a Hardening de SSH: 7 cambios?
Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.
Lee también
- Guía Fedora 44: Novedades, Instalación y Upgrade para Sysadmins Linux
- Configura VLANs en Linux con nmcli: Guía paso a paso para sysadmins
- Protege tu SSH: Guía esencial para configurar Fail2ban en Linux
- Guía Definitiva: Configura Kubernetes con kubeadm en Ubuntu para Sysadmins Linux
- Configura Prometheus y Grafana: Guía DevOps para SysAdmins Linux

Pingback: Terminal productividad: 15 atajos de bash que te ahorran
Pingback: Snapshots de LVM: qué son y cómo usarlos en Linux
Pingback: Network bonding y VLANs en Linux sin GUI: nmcli y systemd-networkd
Pingback: Cron y systemd timers en Linux: automatiza todo en 2026 - Soy Linux
Pingback: Administración de usuarios en Linux: comandos esenciales
Pingback: GLAD: la metodología de 4 pasos que arregla el 99% de errores Linux
Pingback: ClamAV en servidor Linux: cuando vale la pena y cuando no - Soy Linux
Pingback: Cómo Sysadmins Linux Pueden Adaptarse a la Nueva Política de Seguridad CJIS del FBI - Soy Linux