InfraFenceInfraFence

Live Security Operations

Empezar

Cómo frenar los ataques de fuerza bruta SSH

Última actualización:

Cualquier servidor con el puerto 22 abierto a internet recibe intentos de contraseña a los pocos minutos de arrancar, todo el día, desde miles de direcciones. Casi siempre son bots que prueban root, admin y nombres comunes con listas de contraseñas filtradas. Solo entran donde hay una contraseña débil, pero llenan los logs, consumen CPU y basta una cuenta débil.

La solución va por capas: hacer inútiles las contraseñas (solo claves), sacar a root de la ecuación y después banear y limitar lo que quede. Los comandos de abajo son para Debian y Ubuntu y para la familia RHEL (RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora).

Comprobar si te están atacando

El demonio SSH registra cada intento fallido. Cuenta las últimas 24 horas y mira qué direcciones envían más:

Cualquier distribución con systemd
sudo journalctl --since "24 hours ago" | grep -cE "Failed password|Invalid user"
Cualquier distribución con systemd
sudo journalctl --since "24 hours ago" \
  | grep -E "Failed password|Invalid user" \
  | grep -oE "from [0-9a-fA-F.:]+ port" | awk '{print $2}' \
  | sort | uniq -c | sort -rn | head -20

Cientos o miles de líneas al día son lo normal para un servidor con IP pública. Si el journal no se conserva entre reinicios, las mismas líneas están en /var/log/auth.log en Debian y Ubuntu y en /var/log/secure en la familia RHEL.

Configurar las claves SSH y probarlas primero

Todo lo que sigue desactiva las contraseñas, así que primero necesitas una clave que funcione. En tu ordenador, crea una clave, cópiala al servidor y entra con ella. Sustituye deploy por tu usuario y SERVER_IP por la dirección del servidor.

En tu ordenador
ssh-keygen -t ed25519 -C "laptop"
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@SERVER_IP
ssh -i ~/.ssh/id_ed25519 deploy@SERVER_IP

El último comando debe dejarte entrar sin pedir la contraseña de la cuenta (puede pedir la frase de paso de la clave, que se queda en tu ordenador).

Atención: A partir de aquí, mantén abierta una sesión SSH con el servidor y haz cada prueba desde un segundo terminal. Si un cambio te deja fuera, la sesión abierta es la forma de deshacerlo. Antes de empezar, localiza también la consola web de tu proveedor (VNC o serie): funciona sin SSH.

Crear un usuario sudo antes de desactivar root

Si hoy entras como root, crea primero un usuario normal con permisos de sudo y dale las mismas claves.

Debian / Ubuntu
sudo adduser deploy
sudo usermod -aG sudo deploy
RHEL, Rocky Linux, AlmaLinux, Oracle Linux, Fedora
sudo useradd -m deploy
sudo passwd deploy
sudo usermod -aG wheel deploy
Copiar las claves de root al nuevo usuario
sudo mkdir -p /home/deploy/.ssh
sudo cp /root/.ssh/authorized_keys /home/deploy/.ssh/
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
Solo familia RHEL, para las etiquetas de SELinux
sudo restorecon -R /home/deploy/.ssh

Desde el segundo terminal, entra con el nuevo usuario y ejecuta sudo -v: debe aceptar la contraseña del usuario. Solo entonces sigue adelante.

Desactivar el acceso con contraseña y el de root

Las versiones actuales de Debian, Ubuntu, la familia RHEL 9 y Fedora leen archivos adicionales de /etc/ssh/sshd_config.d/. Comprueba que la tuya lo hace:

Cualquier distribución
grep -i '^Include' /etc/ssh/sshd_config

