Saltar al contenido

Endurecer SSH: la guía que todo servidor Linux necesita el primer día

    Endurecer SSH:

    la guía que todo servidor Linux necesita el primer día

    Linux y sistemas  |  unfantasmaenelsistema.com

    Levantas un servidor nuevo, le pones una IP pública y, en cuestión de minutos, el log de autenticación empieza a llenarse de intentos de acceso por SSH desde medio mundo. No es que alguien te tenga manía: son redes automatizadas que barren internet entero probando `root` / `123456`, `admin` / `admin` y listas de miles de contraseñas filtradas. En el artículo sobre Hydra vimos esa misma técnica desde el lado del atacante, y su objetivo de ejemplo era, precisamente, SSH.

    La buena noticia es que con la configuración por defecto de casi cualquier distribución esos ataques ya fracasan si has puesto una contraseña decente. La mejor noticia es que, con media hora de trabajo el primer día, puedes dejar el servicio en un estado en el que la fuerza bruta deja de ser siquiera una posibilidad. Esta guía recorre esa media hora: de la autenticación por clave a los certificados SSH, pasando por `sshd_config`, el doble factor y los bastiones.

    Qué aprenderás en este artículo

    Por qué la autenticación por clave hace irrelevante la fuerza bruta de contraseñas · Qué tipo de clave usar hoy (ed25519) y cómo protegerla con passphrase y ssh-agent · Las directivas de sshd_config que de verdad importan, con un fichero endurecido y comentado · Por qué cambiar el puerto reduce el ruido en los logs pero no es una medida de seguridad · Doble factor en SSH: TOTP con PAM o llave física FIDO2 con claves sk-ed25519 · Certificados SSH y CA propia para gestionar flotas sin repartir claves públicas a mano · Bastiones o hosts de salto, y fail2ban / sshguard para el ruido residual

    1. Lo primero: claves en lugar de contraseñas

    El cambio con más impacto, y el único imprescindible, es dejar de autenticarte con contraseña y pasar a autenticación por clave pública. En vez de un secreto que viaja (aunque sea cifrado) y que un humano tiene que recordar, usas un par de claves: la privada vive en tu portátil y nunca sale de él; la pública se copia al servidor, en `~/.ssh/authorized_keys`. Al conectar, el servidor lanza un reto que solo se puede firmar con la clave privada correspondiente. Nada reutilizable viaja por la red, y no hay contraseña que adivinar.

    1.1 Qué tipo de clave generar

    TipoRecomendaciónNotas
    ed25519La opción por defecto hoyCurva moderna (Curve25519), claves y firmas cortas, rápida, sin parámetros que elegir mal
    ecdsaEvitarDepende de curvas NIST con constantes de origen discutido; ed25519 la sustituye sin desventajas
    rsa 4096Solo por compatibilidadVálida y segura con 4096 bits, pero más lenta y voluminosa; úsala si el otro extremo es muy antiguo
    dsaProhibidaLimitada a 1024 bits y retirada de OpenSSH desde hace años
    # Generar una clave ed25519 con passphrase y un comentario identificativo
    ssh-keygen -t ed25519 -a 100 -C "ana@portatil-2026"
     
    #  -a 100  ->  rondas de derivacion de la clave privada en disco (mas = mas lento de forzar)
    #  Te pedira una passphrase: PONLA. Es lo que protege la clave si te roban el portatil.
     
    # Copiar la clave publica al servidor (anade a authorized_keys, no lo sobrescribe)
    ssh-copy-id -i ~/.ssh/id_ed25519.pub ana@198.51.100.10

    1.2 La passphrase no es una molestia: usa ssh-agent

    Una clave privada sin passphrase es un fichero que, copiado, da acceso directo. Con passphrase, quien la robe todavía tiene que romperla offline. Para no teclearla en cada conexión está el ssh-agent: la desbloqueas una vez por sesión y el agente la mantiene en memoria. En el portátil, `ssh-add ~/.ssh/id_ed25519` (o la integración del llavero del sistema) es todo lo que necesitas. Lo que no debes hacer es habilitar `ForwardAgent` a la ligera hacia servidores en los que no confías del todo: reenviar el agente permite a ese host usar tus claves mientras estás conectado.

    2. sshd_config: las directivas que importan

    La configuración del servidor está en `/etc/ssh/sshd_config` (y, en distribuciones modernas, en fragmentos dentro de `/etc/ssh/sshd_config.d/`). Tras cada cambio: `sshd -t` para validar la sintaxis y `systemctl reload ssh`. Deja siempre una segunda sesión abierta mientras pruebas, para no quedarte fuera si te equivocas.

    DirectivaValor recomendadoPor qué
    PermitRootLoginnoFuerza a entrar con un usuario normal y escalar con sudo: hay trazabilidad y root no es un objetivo directo
    PasswordAuthenticationnoDesactiva del todo el vector de fuerza bruta; solo se aceptan claves
    KbdInteractiveAuthenticationnoCierra la otra vía por la que se cuelan contraseñas (antes ChallengeResponseAuthentication)
    PubkeyAuthenticationyesEl método que queremos como único
    PermitEmptyPasswordsnoObvio, pero conviene dejarlo explícito
    AllowUsers / AllowGroupslista blancaSolo las cuentas nombradas pueden entrar por SSH, aunque existan otras en el sistema
    MaxAuthTries3Corta la conexión tras 3 intentos fallidos en la misma sesión
    LoginGraceTime20Segundos para completar el login antes de cerrar; reduce conexiones a medias colgando
    MaxSessions / MaxStartupsajustadoLimita sesiones por conexión y conexiones sin autenticar en paralelo
    X11ForwardingnoSi no reenvías interfaz gráfica, es superficie de ataque gratis
    AllowTcpForwardingno salvo que se useEvita que una cuenta comprometida use el servidor como pivote de red

    Cambiar el puerto: menos ruido, no más seguridad

    Mover SSH del 22 a otro puerto hace que los escaneos masivos automatizados no te encuentren, y el log de autenticación queda mucho más limpio · Pero es seguridad por oscuridad: un escaneo dirigido encuentra el puerto en segundos, y no aporta nada frente a un atacante que ya te tiene en el punto de mira · Hazlo si quieres logs legibles, pero nunca como sustituto de desactivar las contraseñas · Si lo cambias por debajo de 1024, recuerda que en algunos sistemas hará falta ajustar SELinux (semanage port) — lo vimos en el artículo de SELinux y AppArmor

    3. Un sshd_config endurecido, comentado

    # /etc/ssh/sshd_config.d/00-hardening.conf
     
    # --- Acceso ---
    PermitRootLogin no
    AllowGroups ssh-users            # solo miembros de este grupo entran por SSH
     
    # --- Metodos de autenticacion ---
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    PubkeyAuthentication yes
    PermitEmptyPasswords no
    AuthenticationMethods publickey  # (ver seccion de 2FA para 'publickey,keyboard-interactive')
     
    # --- Limites de la sesion de login ---
    MaxAuthTries 3
    LoginGraceTime 20
    MaxStartups 10:30:60
     
    # --- Reduccion de superficie ---
    X11Forwarding no
    AllowAgentForwarding no
    AllowTcpForwarding no
    PermitTunnel no
     
    # --- Criptografia moderna (OpenSSH reciente ya trae buenos valores por defecto) ---
    KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
    Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
    MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
     
    # --- Registro ---
    LogLevel VERBOSE                 # registra la huella de la clave usada en cada login

    Ese `LogLevel VERBOSE` es útil de verdad: deja en el log la huella (fingerprint) de la clave con la que ha entrado cada usuario, lo que permite auditar después qué clave concreta se usó y revocarla si hace falta.

    4. Políticas por grupo con bloques Match

    Los bloques `Match` aplican reglas distintas según el usuario, el grupo o la IP de origen. Sirven para relajar o endurecer la política sin abrirla para todos. El bloque acaba donde empieza el siguiente `Match` o el final del fichero, y sus directivas van indentadas por claridad.

    # Las cuentas de servicio solo pueden abrir un tunel SFTP, sin shell
    Match Group sftp-only
        ChrootDirectory /srv/sftp/%u
        ForceCommand internal-sftp
        AllowTcpForwarding no
        X11Forwarding no
     
    # Desde la red de administracion interna se permite reenvio de puertos
    Match Address 10.20.0.0/24
        AllowTcpForwarding yes
     
    # Una cuenta de despliegue solo ejecuta el script de deploy, nada mas
    Match User deploy
        ForceCommand /usr/local/bin/deploy-wrapper
        PermitTTY no

    5. Doble factor en SSH

    Con contraseñas desactivadas, el segundo factor protege el caso en que te roben la clave privada. Hay dos enfoques buenos.

    5.1 TOTP con PAM (el código de 6 dígitos)

    El módulo `pam_google_authenticator` (funciona con cualquier app TOTP, no solo la de Google) añade un código temporal tras la clave. Se combina con `AuthenticationMethods` para exigir clave Y código:

    # En sshd_config:
    AuthenticationMethods publickey,keyboard-interactive
    KbdInteractiveAuthentication yes
     
    # En /etc/pam.d/sshd (arriba), y quitando 'nullok' cuando todos lo tengan configurado:
    auth required pam_google_authenticator.so
     
    # Cada usuario ejecuta una vez, guarda los codigos de recuperacion y escanea el QR:
    google-authenticator

    5.2 Llave física FIDO2 (claves sk-ed25519)

    Desde OpenSSH 8.2, una clave puede vivir dentro de una llave de seguridad (YubiKey, Nitrokey, SoloKey). La clave privada no se puede exfiltrar del token, y cada uso requiere tocar el dispositivo físicamente. Es la opción más resistente al robo de credenciales y encaja con lo que contamos en el artículo de passkeys y FIDO2.

    # Generar una clave ligada a la llave fisica (pide tocar el dispositivo)
    ssh-keygen -t ed25519-sk -O resident -O verify-required -C "ana@yubikey"
     
    #  ed25519-sk        -> la parte privada vive en el token
    #  -O resident       -> se puede recuperar desde la propia llave en otro equipo
    #  -O verify-required -> exige PIN ademas del toque
     
    # La clave publica resultante se copia al servidor como cualquier otra

    6. Certificados SSH: dejar de repartir claves a mano

    Con tres servidores, copiar la clave pública de cada persona a cada `authorized_keys` es llevadero. Con trescientos, es inmanejable: cuando alguien se va, hay que peinar la flota entera. Los certificados SSH resuelven esto con el mismo modelo que una PKI de TLS (que vimos en el artículo de TLS y PKI), pero mucho más simple.

    • Creas una CA de usuarios: un par de claves cuyo único trabajo es firmar
    • Cada servidor confía en esa CA con una sola línea: `TrustedUserCAKeys /etc/ssh/users_ca.pub`
    • En vez de instalar la clave de cada persona, le firmas un certificado con validez corta (horas o pocos días), que indica qué identidades (`principals`) puede usar
    • Al caducar, el acceso desaparece solo. No hay que tocar ningún servidor para revocar a alguien (y para revocar antes de tiempo existe `RevokedKeys`)
    # Una sola vez: generar la CA de usuarios (guardala MUY a buen recaudo)
    ssh-keygen -t ed25519 -f users_ca -C "CA de usuarios - empresa"
     
    # En cada servidor:  /etc/ssh/sshd_config
    TrustedUserCAKeys /etc/ssh/users_ca.pub
     
    # Firmar un certificado para 'ana', valido 8 horas, con el principal 'devs'
    ssh-keygen -s users_ca -I "ana-2026-08-27" -n devs -V +8h ana_id_ed25519.pub
     
    # En el servidor, el usuario del SO se enlaza a los principals con AuthorizedPrincipalsFile

    El mismo mecanismo sirve para los certificados de host: firmando la clave del servidor con una CA de hosts, los clientes dejan de ver el aviso de «authenticity of host can’t be established» y quedan protegidos frente a suplantación del servidor.

    7. Bastión: una sola puerta

    En lugar de exponer el puerto SSH de cada máquina a internet, se expone una sola: el bastión o host de salto. El resto de servidores solo aceptan SSH desde la IP interna del bastión. Toda sesión pasa por ahí, lo que concentra el registro, el 2FA y el endurecimiento en un único punto que vigilar.

    # ~/.ssh/config en el portatil: saltar transparentemente por el bastion
    Host bastion
        HostName bastion.midominio.com
        User ana
     
    Host 10.0.*.*
        User ana
        ProxyJump bastion          # equivale a  ssh -J bastion  ...
     
    # Con esto, 'ssh 10.0.3.15' abre la sesion final pasando por el bastion,
    # sin dejar tu clave privada en el (no uses agent forwarding: usa ProxyJump)

    El bastión no debe guardar secretos

    Usa ProxyJump (-J), no ForwardAgent: así tu clave privada nunca toca el bastión · El bastión solo reenvía la conexión cifrada; la autenticación final ocurre extremo a extremo · Registra las sesiones del bastión y revísalas: es el punto con más valor para un atacante

    8. fail2ban y sshguard: el ruido residual

    Si has desactivado las contraseñas, la fuerza bruta ya no puede entrar. Aun así, esos miles de intentos fallidos consumen recursos y ensucian los logs. fail2ban y sshguard leen el log de autenticación y banean temporalmente (vía iptables/nftables — ver el artículo de iptables y nftables) las IPs que fallan repetidamente.

    # /etc/fail2ban/jail.d/sshd.local
    [sshd]
    enabled  = true
    backend  = systemd
    maxretry = 3          # fallos antes del baneo
    findtime = 10m        # ventana en la que se cuentan
    bantime  = 1h         # duracion del baneo (con 'bantime.increment = true' crece al reincidir)

    Son un complemento, no la base

    fail2ban reduce ruido y frena escaneos, pero tu seguridad real es que no hay contraseña que forzar · No dependas de él como única defensa: un atacante con muchas IPs (una botnet) esquiva el baneo por IP · Ojo con auto-banearte: añade tu red de administración a ‘ignoreip’

    9. Checklist del primer día

    PasoEstado ideal
    Clave ed25519 con passphrase, copiada al servidorHecho antes de tocar sshd_config
    PasswordAuthentication no + KbdInteractiveAuthentication noCon una segunda sesión abierta al aplicar
    PermitRootLogin noAcceso solo con usuario normal + sudo
    AllowGroups / AllowUsers con lista blancaSolo las cuentas que de verdad usan SSH
    MaxAuthTries 3, LoginGraceTime 20Aplicado
    Reenvíos (X11, Agent, TCP) desactivados salvo necesidadAplicado
    2FA (TOTP o FIDO2) al menos para cuentas con sudoConfigurado y probado
    fail2ban/sshguard con la red de admin en la lista de exclusiónActivo
    Firewall: solo el puerto SSH (y lo imprescindible) abiertoAplicado
    Backup de las claves de servidor y, si aplica, de la CAGuardado fuera de la máquina

    Antes de aplicar cambios en un servidor remoto

    Mantén SIEMPRE una segunda sesión SSH abierta mientras editas sshd_config: si te bloqueas, la usas para revertir · Valida con `sshd -t` antes de recargar el servicio · Si el servidor es un VPS, ten a mano la consola web del proveedor (acceso fuera de banda) por si todo lo demás falla · Prueba el login por clave en una sesión nueva ANTES de desactivar las contraseñas, no después

    Conclusión

    Endurecer SSH no es una lista interminable de ajustes esotéricos. El 90 % del beneficio está en dos decisiones que se toman en cinco minutos: autenticarse con una clave ed25519 protegida por passphrase, y poner `PasswordAuthentication no`. Con eso, la fuerza bruta que llena los logs de cualquier servidor recién nacido deja de ser una amenaza y pasa a ser ruido de fondo.

    El resto —2FA con FIDO2, certificados con CA propia, bastiones, bloques Match— son capas que se añaden según crece la infraestructura y el número de personas con acceso. Ninguna sustituye a la base; todas la refuerzan. Y el hábito más barato y más valioso es el más simple: dejar una segunda sesión abierta cada vez que tocas la configuración.

    Referencias

    man sshd_config — La referencia canónica de cada directiva, en tu propia máquina · ssh-audit (github.com/jtesta/ssh-audit) — Audita la configuración de cifrado de un servidor SSH · infosec.mozilla.org/guidelines/openssh — Guía de configuración de OpenSSH de Mozilla · Nuestro artículo «Hydra: fuerza bruta de servicios de red», para entender el ataque que esto neutraliza · Nuestros artículos de «iptables y nftables» y «passkeys y FIDO2», para las piezas que se apoyan aquí

    unfantasmaenelsistema.com | Seguridad, sistemas y todo lo que no debería funcionar (pero funciona)

    Etiquetas:
    Resumen de privacidad

    Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.