Un vistazo en 30 segundos
- Casi todo lo que piden las listas de hardening de SSH ya viene hecho en Debian y Ubuntu actuales. El trabajo real es cerrar los huecos que quedan y comprobar, con
sshd -T, lo que de verdad está en vigor.
- No fijes
Ciphers, MACs ni KexAlgorithms. Una lista copiada de un blog de 2016 congela el servidor y lo deja fuera de mejoras como el intercambio de claves poscuántico, que es el predeterminado desde OpenSSH 10.0.
- El nombre del fichero importa. En
sshd_config.d/ gana el primer valor que se lee. Un 60-hardening.conf pierde contra el 50-cloud-init.conf que deja PasswordAuthentication yes. Lo hemos probado en las cuatro distribuciones.
- Parches automáticos sin reinicio son medio control. Ni Debian ni Ubuntu reinician por defecto. Y en Debian, además,
needrestart no reinicia los servicios cuando actualiza unattended-upgrades.
- fail2ban para SSH depende de la versión: Debian 13 y Ubuntu 26.04 traen
PerSourcePenalties de serie y apenas lo necesitan. En Debian 12, el fail2ban del repositorio ni siquiera arranca sin retocarlo.
- Esta línea base es para máquinas virtuales y servidores sueltos. En un nodo de Proxmox, varias de estas medidas rompen el clúster.
El punto de partida es un artículo de Daniel Valev en DevOps.dev, con una tesis que compartimos: el hardening que fija valores acaba pudriéndose. Valev cuenta que heredó una flota de servidores Ubuntu con una línea Ciphers en sshd_config copiada de un blog de hacia 2016, y que esa línea llevaba años congelando la criptografía de SSH mientras OpenSSH mejoraba sus valores por defecto por su cuenta.
Nos pasa lo mismo cuando recibimos máquinas de otro proveedor o de un cliente que migra. Una parte sorprendente del trabajo consiste en quitar cosas que alguien pegó hace años. Por eso hemos rehecho su línea base para el entorno que más vemos, Debian y Ubuntu, y la hemos comprobado paquete a paquete en las cuatro versiones vigentes: Debian 12 y 13 y Ubuntu 24.04 y 26.04. Por el camino han salido varias cosas que el original no cuenta o que ya han cambiado.
Si lo que tienes delante es una máquina heredada y no sabes qué hace, empieza antes por auditarla. Esto es lo que viene después: fijar un suelo razonable.
Lo que trae cada distribución de serie
Antes de cambiar nada conviene saber de dónde partes. Esto es lo que instala cada versión, comprobado con las imágenes oficiales a 30 de septiembre de 2026:
| Debian 12 | Debian 13 | Ubuntu 24.04 | Ubuntu 26.04 |
|---|
| OpenSSH | 9.2 | 10.0 | 9.6 | 10.2 |
PerSourcePenalties | No | Sí, activo | No | Sí, activo |
| Intercambio poscuántico por defecto | No | Sí (mlkem768x25519) | No | Sí (mlkem768x25519) |
| SSH arranca por | servicio | servicio | socket | socket |
PermitRootLogin efectivo | solo con clave | solo con clave | solo con clave | solo con clave |
PasswordAuthentication efectivo | sí | sí | sí | sí |
ufw | en repositorio | en repositorio | preinstalado | preinstalado |
| fail2ban del repositorio | 1.0.2 (roto de serie) | 1.1.0 | 1.0.2 | 1.1.0 |
| Lynis del repositorio | 3.0.8 | 3.1.4 | 3.0.9 | 3.1.6 |
Tres cosas saltan a la vista. La primera, que el acceso de root con contraseña ya está cerrado en todas: PermitRootLogin vale prohibit-password por defecto en OpenSSH desde la 7.0, publicada en agosto de 2015. La segunda, que la contraseña para el resto de usuarios sigue abierta en todas, y eso sí hay que cerrarlo. La tercera, que entre Debian 12 y Debian 13 hay un salto grande: con la 13 llega OpenSSH 10, y con él la protección contra fuerza bruta integrada y el intercambio de claves poscuántico.
Paso 0: que sea imposible quedarte fuera
Abre una segunda sesión SSH antes de tocar nada y déjala conectada. Si un cambio rompe la autenticación, esa sesión sigue viva y puedes deshacerlo. Es obvio, hasta la noche en que te lo saltas. Si la máquina es virtual, ten además a mano la consola del hipervisor; en nuestras plataformas es lo primero que comprobamos antes de tocar el acceso de una VM.
La segunda precaución: comprueba que tu clave pública ya funciona para el usuario administrador y que ese usuario está en el grupo sudo. El script de más abajo se niega a seguir si no es así.
SSH: claves sí, root no, y los cifrados quietos
Hay tres ajustes que importan:
PasswordAuthentication no y KbdInteractiveAuthentication no. Desactivar solo el primero deja un camino por PAM que puede seguir pidiendo contraseña. En Debian y Ubuntu el segundo ya viene a no, pero conviene fijarlo por si alguien lo cambió.
PermitRootLogin no. Como root con contraseña ya está cerrado, pasar a no es un cambio real y adicional: prohíbe también root con clave. En un servidor suelto es lo sensato. En un nodo de Proxmox, no; lo explicamos al final.
AuthenticationMethods publickey, que deja por escrito que solo vale la clave, y MaxAuthTries 3, que corta antes a quien va probando claves.
El orden de los ficheros decide
Aquí está la trampa que menos se cuenta. Debian y Ubuntu incluyen al principio de sshd_config la línea Include /etc/ssh/sshd_config.d/*.conf, así que los ficheros de esa carpeta se leen antes que el resto y en orden alfabético. Y en sshd, para la mayoría de opciones, gana el primer valor que encuentra, no el último.
¿Por qué importa? Porque las imágenes que se despliegan con cloud-init, que son casi todas las de un proveedor y las que se preparan con plantillas en Proxmox, suelen dejar un 50-cloud-init.conf con PasswordAuthentication yes. Lo hemos probado en las cuatro distribuciones:
| Tu fichero | Contra 50-cloud-init.conf con yes | Resultado en sshd -T |
|---|
60-hardening.conf con no | se lee después | passwordauthentication yes |
10-baseline.conf con no | se lee antes | passwordauthentication no |
Si pones tu ajuste en un fichero que ordena detrás, sshd -t no se queja, el servicio arranca y la contraseña sigue abierta. Por eso la única comprobación que vale es sshd -T, que muestra la configuración efectiva, y no leer el fichero que acabas de escribir.
No fijes los algoritmos
La tentación es añadir líneas Ciphers, MACs y KexAlgorithms con una lista «segura». No lo hagas. La oferta por defecto de Debian y Ubuntu ya no incluye CBC, 3DES ni intercambios con SHA-1, y la propia documentación de Canonical dice que la selección por defecto «debería bastar para la mayoría de escenarios» y avisa de que restringir algoritmos en el servidor puede dejarte fuera de un sistema remoto.
Fijarlos tiene además un coste que no se ve: dejas de recibir mejoras. OpenSSH 10.0 (9 de abril de 2025) hizo predeterminado el intercambio híbrido poscuántico mlkem768x25519-sha256, y Debian 13 y Ubuntu 26.04 ya lo ofrecen primero. Una línea KexAlgorithms copiada de un blog antiguo lo desactiva sin avisar. La misma versión eliminó del todo el soporte de DSA, así que los consejos de 2026 que aún piden desactivar ssh-dss están pidiendo quitar algo que ya no existe.
Si una norma de cumplimiento te obliga de verdad a quitar un algoritmo, usa la forma sustractiva, con un guion delante: Ciphers -aes128-ctr,aes192-ctr. Así restas de la lista por defecto actual en lugar de sustituirla por una congelada. Lo hemos comprobado: la lista resultante mantiene el resto de algoritmos y sigue recibiendo los nuevos cuando actualices.
La activación por socket de Ubuntu
Desde Ubuntu 22.10, sshd arranca por activación de socket: quien escucha el puerto 22 es ssh.socket, y systemd lanza sshd cuando llega una conexión. En Debian también existe esa opción, pero viene desactivada y el servidor arranca como servicio normal.
El borrador del que partimos dice que en Ubuntu Port y ListenAddress de sshd_config se ignoran. Eso fue cierto en la 22.10 y la 23.04. Desde la 24.04, un generador de systemd vuelve a leerlos de sshd_config, pero el cambio no se aplica hasta que ejecutas systemctl daemon-reload y reinicias ssh.socket. Si cambias el puerto y solo recargas sshd, seguirá escuchando en el viejo.
El efecto secundario útil es otro: como cada conexión arranca un sshd nuevo, los cambios de configuración se aplican a la siguiente conexión sin recargar nada, y tu sesión abierta sigue con la configuración anterior. Es justo la red de seguridad que quieres mientras pruebas.
Cortafuegos: denegar por defecto y nada más
Ubuntu trae ufw instalado pero desactivado. En Debian no viene instalado, pero está en los repositorios, y usarlo en las dos distribuciones simplifica la vida si administras ambas. Por debajo escribe reglas de nftables a través de iptables-nft, así que no pierdes el motor moderno.
La receta es corta: denegar la entrada por defecto, permitir SSH y permitir los puertos que el servidor sirve de verdad. Si necesitas NAT, límites de tasa finos o cadenas propias, pasa a nftables a mano y desactiva ufw; los dos a la vez acaban en reglas que nadie entiende.
Dos avisos que vemos a menudo:
- Docker se salta
ufw. Cuando publicas un puerto con Docker, este inserta sus propias reglas antes que las de ufw, y el puerto queda accesible aunque ufw status diga lo contrario. Si el servidor va a llevar contenedores, publica los puertos en 127.0.0.1 y pon delante un proxy inverso, o controla el tráfico en la cadena DOCKER-USER.
- El cortafuegos de la máquina no sustituye al de fuera. En infraestructura propia la primera capa es el cortafuegos del hipervisor y la segmentación de red, que contamos en el post de microsegmentación en Proxmox. El de la VM es la segunda.
Parches automáticos: el reinicio que nadie activa
Ubuntu instala unattended-upgrades y aplica las actualizaciones de seguridad desde el primer día. En Debian depende de cómo se instaló la máquina, así que compruébalo con dpkg -l unattended-upgrades. En las dos, cuando está activo, lo que se actualiza por defecto son los parches de seguridad y las versiones de punto de la distribución.
La mayoría se queda ahí y da el servidor por parcheado. Está a medias. Unattended-Upgrade::Automatic-Reboot viene a false en las cuatro distribuciones. Un parche del kernel o de glibc se descarga, se instala y se queda en el disco sin hacer nada, mientras /var/run/reboot-required sigue ahí. En Debian ese fichero lo crea el propio unattended-upgrades con un disparador del kernel. En Ubuntu lo hace update-notifier-common.
La decisión de reiniciar hay que tomarla a propósito. Nosotros la activamos a una hora fija y con Automatic-Reboot-WithUsers "false", para que una sesión abierta aplace el reinicio. Ojo: esa opción viene a true, así que si activas el reinicio sin tocarla, el servidor se reiniciará aunque haya alguien trabajando dentro.
Si el reinicio desatendido no encaja con tus cargas, la alternativa no es no reiniciar, sino saber qué máquinas lo tienen pendiente y cerrarlo en una ventana. Es de lo que se ocupa una herramienta como PatchMon. Y si te preguntas si se puede cambiar de kernel sin reiniciar la carga, la respuesta corta está en KHO y LUO: todavía no para un servidor normal.
needrestart: Debian y Ubuntu hacen cosas distintas
Reiniciar la máquina cubre el kernel. Las bibliotecas son otro asunto: si se actualiza OpenSSL, los servicios que ya estaban corriendo siguen con la versión vieja en memoria hasta que se reinician. De eso se encarga needrestart, y aquí las dos distribuciones se separan:
- Ubuntu (desde la 24.04) activa un «modo Ubuntu» que reinicia los servicios afectados de forma automática. Normalmente es lo que quieres. Si un servicio concreto no debe reiniciarse solo, exclúyelo con un fichero en
/etc/needrestart/conf.d/. Ojo: si fijas ahí $nrconf{restart} de forma explícita, el modo Ubuntu se desactiva.
- Debian trae el modo interactivo, y el código de
needrestart lo cambia a solo listar cuando no hay terminal. Traducido: cuando unattended-upgrades actualiza una biblioteca por la noche, needrestart anota qué habría que reiniciar y no reinicia nada. Además, en Debian needrestart no viene instalado. Si quieres el comportamiento de Ubuntu, instálalo y fija el modo automático en conf.d, que es lo que hace el script.
fail2ban: mira antes si sshd ya lo hace
Aquí es donde más ha cambiado el consejo. OpenSSH 9.8 (1 de julio de 2024) añadió PerSourcePenalties, activo por defecto. sshd lleva la cuenta, por dirección de origen, de las autenticaciones fallidas, de las conexiones que nunca llegan a autenticarse y de los fallos del propio proceso, y rechaza esa dirección durante el tiempo acumulado. Lo hace dentro del propio servicio, sin leer registros.
Compruébalo así:
sshd -T | grep -i persourcepenalties
En Debian 13 y Ubuntu 26.04 sale la línea con sus valores: 5 segundos por fallo de autenticación, 1 por conexión que no se autentica, 90 por caída del proceso, con un máximo de 600. En Debian 12 y Ubuntu 24.04 no sale nada, porque sus versiones son anteriores a la 9.8.
Así que el reparto honesto es este:
- En Debian 13 y Ubuntu 26.04, la jaula de SSH de fail2ban es en gran parte redundante.
- En Ubuntu 24.04 sigue teniendo sentido, y funciona tal como viene.
- En Debian 12, el fail2ban del repositorio viene con la jaula de SSH activada pero no arranca: como Debian 12 ya no instala
rsyslog, no hay /var/log/auth.log, y el servicio falla con «Have not found any log file for sshd jail». Lo hemos reproducido. Es el fallo #1037437, corregido en la versión 1.0.2-3 que no llegó a Debian 12. El arreglo es poner backend = systemd en jail.d e instalar python3-systemd.
Donde fail2ban sigue ganándose el sitio en cualquier versión es por encima de SSH: intentos fallidos en nginx, formularios de acceso de aplicaciones, correo. Y un dato que ha cambiado respecto al borrador: fail2ban ya no está parado en la 1.1.0 de abril de 2024. La 1.1.1 salió el 15 de agosto de 2026, aunque ninguna de estas distribuciones la trae todavía.
Registros: que sobrevivan al reinicio
Antes de instalar herramientas de auditoría, asegúrate de que los registros sobreviven al reinicio que acabas de activar. Desde Debian 12, rsyslog ya no se instala por defecto y el diario de systemd pasa a ser el registro principal, guardado en /var/log/journal. Pero si ese directorio no existe, journald guarda en memoria y lo pierdes todo al reiniciar. Las notas de Debian 12 avisan de que los sistemas actualizados desde versiones anteriores pueden haberse quedado así.
journalctl --disk-usage
ls -ld /var/log/journal
Y una cosa que ninguna línea base resuelve dentro de la máquina: si los registros solo están en el propio servidor, quien consiga root los edita. auditd registra, no impide. Para que sirvan como evidencia tienen que salir de la máquina.
Lynis: una lista de comprobación, no un veredicto
Lynis está en la 3.1.7, publicada el 25 de junio de 2026. Las anteriores son de octubre de 2025 (3.1.6), julio de 2025 (3.1.5) y enero de 2025 (3.1.4), y los cambios son sobre todo detección de sistemas y fechas de fin de soporte. Es mantenimiento estable, no desarrollo acelerado. Y la tabla de arriba lo confirma: los repositorios van por detrás, de la 3.0.8 de Debian 12 a la 3.1.6 de Ubuntu 26.04. Si quieres la última, CISOfy mantiene su propio repositorio.
Úsalo como una lista que sabe más detalles de cada distribución que tú, no como un escáner que encuentra intrusos. Su índice de endurecimiento es una barra de progreso orientativa. Perseguir un 90 lleva a desactivar cosas que la aplicación necesita.
El script
Probado en Debian 12 y 13 y Ubuntu 24.04 y 26.04, con bash 5 y como root. Se puede ejecutar varias veces sin romper nada. Escribe ficheros propios en las carpetas .d en lugar de editar la configuración de la distribución, así que las actualizaciones de paquetes no chocan con tus cambios.
Respecto al original, esta versión se niega a ejecutarse en un nodo de Proxmox, comprueba que el administrador está en sudo, instala ufw también en Debian, arregla el fail2ban de Debian 12, solo lo instala donde sshd no se defiende solo y ajusta needrestart en Debian.
#!/usr/bin/env bash
# Línea base para Debian 12/13 y Ubuntu 24.04/26.04. Ejecutar como root.
set -euo pipefail
[[ $EUID -eq 0 ]] || { echo "ejecútalo como root" >&2; exit 1; }
[[ -d /etc/pve ]] && { echo "nodo Proxmox: esta línea base no es para el hipervisor" >&2; exit 1; }
. /etc/os-release
ADMIN_USER="${ADMIN_USER:-${SUDO_USER:-}}"
SSH_PORT="${SSH_PORT:-22}"
export DEBIAN_FRONTEND=noninteractive
# 0. Que haya un administrador con clave y sudo antes de cerrar nada
[[ -n "$ADMIN_USER" ]] || { echo "define ADMIN_USER" >&2; exit 1; }
home_dir="$(getent passwd "$ADMIN_USER" | cut -d: -f6)"
[[ -s "${home_dir}/.ssh/authorized_keys" ]] || { echo "FATAL: ${ADMIN_USER} no tiene authorized_keys" >&2; exit 1; }
id -nG "$ADMIN_USER" | grep -qw sudo || { echo "FATAL: ${ADMIN_USER} no está en el grupo sudo" >&2; exit 1; }
# 1. SSH: el prefijo 10- se lee antes que el 50-cloud-init.conf
install -d -m 0755 /etc/ssh/sshd_config.d
cat > /etc/ssh/sshd_config.d/10-baseline.conf <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
AuthenticationMethods publickey
MaxAuthTries 3
X11Forwarding no
EOF
chmod 0644 /etc/ssh/sshd_config.d/10-baseline.conf
sshd -t
# Con ssh.socket (Ubuntu) se aplica en la próxima conexión; con ssh.service, recargar
systemctl is-active --quiet ssh.service && systemctl reload ssh.service
# 2. Cortafuegos
apt-get update -qq
apt-get install -y -qq --no-install-recommends ufw >/dev/null
ufw default deny incoming
ufw default allow outgoing
ufw allow "${SSH_PORT}/tcp" comment 'ssh'
ufw --force enable
# 3. Parches automáticos con reinicio controlado
apt-get install -y -qq --no-install-recommends unattended-upgrades needrestart >/dev/null
cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOF
cat > /etc/apt/apt.conf.d/52baseline-reboot <<'EOF'
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
Unattended-Upgrade::Automatic-Reboot-Time "03:30";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
EOF
# Debian: sin terminal, needrestart solo lista. Ubuntu ya reinicia servicios solo.
if [[ "$ID" == "debian" ]]; then
install -d /etc/needrestart/conf.d
echo "\$nrconf{restart} = 'a';" > /etc/needrestart/conf.d/50-baseline.conf
fi
# 4. fail2ban solo donde sshd no trae PerSourcePenalties (OpenSSH anterior a 9.8)
if ! sshd -T | grep -qi '^persourcepenalties'; then
apt-get install -y -qq --no-install-recommends fail2ban python3-systemd >/dev/null
cat > /etc/fail2ban/jail.d/10-baseline.conf <<EOF
[sshd]
enabled = true
backend = systemd
port = ${SSH_PORT}
EOF
systemctl enable --now fail2ban
systemctl restart fail2ban
fi
# 5. Registros persistentes y auditoría
if [[ ! -d /var/log/journal ]]; then
install -d -m 2755 -o root -g systemd-journal /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald
fi
apt-get install -y -qq --no-install-recommends auditd lynis >/dev/null
systemctl enable --now auditd
cat <<'EOF'
Aplicado. Ahora comprueba; no te fíes del código de salida de este script:
sshd -T | grep -E 'permitrootlogin|passwordauthentication|kbdinteractive|persourcepenalties'
ufw status verbose
unattended-upgrade --dry-run --debug 2>&1 | tail -20
fail2ban-client status sshd # solo en Debian 12 y Ubuntu 24.04
journalctl --disk-usage
lynis audit system --quick
EOF
El bloque final es lo importante. Un script de hardening que solo informa de su propio éxito te mentirá la primera vez que la distribución cambie un valor por defecto. Y cambian: este mismo texto habría sido distinto hace un año.
Cuando esta línea base no es la herramienta
En un nodo de Proxmox VE. Es el caso que más nos toca, y por eso el script se niega a ejecutarse en él. Tres medidas de esta lista rompen un clúster:
PermitRootLogin no. Proxmox usa túneles SSH entre nodos como root, con claves en /etc/pve/priv/authorized_keys, para la consola de un nodo desde otro, las migraciones de memoria y almacenamiento local en modo seguro y la replicación. Lo razonable es dejar prohibit-password, que ya es el valor por defecto, y cerrar el acceso desde fuera con el cortafuegos y una red de gestión aislada. Lo contamos en endurecer el acceso a Proxmox.
ufw. Proxmox tiene su propio cortafuegos, pve-firewall, que gestiona las reglas del nodo y de cada invitado. Meter ufw al lado es buscarse reglas cruzadas.
- El reinicio automático. Reiniciar un nodo por su cuenta a las 03:30 en un clúster con alta disponibilidad es provocar migraciones y, con mala suerte, perder quórum. En un hipervisor, los reinicios se planifican nodo a nodo, vaciándolo antes.
Infraestructura inmutable o efímera. Si las instancias se reconstruyen desde una imagen en vez de parchearse en caliente, unattended-upgrades es el mecanismo equivocado. Parchea la imagen y vuelve a desplegar. La lógica de reinicio de aquí choca con ese modelo.
Regímenes de cumplimiento. CIS Benchmarks, el ENS en su categoría alta o PCI DSS piden bastante más: AIDE, reglas concretas de auditd, caducidad de contraseñas, evidencias. Esto es un suelo, no un paquete de auditoría. Si te mueves en ese terreno, empieza por nuestra guía de NIS2.
Contenedores. Endurecer el anfitrión no dice nada de lo que hay dentro de las imágenes. Lo explicamos con el caso de Leaky Vessels.
Lo que no te protege
Conviene decirlo sin rodeos. Esta línea base no hace nada contra:
- Tu aplicación. Inyecciones SQL, SSRF, controles de acceso rotos. La mayoría de las intrusiones reales entran por el 443.
- La cadena de suministro. Una dependencia comprometida se ejecuta con los privilegios de tu servicio, y
PermitRootLogin no le da igual.
- Credenciales robadas. Una clave privada filtrada o un portátil comprometido convierten el acceso solo con clave en un trámite.
- Lo que pasa después de entrar.
auditd registra, no impide, y si los registros no salen de la máquina, el intruso los borra.
- Los fallos de día cero del kernel o de OpenSSH. Los parches automáticos acortan la ventana, no la cierran.
Esa lista no es una excusa para saltarse la línea base. Es la razón para dedicarle treinta minutos y no más, y pasar el resto del día en lo que de verdad expone el servidor.
Lo que nos llevamos
Comprobar antes de cambiar: sshd -T, ufw status verbose y unattended-upgrade --dry-run dicen qué está en vigor, y casi siempre hay menos que hacer de lo que prometen las listas. No fijar algoritmos. Tomar la decisión del reinicio a propósito. Mirar PerSourcePenalties antes de instalar fail2ban. Y tratar a Lynis como una lista de comprobación.
En los servicios gestionados que operamos, esta línea base se aplica con la plantilla de cada VM, no a mano, y las comprobaciones del final corren de forma periódica para detectar cuándo un valor se ha movido. Si tienes una flota de Debian y Ubuntu repartida entre proveedores y no sabes cuántas máquinas siguen con la contraseña abierta o con el reinicio pendiente, cuéntanos cómo la tienes y lo miramos contigo.
Preguntas frecuentes
¿Hay que cambiar el puerto de SSH?
No mejora la seguridad de forma real: reduce el ruido en los registros, pero un escaneo encuentra el puerto en segundos. Si lo haces en Ubuntu 24.04 o posterior, recuerda que el cambio en sshd_config necesita systemctl daemon-reload y reiniciar ssh.socket, y abre el puerto nuevo en el cortafuegos antes de cerrar el viejo.
¿Por qué no debo fijar los cifrados de SSH?
Porque la lista por defecto de Debian y Ubuntu ya es moderna y porque fijarla te deja fuera de mejoras futuras. El ejemplo reciente es el intercambio de claves poscuántico mlkem768x25519-sha256, predeterminado desde OpenSSH 10.0. Si una norma te obliga a quitar un algoritmo, hazlo con la forma sustractiva: Ciphers -nombre.
¿Necesito fail2ban en 2026?
Para SSH, depende de la versión de OpenSSH. Desde la 9.8, sshd trae PerSourcePenalties, que bloquea por origen sin leer registros; Debian 13 y Ubuntu 26.04 lo tienen activo. En Debian 12 y Ubuntu 24.04 sigue siendo útil. Para servicios web, formularios de acceso o correo, fail2ban sigue teniendo sentido en cualquier versión.
¿Por qué mi configuración de SSH no se aplica aunque sshd -t no da errores?
Casi siempre por el orden de los ficheros. En sshd_config.d/ se leen en orden alfabético y gana el primer valor. Si cloud-init dejó un 50-cloud-init.conf con PasswordAuthentication yes, un fichero tuyo llamado 60-algo.conf pierde. Ponle un prefijo menor, como 10-, y comprueba con sshd -T.
¿unattended-upgrades reinicia el servidor?
No por defecto. Automatic-Reboot viene a false en Debian y Ubuntu. Hay que activarlo a propósito y, si lo haces, poner Automatic-Reboot-WithUsers "false" para no reiniciar con alguien conectado. Comprueba si hay un reinicio pendiente con ls /var/run/reboot-required.
¿Puedo aplicar esta línea base a un nodo de Proxmox VE?
No tal cual. PermitRootLogin no rompe las migraciones y la consola entre nodos, ufw choca con el cortafuegos de Proxmox y el reinicio automático no tiene sentido en un clúster. En el hipervisor, el acceso se endurece con doble factor, roles, tokens y una red de gestión aislada.
Fuentes
- Daniel Valev, The 30-Minute Linux Server Hardening Baseline I Apply to Everything (DevOps.dev, 4 de agosto de 2026): el artículo de partida y su script original para Ubuntu.
- OpenSSH, notas de versión:
prohibit-password por defecto (7.0, 11-ago-2015), PerSourcePenalties (9.8, 1-jul-2024), eliminación de DSA y mlkem768x25519-sha256 por defecto (10.0, 9-abr-2025).
- Canonical, OpenSSH crypto configuration: los valores por defecto bastan para la mayoría de despliegues y el aviso sobre restringir algoritmos.
- Ubuntu Community Hub, SSHd now uses socket-based activation: activación por socket desde la 22.10 y el generador que lee
Port desde la 24.04.
- Debian, notas de publicación de Debian 12, capítulo 5:
rsyslog deja de instalarse por defecto y el diario persistente en /var/log/journal.
- Debian, fallo #1037437: la jaula
sshd de fail2ban no arranca en Debian 12 sin rsyslog; corregido en 1.0.2-3.
- fail2ban, versiones en GitHub: 1.1.0 (25-abr-2024) y 1.1.1 (15-ago-2026).
- CISOfy, versiones de Lynis: 3.1.4 (28-ene-2025), 3.1.5 (29-jul-2025), 3.1.6 (23-oct-2025) y 3.1.7 (25-jun-2026).
- Proxmox, Cluster Manager: el papel de SSH entre nodos (consola, migraciones, replicación) y el requisito del puerto 22.
Las versiones de paquetes, los valores de sshd -T, el orden de los ficheros de sshd_config.d, el comportamiento de needrestart y el fallo de fail2ban en Debian 12 los hemos comprobado en las imágenes oficiales debian:12, debian:13, ubuntu:24.04 y ubuntu:26.04 el 30 de septiembre de 2026.