Si muestra Include /etc/ssh/sshd_config.d/*.conf, escribe los ajustes en un archivo propio. SSH se queda con el primer valor que lee para cada ajuste, y los archivos se leen por orden de nombre, así que el prefijo 00- gana a archivos como 50-cloud-init.conf, que en algunas imágenes cloud vuelve a activar las contraseñas.

Cualquier distribución
sudo tee /etc/ssh/sshd_config.d/00-hardening.conf > /dev/null <<'EOF'
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
MaxAuthTries 4
LoginGraceTime 30
EOF

Si no hay línea Include (RHEL 8 y sus derivadas), pon las mismas seis líneas directamente en /etc/ssh/sshd_config, en lugar de las que ya existen. Después prueba la configuración y mira los valores que SSH va a usar de verdad:

Cualquier distribución
sudo sshd -t && echo "config OK"
sudo sshd -T | grep -Ei '^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin|maxauthtries) '

Cada línea debe mostrar el valor que has puesto. Si root todavía tiene que entrar con una clave, por ejemplo para una copia de seguridad, usa PermitRootLogin prohibit-password en lugar de no. Recarga SSH: las sesiones abiertas siguen conectadas.

Debian / Ubuntu
sudo systemctl reload ssh
RHEL, Rocky Linux, AlmaLinux, Oracle Linux, Fedora
sudo systemctl reload sshd

Atención: No cierres todavía la sesión abierta. Desde el segundo terminal, entra con tu clave y comprueba que se rechaza una contraseña: el comando de abajo debe terminar con Permission denied (publickey).

En tu ordenador
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password deploy@SERVER_IP

Cambiar el puerto SSH, sabiendo lo que no resuelve

Sacar SSH del puerto 22 reduce el ruido en los logs, porque la mayoría de los bots solo prueban el 22. No hace SSH más seguro: los escáneres de puertos encuentran el nuevo puerto en pocas horas, y un atacante con un objetivo concreto, enseguida. Con acceso solo por clave, el puerto importa poco. Hazlo para tener logs más limpios, no por seguridad, y solo después de los pasos anteriores.

Elige un puerto (aquí el 2222), ábrelo en el firewall y, en la familia RHEL, dile a SELinux que SSH puede usarlo:

Debian / Ubuntu con ufw
sudo ufw allow 2222/tcp
Familia RHEL con firewalld y SELinux
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo dnf install -y policycoreutils-python-utils
sudo semanage port -a -t ssh_port_t -p tcp 2222

Abre el puerto también en el firewall cloud o el security group de tu proveedor. Después configura el puerto y reinicia SSH:

Cualquier distribución
echo "Port 2222" | sudo tee /etc/ssh/sshd_config.d/01-port.conf
sudo sshd -t && echo "config OK"

En Ubuntu 24.04 y posteriores, SSH puede arrancarlo ssh.socket: si el primer comando de abajo muestra active, ejecuta las cuatro líneas; si no, solo la última.

Debian / Ubuntu
systemctl is-active ssh.socket
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
sudo systemctl restart ssh
RHEL, Rocky Linux, AlmaLinux, Oracle Linux, Fedora
sudo systemctl restart sshd
En tu ordenador
ssh -p 2222 deploy@SERVER_IP

Atención: Solo cuando el acceso por el nuevo puerto funcione, quita del firewall la regla del puerto 22. fail2ban, sshguard y los límites de abajo también tienen que usar el nuevo puerto.

Banear a quien insiste con fail2ban o sshguard

Con las contraseñas desactivadas los intentos no pueden tener éxito, pero banear las direcciones que siguen probando reduce la carga y el ruido. fail2ban lee el log y banea una dirección en el firewall tras un número de fallos.

Debian / Ubuntu
sudo apt update
sudo apt install -y fail2ban python3-systemd
Rocky Linux, AlmaLinux, CentOS Stream (EPEL)
sudo dnf install -y epel-release
sudo dnf install -y fail2ban

En RHEL y Oracle Linux, activa EPEL como indica su documentación y después instala fail2ban. En Fedora está en los repositorios principales. Amazon Linux 2023 no puede usar EPEL: usa en su lugar el límite del firewall que se explica más abajo.

Los ajustes van en jail.local, nunca en jail.conf, que las actualizaciones sobrescriben. Comprueba que no tienes ya uno y después créalo. Sustituye YOUR_OFFICE_IP por las direcciones fijas desde las que trabajas, o bórralo, y pon en port tu puerto SSH si lo has cambiado.

Cualquier distribución
ls -l /etc/fail2ban/jail.local
Cualquier distribución
sudo tee /etc/fail2ban/jail.local > /dev/null <<'EOF'
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 YOUR_OFFICE_IP
bantime = 1h
bantime.increment = true
findtime = 10m
maxretry = 5

[sshd]
enabled = true
backend = systemd
port = ssh
EOF

Así se banea una dirección durante una hora tras 5 fallos en 10 minutos, y durante más tiempo cada vez que vuelve. backend = systemd lee el journal, lo que funciona en Debian 12 y posteriores, donde /var/log/auth.log puede no existir.

Cualquier distribución
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

Durante un ataque, Total failed y Currently banned crecen en pocos minutos. Si Total failed se queda en 0 mientras el journal muestra accesos fallidos, la jail no está leyendo el log correcto. Para levantar un baneo:

Cualquier distribución
sudo fail2ban-client set sshd unbanip 203.0.113.7

sshguard, la alternativa más ligera

sshguard hace el mismo trabajo con menos configuración y está empaquetado para Debian y Ubuntu (y en EPEL). Pon primero tus propias direcciones en su lista blanca y después mira sudo journalctl -u sshguard -f durante un ataque para ver los bloqueos. Usa uno de los dos, no ambos.

Debian / Ubuntu
sudo apt install -y sshguard
echo "YOUR_OFFICE_IP" | sudo tee -a /etc/sshguard/whitelist
sudo systemctl restart sshguard

Limitar o restringir SSH en el firewall

En Ubuntu y Debian con ufw, limit rechaza una dirección que abre 6 o más conexiones en 30 segundos. Sustituye a una regla allow 22/tcp simple. Si sudo ufw status muestra también una regla OpenSSH, bórrala cuando el límite esté activo (sudo ufw delete allow OpenSSH): se aplicaría antes que el límite.

Debian / Ubuntu con ufw
sudo ufw limit 22/tcp
sudo ufw enable
sudo ufw status verbose

Atención: Si ufw todavía no está activo, ufw enable empieza a bloquear todos los puertos que no tengan una regla. Permite los servicios que ofrece el servidor (por ejemplo sudo ufw allow 80,443/tcp) antes de activarlo, y mantén la sesión abierta.

Las rich rules de firewalld tienen un limit, pero cuenta todos los orígenes juntos, así que un ataque puede agotar el cupo y dejarte fuera también a ti. En la familia RHEL, usa fail2ban para los límites por dirección.

Permitir SSH solo desde tus direcciones

El paso más fuerte, si trabajas desde direcciones fijas o una VPN: nadie más puede siquiera llegar a SSH.

Debian / Ubuntu con ufw
sudo ufw allow from YOUR_OFFICE_IP to any port 22 proto tcp
sudo ufw delete limit 22/tcp
sudo ufw status numbered
Familia RHEL con firewalld
sudo firewall-cmd --permanent --zone=trusted --add-source=YOUR_OFFICE_IP
sudo firewall-cmd --reload
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
sudo firewall-cmd --reload

Atención: Esto deja fuera a cualquier otra dirección, también a la tuya si cambia. Prueba un nuevo acceso tras la primera recarga y antes de quitar la regla SSH pública, y ten la consola web del proveedor como vía de regreso.

Cómo gestiona InfraFence la fuerza bruta SSH

El agente de InfraFence lee el log de SSH a medida que se escribe y banea una dirección en el firewall cuando alcanza el umbral de fallos dentro de la ventana de tiempo; ambos valores se pueden cambiar en el panel.

  • 15 patrones de detección: contraseñas fallidas, usuarios inexistentes, fallos de PAM, escaneos antes de la autenticación, errores de protocolo y conexiones cortadas durante el intercambio de claves.
  • Baneos crecientes: 24 horas, 7 días, 30 días y después permanente, o la duración que elijas. Opcional: una dirección baneada en un servidor se banea en todos tus servidores.
  • La misma protección para los accesos a Postfix, Dovecot, MySQL, PostgreSQL, MongoDB, Pure-FTPd, ProFTPD y vsftpd.
  • Las comprobaciones de hardening señalan el acceso de root, el acceso con contraseña, el puerto por defecto y un MaxAuthTries alto. MaxAuthTries se corrige con un clic, en un archivo propio (/etc/ssh/sshd_config.d/00-infrafence-hardening.conf) que se prueba antes de recargar SSH. El acceso de root, las contraseñas y el puerto pueden dejarte fuera, así que en su lugar se explican paso a paso, como en esta guía.
  • Cada sesión SSH se sigue desde el inicio hasta el cierre y recibe una puntuación de riesgo, así sabes quién estaba conectado cuando saltó otra cosa.

Funciona junto a fail2ban: el agente lo reconoce y mantiene sus propias reglas en cadenas INFRAFENCE separadas, que vuelve a poner después de una recarga del firewall.

Preguntas

¿Basta con cambiar el puerto SSH para frenar la fuerza bruta?
No. Reduce el número de intentos en los logs, porque la mayoría de los bots solo prueban el puerto 22, pero los escáneres encuentran el nuevo puerto enseguida. Lo que hace inútiles los intentos es el acceso solo con clave.
¿Uso fail2ban o sshguard?
Para SSH sirven los dos. fail2ban tiene más filtros para otros servicios y más opciones; sshguard necesita menos configuración. Usa uno, no los dos, para que no se peleen por las mismas direcciones.
He desactivado las contraseñas y sigo viendo accesos fallidos. ¿Pasa algo?
No. Los bots siguen probando porque no saben que las contraseñas están desactivadas. Los intentos fallan antes de que se compruebe ninguna contraseña. Los baneos y los límites reducen el ruido.

Pruébalo gratis en 1 servidor

Starter es gratuito para un servidor, con protección completa y bloqueo automático. Sin tarjeta de crédito.

Instala el agente como root, con tu token en lugar de <TOKEN>:

curl -fsSL https://github.com/infrafence/infrafence-agent/releases/latest/download/install.sh | sudo bash -s -- --token <TOKEN>

El token se crea en el panel, en Servidores › Añadir servidor.