Debian 13: puerto SSH cambiado — corregir firewall y orden de sshd sin bloquearte (caso #87)

¿Te fue útil?

Cambiaste el puerto SSH. Pareció responsable. Luego tu terminal se quedó colgado al conectar, las alertas se encendieron y te diste cuenta de que quizá acababas de
enseñar al servidor a ignorarte. Lo peor: todo parece “bien” desde dentro.

El caso nº 87 es clásico: sshd está configurado para el nuevo puerto, pero el firewall y el orden de arranque no coinciden. O sshd nunca enlazó el socket que crees.
La solución no es “reiniciar y rezar”. Es una migración controlada con puntos de verificación verificables y un plan de rollback que sobreviva la latencia, el error humano y systemd.

Qué cambió realmente (y qué no)

Cambiar el puerto SSH son en realidad dos problemas separados que a la gente le encanta mezclar:

  • sshd debe escuchar en el nuevo puerto (y no fallar la validación de configuración, enlazar en la interfaz equivocada o ser bloqueado por otro servicio).
  • la red debe permitir el nuevo puerto (firewall local, security groups en la nube, ACLs upstream, hosts de salto, NAT y cualquier equipo perimetral “útil” de la empresa).

Debian 13 no “bloquea mágicamente” tu nuevo puerto, pero viene con valores por defecto modernos: activación por socket systemd en algunos entornos, nftables como
backend por defecto para muchas herramientas de administración y políticas de endurecimiento cada vez más categóricas. El orden en que los servicios arrancan al inicio
puede importar más de lo que te gustaría, especialmente si las reglas del firewall se aplican tarde, se vacían inesperadamente o son reemplazadas por una herramienta que cree que lo controla todo.

Aquí está la verdad central: si no puedes demostrar qué está escuchando y qué está permitido, estás adivinando. Adivinar es como la gente acaba abriendo tickets
que empiezan con “Ayer funcionaba” y acaban con “Adjuntamos un ISO de rescate”.

Datos interesantes y breve historia (lo que cambia cómo solucionas problemas)

  1. El puerto 22 no siempre fue el predeterminado. Las primeras implementaciones de SSH fueron más fluidas; las convenciones de puerto se endurecieron a medida que las herramientas y la documentación se estandarizaron.
  2. Cambiar el puerto no “asegura SSH” desde el punto de vista criptográfico. Principalmente reduce el ruido de escaneos genéricos. Aun así puede ser útil operacionalmente.
  3. systemd hizo el arranque de servicios determinista… y también más fácil de reordenar accidentalmente. Si añades drop-ins de forma casual, puedes crear carreras en el arranque que sólo aparecen tras reinicios.
  4. nftables reemplazó a iptables como la dirección preferida del firewall en Linux. Muchos sistemas aún mantienen capas de compatibilidad con iptables; herramientas mixtas pueden crear situaciones donde “las reglas parecen correctas pero no están activas”.
  5. OpenSSH soporta desde hace tiempo múltiples puertos de escucha. Puedes ejecutar 22 y 2222 en paralelo durante una ventana de migración y eliminar el puerto antiguo después.
  6. Los firewalls en la nube son aparte de los firewalls del host. Puedes abrir nftables y seguir estando bloqueado por un security group, o al revés.
  7. sshd puede fallar silenciosamente desde la perspectiva de clientes remotos. Una mala configuración puede impedir el bind; el servicio puede entrar en bucle de reinicios mientras los clientes ven “Connection refused” o timeouts.
  8. Algunos entornos usan activation por socket de systemd para ssh. En ese caso, el “puerto que escucha” lo controla la unidad socket, no sólo sshd_config.

Una cita que mantengo pegada en mi monitor, porque resume todo en una frase:
“La esperanza no es una estrategia.” — Gene Kranz

Guion rápido de diagnóstico

Cuando SSH falla tras un cambio de puerto, no necesitas filosofía. Necesitas una secuencia que encuentre el cuello de botella rápido.

Primero: determina qué fallo estás viendo

  • “Connection refused”: nada está escuchando en esa IP:puerto, o un firewall está rechazando activamente.
  • Timeout / cuelgues: un firewall está descartando paquetes, el enrutamiento/NAT es incorrecto, o estás apuntando a la IP equivocada.
  • Handshake pero falla la autenticación: sshd es alcanzable, pero la configuración (usuarios/llaves/AllowUsers/Match blocks) cambió.

Segundo: demuestra que sshd está escuchando donde crees

  • Comprueba ss -lntp localmente.
  • Revisa systemctl status ssh y los logs.
  • Si la activación por socket está involucrada, revisa systemctl status ssh.socket.

Tercero: demuestra que el puerto está permitido de extremo a extremo

  • Firewall del host: estado de nftables/UFW/iptables.
  • Firewall upstream: security group de la nube, NACL, ACL corporativa o router.
  • Desde un host remoto: nc -vz o ssh -vvv para ver si los SYNs vuelven.

Cuarto: confirma el orden de arranque si falla sólo después de reiniciar

  • Busca servicios de firewall que vacíen/reapliquen reglas después de que sshd arranque.
  • Revisa dependencias: sshd no debería iniciar “correctamente” y luego perder el puerto porque las reglas fueron reemplazadas.

Chiste #1: Cambiar puertos SSH a las 16:59 es una excelente forma de asegurarte de que estarás “disponible” todo el fin de semana.

Cómo cambiar puertos SSH sin auto-provocarte una caída

El cambio de puerto más seguro es una migración, no un interruptor. Ejecuta ambos puertos temporalmente, valida desde al menos dos redes cliente independientes y luego elimina el puerto antiguo.
Sí, es aburrido. Sí, lo aburrido es la intención.

Principio 1: Mantén una línea de vida

Antes de tocar nada: asegúrate de tener una vía fuera de banda. Puede ser una consola serial de la nube, IPMI/iDRAC/iLO, una consola del hipervisor
o un camino bastión “romper vidrio”. Si no la tienes, tu ventana de cambio ya es una apuesta.

Principio 2: Valida la configuración antes de reiniciar

sshd es estricto por una razón. Un typo en sshd_config puede detener el servicio. Valida la configuración con sshd -t y solo entonces recarga.
Recargar es mejor que reiniciar cuando estás remoto: los reinicios cierran sesiones existentes más rápidamente.

Principio 3: No confíes en una sola herramienta de firewall

Elige una autoridad: nftables, o UFW (que gestiona nftables/iptables detrás de escena), o una política de gestión de configuración que sea la propietaria de las reglas.
Mezclar comandos iptables con sistemas gestionados por nftables es la forma de crear reglas que aparecen pero no se aplican, o que se aplican hasta la próxima recarga.

Principio 4: Demuestra la alcanzabilidad desde afuera

Las comprobaciones locales mienten por omisión. Tu servidor puede estar escuchando felizmente mientras un security group bloquea el puerto. Valida desde un host externo en Internet
o al menos desde un segmento diferente al de tu estación de administración.

Principio 5: Hazlo reversible en 60 segundos

Cuando edites el puerto, prepara un rollback: mantén un shell root abierto, programa un rollback automático del firewall con at o systemd-run,
y cancélalo solo después de confirmar que puedes reconectar por el nuevo puerto.

Chiste #2: Los firewalls son como los organigramas corporativos—todos creen entenderlos hasta que necesitan una excepción pequeña.

Tareas prácticas (comandos, salidas, decisiones)

Estas son las tareas que realmente ejecuto en producción cuando alguien dice “SSH murió tras un cambio de puerto.” Cada una incluye qué significa la salida y
qué decisión debes tomar a continuación. Hazlas en orden cuando sea posible.

Tarea 1: Confirma qué puerto cree usar sshd

cr0x@server:~$ sudo sshd -T | grep -E '^(port|listenaddress)\b'
port 2222
listenaddress 0.0.0.0
listenaddress ::

Significado: Esta es la configuración efectiva de sshd tras includes y Match blocks. Si port sigue mostrando 22, tu edición no se aplicó o fue sobreescrita.
Si listenaddress está restringido, podrías estar enlazando solo en una interfaz de gestión.
Decisión: Si el puerto es incorrecto, corrige la configuración primero. Si el puerto es correcto, pasa a sockets de escucha.

Tarea 2: Valida la sintaxis de sshd antes de recargar nada

cr0x@server:~$ sudo sshd -t
cr0x@server:~$ echo $?
0

Significado: Código de salida 0 significa que la sintaxis está OK. Un valor distinto a cero significa que sshd no arrancará con esa configuración.
Decisión: Si es distinto a cero, no reinicies. Arregla la configuración y vuelve a ejecutar sshd -t.

Tarea 3: Comprueba qué está realmente escuchando en la máquina

cr0x@server:~$ sudo ss -lntp | grep -E '(:22|:2222)\b' || true
LISTEN 0      128          0.0.0.0:2222      0.0.0.0:*    users:(("sshd",pid=944,fd=3))
LISTEN 0      128             [::]:2222         [::]:*    users:(("sshd",pid=944,fd=4))

Significado: sshd está enlazado en IPv4 e IPv6 para el puerto 2222. Si ves solo IPv6, clientes solo IPv4 fallarán.
Si no ves nada, sshd no enlazó—o está caído, o la activación por socket está en juego, o se bloqueó.
Decisión: Si no está escuchando, inspecciona el estado de systemd y los logs a continuación.

Tarea 4: Verifica el estado del servicio sshd y errores recientes

cr0x@server:~$ systemctl status ssh --no-pager
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Wed 2025-12-31 10:11:22 UTC; 2min 9s ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 944 (sshd)
      Tasks: 1 (limit: 19000)
     Memory: 6.4M
        CPU: 72ms
     CGroup: /system.slice/ssh.service
             └─944 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

Significado: Estar en ejecución es bueno, pero no prueba alcanzabilidad. Si ves bucles de reinicio, es probable que haya problemas de configuración o bind.
Decisión: Si está activo, procede al firewall. Si falla, entra en los logs.

Tarea 5: Lee los logs de sshd por problemas de bind y auth

cr0x@server:~$ sudo journalctl -u ssh -n 50 --no-pager
Dec 31 10:11:21 server sshd[944]: Server listening on 0.0.0.0 port 2222.
Dec 31 10:11:21 server sshd[944]: Server listening on :: port 2222.

Significado: El bind fue exitoso. Si en su lugar ves “Bad configuration option” o “Could not load host key,” sshd puede no aceptar conexiones.
Decisión: Si el bind está OK, muévete hacia afuera: firewall y upstream.

Tarea 6: Detecta si la activación por socket de systemd está involucrada

cr0x@server:~$ systemctl list-unit-files | grep -E '^ssh\.socket'
ssh.socket                                disabled        enabled

Significado: Si ssh.socket está enabled y activo, puede definir los puertos de escucha. Si está disabled, sshd enlaza directamente.
Decisión: Si la activación por socket está habilitada, inspecciona y edita la unidad socket, no solo sshd_config.

Tarea 7: Inspecciona la unidad socket por los puertos que escucha (si aplica)

cr0x@server:~$ systemctl cat ssh.socket
# /lib/systemd/system/ssh.socket
[Unit]
Description=OpenBSD Secure Shell server socket
Before=sockets.target

[Socket]
ListenStream=22
Accept=no

[Install]
WantedBy=sockets.target

Significado: Este socket sigue en el 22. Si está activo, clientes que apunten a 2222 fallarán.
Decisión: O bien actualiza ListenStream (o añade otro) y reinicia el socket, o deshabilita la activación por socket y ejecuta ssh.service normalmente.

Tarea 8: Confirma tu backend de firewall y el ruleset activo

cr0x@server:~$ sudo update-alternatives --display iptables | sed -n '1,12p'
iptables - auto mode
  link best version is /usr/sbin/iptables-nft
  link currently points to /usr/sbin/iptables-nft
  link iptables is /usr/sbin/iptables
  slave iptables-restore is /usr/sbin/iptables-restore
  slave iptables-save is /usr/sbin/iptables-save

Significado: Este host usa iptables-nft en compatibilidad. Si ejecutas comandos “clásicos” iptables asumiendo comportamiento legacy, tu modelo mental puede desviarse.
Decisión: Prefiere inspeccionar nftables directamente con nft list ruleset.

Tarea 9: Lista reglas de nftables y busca tu puerto SSH

cr0x@server:~$ sudo nft list ruleset | sed -n '1,120p'
table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;
		ct state established,related accept
		iif "lo" accept
		tcp dport 2222 accept
		icmp type echo-request accept
	}
}

Significado: Política drop significa “todo lo no aceptado explícitamente muere.” Tienes una regla que permite tcp/2222.
Decisión: Si falta la regla, añádela antes de mover sshd al nuevo puerto (o manten ambos puertos temporalmente).

Tarea 10: Si usas UFW, verifica que realmente permita el nuevo puerto

cr0x@server:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
2222/tcp                    ALLOW IN    Anywhere
2222/tcp (v6)               ALLOW IN    Anywhere (v6)

Significado: UFW está permitiendo tcp/2222 para IPv4 e IPv6. Si solo existe v4, clientes v6 fallarán (o al revés).
Decisión: Añade ambas familias si usas dual-stack. Luego prueba desde el exterior.

Tarea 11: Prueba desde el propio servidor (chequeo de sanidad, no prueba concluyente)

cr0x@server:~$ nc -vz 127.0.0.1 2222
Connection to 127.0.0.1 2222 port [tcp/*] succeeded!

Significado: La conexión por loopback local funciona. Esto prueba “escucha” pero no “alcanzable remotamente.”
Decisión: Si esto falla, sshd no está escuchando o la política local está bloqueando. Arregla localmente antes de tocar upstream.

Tarea 12: Prueba desde un host remoto con SSH verbose

cr0x@server:~$ ssh -vvv -p 2222 admin@203.0.113.10
OpenSSH_9.7p1 Debian-2, OpenSSL 3.0.14 4 Jun 2024
debug1: Connecting to 203.0.113.10 [203.0.113.10] port 2222.
debug1: Connection established.
debug1: Remote protocol version 2.0, remote software version OpenSSH_9.7p1 Debian-2
debug1: Authenticating to 203.0.113.10:2222 as 'admin'
debug1: Offering public key: /home/cr0x/.ssh/id_ed25519
debug1: Server accepts key: /home/cr0x/.ssh/id_ed25519
Authenticated to 203.0.113.10 ([203.0.113.10]:2222).

Significado: Esto muestra las etapas críticas: conexión TCP, handshake, auth. Si se queda antes de “Connection established,” probablemente sea firewall/enrutamiento.
Si ocurre el handshake pero falla la autenticación, centra la atención en los controles de auth de sshd.
Decisión: Si la conexión remota funciona, mantén la sesión abierta y procede a eliminar el puerto viejo más tarde. Si no, no elimines el puerto 22 todavía.

Tarea 13: Confirma que el servicio es alcanzable en la interfaz pública (errores de dirección de escucha)

cr0x@server:~$ ip -br a
lo               UNKNOWN        127.0.0.1/8 ::1/128
ens3             UP             203.0.113.10/24 2001:db8:10::10/64

Significado: Si sshd está enlazado a 127.0.0.1 o a una interfaz privada únicamente, el acceso remoto falla.
Decisión: Asegura que ListenAddress en sshd_config cubre las interfaces correctas, o elimínalo para enlazar a todas.

Tarea 14: Detecta fallos “funciona hasta el reinicio” por orden de arranque

cr0x@server:~$ systemd-analyze critical-chain ssh.service
ssh.service +42ms
└─network-online.target @3.211s
  └─systemd-networkd-wait-online.service @1.902s +1.292s
    └─systemd-networkd.service @1.501s +386ms
      └─systemd-udevd.service @1.221s +265ms
        └─systemd-tmpfiles-setup-dev.service @1.159s +54ms
          └─kmod-static-nodes.service @1.085s +58ms
            └─systemd-journald.socket @1.010s

Significado: Esto te dice de qué esperó ssh. Si tu servicio de firewall llega más tarde y vacía reglas, ssh puede ser momentáneamente alcanzable y luego no.
Decisión: Inspecciona el orden de la unidad del firewall y asegura que el firewall se aplique antes de que confíes en él para el control de acceso, o evita que el firewall vacíe políticas establecidas inesperadamente.

Tarea 15: Verifica el orden del servicio de firewall y si vacía reglas

cr0x@server:~$ systemctl status nftables --no-pager
● nftables.service - nftables
     Loaded: loaded (/lib/systemd/system/nftables.service; enabled; preset: enabled)
     Active: active (exited) since Wed 2025-12-31 10:10:59 UTC; 2min 50s ago
       Docs: man:nft(8)
    Process: 611 ExecStart=/usr/sbin/nft -f /etc/nftables.conf (code=exited, status=0/SUCCESS)

Significado: nftables cargó desde un archivo y salió (normal). Si tienes otro gestor de firewall, revisa su unidad y scripts por “flush rules.”
Decisión: Si el firewall no está habilitado, las reglas pueden no persistir tras reinicio. Habilítalo o gestiona la persistencia de otra forma.

Tarea 16: Demuestra persistencia: comprobar la configuración segura para reinicio sin reiniciar

cr0x@server:~$ sudo nft -c -f /etc/nftables.conf && echo "nftables.conf parses OK"
nftables.conf parses OK

Significado: -c verifica sintaxis y semántica sin aplicar. Si falla, un reinicio podría aplicar un ruleset roto y bloquear SSH.
Decisión: Arregla errores de parseo ahora. Luego confirma que la unidad está enabled para que cargue las reglas correctas al arrancar.

Tarea 17: Programa un rollback temporizado de cambios en el firewall

cr0x@server:~$ echo "sudo nft flush ruleset; sudo nft -f /root/nftables.known-good.conf" | at now + 5 minutes
warning: commands will be executed using /bin/sh
job 12 at Wed Dec 31 10:20:00 2025

Significado: Acabas de crear un paracaídas. Si te bloqueas, el servidor revertirá en 5 minutos (suponiendo que pueda ejecutar el job).
Decisión: Cancela el job solo después de haber confirmado SSH remoto en el nuevo puerto y de verificar que las reglas del firewall son persistentes.

Tarea 18: Cancela el rollback una vez verificado el nuevo puerto

cr0x@server:~$ atq
12	Wed Dec 31 10:20:00 2025 a root
cr0x@server:~$ atrm 12
cr0x@server:~$ atq

Significado: No queda un rollback en la cola.
Decisión: Ahora es seguro proceder a eliminar el puerto 22 (más tarde), porque has probado el acceso y quitado las ruedas de entrenamiento.

Orden en systemd: sshd vs firewall al arrancar

La mayoría de los incidentes “cambié el puerto y funcionó hasta reiniciar” no son místicos. Son problemas de orden y propiedad:
un firewall carga después de sshd y reemplaza reglas, una herramienta vacía el ruleset, o un agente de gestión de configuración escribe una política antigua al arrancar.

Qué significa realmente “orden” en este contexto

El orden en systemd no es sólo qué unidad arranca primero. También importa qué unidad se considera “lista” y qué jala mediante dependencias.
Que sshd esté “active (running)” no significa “alcanzable desde tu portátil.” Sólo significa que el demonio está vivo.

Haz que las reglas del firewall se apliquen pronto, de forma consistente y desde una sola fuente

En Debian, si gestionas el firewall vía nftables.service, las reglas suelen cargarse desde /etc/nftables.conf.
Si gestionas vía UFW, UFW tiene su propio servicio systemd y generará reglas.
Si gestionas vía un agente corporativo, puede reescribir ambos.

El objetivo es simple:

  • La política del firewall se aplica antes de que dependas de ella para el control de acceso.
  • La política del firewall no se vacía ni reemplaza inesperadamente después de que sshd ya esté en uso.
  • El puerto SSH que necesitas está permitido en la misma política que efectivamente se carga al inicio.

Un enfoque práctico: mantener sshd alcanzable durante las transiciones

Cuando migres puertos, permite ambos puertos en el firewall y configura sshd para escuchar en ambos puertos temporalmente.
Luego elimina el puerto 22 del firewall sólo después de haber validado el nuevo acceso desde múltiples puntos y sobrevivido al menos a un reinicio.

Editar sshd_config de forma segura: múltiples puertos

OpenSSH soporta múltiples directivas Port. Para una ventana de migración, haz esto:

cr0x@server:~$ sudo sh -c 'printf "\n# Migration window: keep 22 temporarily\nPort 22\nPort 2222\n" >> /etc/ssh/sshd_config'

Luego valida y recarga (no reinicies) cuando estés remoto:

cr0x@server:~$ sudo sshd -t && sudo systemctl reload ssh

Decisión: Sólo elimina Port 22 después de que puedas conectarte de forma fiable por el nuevo puerto y hayas confirmado la persistencia del firewall.

Cuando ssh.socket complica la situación

Si ssh.socket está habilitado, systemd posee el socket de escucha. En ese caso:

  • Cambiar Port en sshd_config puede no hacer lo que crees.
  • Debes cambiar ListenStream en la unidad socket (idealmente mediante un drop-in).
  • Luego reinicia la unidad socket, no solo el servicio.

Si no quieres activación por socket, desactívala intencionalmente—no vivas en un estado medio donde no sabes quién posee el puerto.

Recetas de firewall: nftables, UFW, iptables

El “firewall correcto” es el que tu sistema realmente usa de forma coherente entre reinicios y automatización. En Debian 13, nftables es el camino más limpio.
Si usas UFW, trátalo como tu única interfaz. Si estás atado a iptables en un parque legacy, sé explícito sobre si usas legacy o backend nft.

nftables: permitir el nuevo puerto SSH (tabla inet)

Ejemplo de fragmento en /etc/nftables.conf. Esto asume una política input por defecto drop:

cr0x@server:~$ sudo grep -n 'chain input' -n /etc/nftables.conf | head
12:	chain input {

Añade una regla allow cerca de otros aceptes TCP:

cr0x@server:~$ sudo nft add rule inet filter input tcp dport 2222 accept

Significado: Esto modifica el ruleset en ejecución pero no necesariamente el archivo de configuración. Si reinicias sin guardar, puede desaparecer.
Decisión: Refleja inmediatamente el cambio en /etc/nftables.conf (o en tu include gestionado) y valida con nft -c.

UFW: permitir nuevo puerto SSH (y hacerlo perdurable)

cr0x@server:~$ sudo ufw allow 2222/tcp
Rule added
Rule added (v6)

Significado: UFW creó reglas para IPv4 e IPv6.
Decisión: Mantén ufw status verbose como tu fuente de verdad. No mezcles con reglas nft crudas a menos que disfrutes depurar a las 2 AM.

iptables (entornos legacy): permitir nuevo puerto con cuidado

cr0x@server:~$ sudo iptables -S INPUT | sed -n '1,25p'
-P INPUT DROP
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT

Añade la nueva regla:

cr0x@server:~$ sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT

Significado: Regla añadida al final; si el orden importa (y a menudo lo hace), quizá prefieras insertar antes de una regla de reject/log.
Decisión: Asegura persistencia mediante tu mecanismo elegido (por ejemplo, un archivo gestionado o servicio). De lo contrario, un reinicio te revierte.

No olvides los firewalls upstream

El firewall del host puede ser perfecto y aun así inútil si:

  • un security group de la nube bloquea 2222,
  • un ACL perimetral corporativo permite sólo 22 hacia tu subred,
  • un load balancer tiene un health check fijado en 22 y marca tu nodo como caído.

Tu evidencia está en el comportamiento de la red: “refused” suele significar que alcanzaste el host; los timeouts suelen significar que no lo hiciste.
Trata esa diferencia como una brújula.

Tres historias corporativas desde el terreno

1) El incidente por una suposición errónea: “Abrimos el firewall”

Una compañía mediana migró una flota a Debian 13 y decidió “reducir la superficie de ataque” moviendo SSH a 2222. El ingeniero de guardia abrió 2222 en UFW
y confirmó que ss -lntp mostraba sshd escuchando. Marcas verdes. Luego eliminó el puerto 22 por todas partes. Seguro, ordenado y equivocado.

A la mañana siguiente, la mitad de la flota era inalcanzable. No todas: solo los hosts en una región. Los logs en los hosts accesibles estaban limpios, y en los inalcanzables
la consola mostraba sshd funcionando bien. El equipo quemó horas persiguiendo bugs fantasma de sshd e incluso reconstruyó una VM desde una imagen solo para “resetearla”.

La causa raíz fue upstream: una plantilla de security group dependía de la región. Una región permitía sólo 22, otra permitía “SSH” como servicio nombrado que apuntaba a 22, y sólo una tercera tenía la regla nueva 2222. La suposición del ingeniero—“el firewall del host equivale a acceso”—fue la trampa.

La solución fue aburrida: actualizar security groups, mantener escucha dual durante 48 horas y añadir una prueba de conectividad externa al procedimiento de cambio.
El equipo aprendió a tratar “puedo conectar desde dentro del VPC” como una verdad parcial, no como una victoria.

2) La optimización que salió mal: “Simplifiquemos el servicio de firewall”

Un equipo de plataforma de una gran empresa estandarizó en nftables y escribió una pequeña unidad systemd oneshot que vaciaba y recargaba el ruleset al arrancar.
Su objetivo era sensato: una sola fuente de verdad, sin deriva, arranque rápido. Lo desplegaron ampliamente y siguieron con su trabajo.

Semanas después, otro equipo cambió puertos SSH durante una ventana aprobada. Actualizaron /etc/nftables.conf, recargaron nftables, validaron acceso externo,
y luego reiniciaron para una actualización de kernel. Tras el reinicio, SSH estaba muerto—pero solo de forma intermitente en algunos hosts.

El culpable fue esa unidad de “optimización”. Se ejecutaba después de network-online pero antes de que algunos nombres de interfaz se establecieran, y además vaciaba reglas como primer paso.
Durante una ventana corta, el host tenía política drop por defecto sin reglas allow cargadas aún. Algunas conexiones fueron intentadas en esa ventana y fallaron.
Los humanos lo llamaron “SSH roto”, pero en realidad era una carrera en el arranque más un flush agresivo.

La corrección permanente: dejar de vaciar indiscriminadamente, cargar un ruleset atómico y ordenar la unidad del firewall lo suficientemente temprano para que servicios que dependen de acceso entrante
no sufran una brecha “denegar todo” breve. También: no “optimices” el arranque del firewall sin medir qué hace a la alcanzabilidad durante el boot.

3) La práctica aburrida pero correcta que salvó el día: puerto dual + rollback temporizado

Un equipo de servicios financieros tenía una regla: cualquier cambio que pudiera bloquearte debía tener un rollback temporizado. No un “seremos cuidadosos.” Un revert programado real.
La gente se quejaba de que era paranoico. Luego un viernes les demostró su valor, silenciosamente.

Un ingeniero cambió SSH a 2222 y actualizó el firewall. Funcionó desde su estación. No funcionó desde los runners de CI que vivían en una subred separada con su propia política de salida. Nadie lo notó hasta que las deploys empezaron a fallar. El equipo aún tenía una sesión SSH abierta, pero estaban a una desconexión de un incidente real.

Como habían programado un job de rollback, pudieron experimentar con seguridad. Reabrieron 22 temporalmente, arreglaron la política de salida de los runners, probaron 2222 desde ambas redes y solo entonces quitaron 22 de nuevo. Sin consolas de rescate, sin reimágenes, sin pánicos de “quién tiene la contraseña del hipervisor?”

La lección es consistentemente molesta: la mejor práctica de fiabilidad a menudo parece ceremonia innecesaria hasta que evita una caída.

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: “Connection refused” en el nuevo puerto

  • Causa raíz: sshd no está escuchando en ese puerto, o está enlazado solo a localhost/otra interfaz, o ssh.socket sigue en 22.
  • Solución: Revisa sshd -T, ss -lntp y systemctl cat ssh.socket. Corrige Port/ListenAddress o la unidad socket, valida con sshd -t y luego recarga.

2) Síntoma: Timeout / cuelgue al conectar

  • Causa raíz: drop de firewall (host o upstream), IP equivocada, enrutamiento/NAT mismatched, o security group no actualizado.
  • Solución: Desde un host remoto ejecuta ssh -vvv. En el servidor, revisa nftables/UFW. Luego confirma reglas upstream. Los timeouts casi nunca son “sshd mal configurado”.

3) Síntoma: Funciona desde una red, falla desde otra

  • Causa raíz: allowlists asimétricas; la salida corporativa bloquea puertos altos; solo un path fue actualizado.
  • Solución: Prueba desde al menos dos puntos de vista (oficina + subnet de CI, o ISP doméstico + bastión). Actualiza ACLs. Considera mantener 22 abierto pero restringido a rango de IPs del bastión durante la migración.

4) Síntoma: Funciona hasta reiniciar, luego falla

  • Causa raíz: reglas del firewall no persistentes; un servicio de arranque vacía reglas; activación por socket revertida; CM sobrescribe cambios.
  • Solución: Asegura que el servicio de firewall esté enabled y cargue una configuración válida. Confirma con nft -c -f /etc/nftables.conf. Audita systemctl list-dependencies para tu tooling de firewall y revisa políticas de CM.

5) Síntoma: SSH conecta pero la autenticación falla tras el cambio de puerto

  • Causa raíz: Editaste más que el puerto: AllowUsers, PasswordAuthentication, PubkeyAuthentication o un bloque Match aplica diferente de lo esperado.
  • Solución: Usa sshd -T para ver settings efectivos; revisa journalctl -u ssh por mensajes de auth; asegura archivos de llaves y permisos correctos.

6) Síntoma: IPv6 funciona, IPv4 falla (o viceversa)

  • Causa raíz: sshd enlazó solo a una familia; reglas de firewall solo para v4 o v6; los clientes prefieren una familia que no probaste.
  • Solución: Confirma que ss muestra tanto 0.0.0.0:2222 como [::]:2222 si quieres dual-stack. Replica reglas del firewall para ambas familias.

7) Síntoma: El puerto parece abierto localmente, pero el remoto ve “No route to host”

  • Causa raíz: problema de enrutamiento/ACL upstream; a veces ICMP está bloqueado y problemas de MTU complican, pero “No route” suele ser de red.
  • Solución: Confirma la IP pública correcta, revisa security group/NACL y valida desde un host en la misma región/segmento.

Listas de verificación / plan paso a paso

Plan A: Migración segura (recomendado para producción)

  1. Mantén una sesión SSH existente abierta (preferiblemente dos: una root/sudo y otra observadora de solo lectura).
  2. Confirma acceso fuera de banda (consola cloud, IPMI, consola del hipervisor).
  3. Abre el nuevo puerto en el firewall primero (firewall del host y upstream). No elimines 22 todavía.
  4. Configura sshd para escuchar en ambos puertos: Port 22 y Port 2222.
  5. Valida la configuración: sshd -t y sshd -T | grep '^port '.
  6. Recarga sshd (no reinicies): systemctl reload ssh.
  7. Verifica sockets de escucha: ss -lntp | grep ':2222'.
  8. Prueba desde dos puntos remotos: estación + un host en otra red.
  9. Programa un job de rollback antes de quitar 22; cancélalo solo tras el éxito.
  10. Reinicia una vez durante la ventana si puedes, para probar persistencia y orden.
  11. Elimina el puerto 22 de sshd y del firewall después de la ventana de migración.
  12. Actualiza la automatización (inventario Ansible, configs de bastión, monitorización, runbooks de incidentes).

Plan B: Recuperación de emergencia cuando ya estás bloqueado

  1. Usa la consola fuera de banda para acceder al host.
  2. Revisa estado y logs de sshd: systemctl status ssh, journalctl -u ssh.
  3. Comprueba puertos de escucha: ss -lntp.
  4. Permite temporalmente el puerto 22 (o el puerto conocido-bueno) en el firewall del host y en upstream.
  5. Restaura sshd_config a escucha dual y valida con sshd -t.
  6. Recarga sshd, luego prueba desde el exterior.
  7. Sólo entonces reintenta una migración controlada usando el Plan A.

Plan C: Si la activación por socket de systemd está habilitada

  1. Confirma la propiedad: comprueba si ssh.socket está enabled y activo.
  2. Edita mediante drop-in en lugar de modificar unidades vendor directamente.
  3. Añade new ListenStream para la migración (por ejemplo, 22 y 2222), luego recarga daemon y reinicia el socket.
  4. Valida con ss -lntp y pruebas remotas.
  5. Elimina 22 más tarde borrando el ListenStream antiguo.

Preguntas frecuentes (FAQ)

1) ¿Vale la pena cambiar el puerto SSH?

No es magia de seguridad. Es reducción de ruido. Si ya exiges llaves, deshabilitas autenticación por contraseña y restringes por IP o bastión, el cambio de puerto es opcional.
En entornos de alto ruido puede reducir el spam en logs y los intentos de fuerza bruta. Trátalo como un ajuste operativo, no como un control para apostar tu auditoría.

2) ¿Debería reiniciar o recargar sshd?

Recarga cuando estés remoto. Recargar aplica la config sin cerrar sesiones existentes en la mayoría de casos. Reiniciar está bien cuando tienes acceso por consola o una ventana de mantenimiento,
pero es un riesgo innecesario cuando cambias lo que estás usando para conectarte.

3) ¿Puede sshd escuchar en dos puertos a la vez?

Sí. Añade múltiples líneas Port en /etc/ssh/sshd_config. Es la técnica de migración más segura porque es reversible y testeable.

4) Cambié Port, pero sshd sigue en 22. ¿Por qué?

Causas comunes: un archivo de configuración posterior lo sobreescribe (includes), un bloque Match cambia el comportamiento, o la activación por socket está habilitada y ssh.socket sigue en 22.
Usa sshd -T y systemctl cat ssh.socket para dejar de adivinar.

5) ¿Por qué obtengo timeouts en lugar de “refused”?

Los timeouts suelen significar que los paquetes se descartan en algún punto: política drop del firewall del host, security group que descarta, ACL upstream o enrutamiento.
“Refused” suele significar que alcanzaste el host y nada escuchó (o rechazó activamente). Esta distinción es una de tus mejores señales para troubleshooting.

6) ¿Necesito abrir también el puerto en IPv6?

Si tu host tiene conectividad IPv6 y los clientes pueden alcanzarlo, sí. Si no, obtendrás resultados inconsistentes: un cliente llega por v6 y funciona, otro por v4 y falla.
Replica reglas para ambas familias si tienes dual-stack.

7) ¿Cuál es la técnica de rollback más segura?

Mantén una sesión existente abierta y programa un rollback temporizado del firewall y/o de la configuración de sshd con at o systemd-run.
Cancela el rollback solo después de haberte conectado con éxito al nuevo puerto desde un host externo y validar la persistencia tras reinicio.

8) ¿Cómo evito romper la automatización y la monitorización?

Inventarios y herramientas a menudo asumen el puerto 22: CM, agentes de backup, checks de monitorización, bastiones y runners de CI.
Actualiza esas configuraciones durante la ventana de migración mientras ambos puertos funcionan, luego elimina 22. Si cambias primero, pasarás la noche encontrando cada suposición oculta.

9) ¿Cambiar el puerto rompe fail2ban o reglas de IDS?

Puede. Algunos rulesets y jails están ligados a definiciones de servicio “ssh” o asumen el puerto 22. Tras la migración, verifica que tus herramientas de seguridad vigilen el nuevo puerto
y no estén inactivas en silencio.

10) ¿Debo restringir SSH por IP origen en lugar de cambiar el puerto?

Prefiere la restricción por origen (solo bastión, solo VPN, IPs admin allowlist) cuando sea factible. El cambio de puerto puede ser una capa adicional, pero el control de acceso vence al camuflaje.
Haz ambos si encaja con tu modelo de amenazas y tu tolerancia operacional.

Conclusión: próximos pasos prácticos

Si te llevas una cosa del caso nº 87, que sea esta: trata los cambios de puerto SSH como una migración en producción, no como un tweak de configuración. Abre la nueva vía primero, mantiene la antigua durante la validación, demuestra la alcanzabilidad desde afuera y solo entonces cierra la puerta vieja.

Próximos pasos que rinden de inmediato:

  • Ejecuta sshd -T y ss -lntp para confirmar la realidad, no la intención.
  • Elige una autoridad de firewall (nftables o UFW) y hazla persistente tras reinicios.
  • Adopta la migración con puertos duales más un job de rollback temporizado como procedimiento estándar.
  • Reinicia durante la ventana de cambio al menos una vez, si puedes, para atrapar problemas de orden y persistencia mientras aún hay humanos despiertos.

Los sistemas en producción no premian la valentía. Premian hábitos repetibles, evidencia limpia y el tipo de paranoia que parece profesionalismo.

← Anterior
ZFS Proxmox: valores predeterminados de almacenamiento de VM que debes cambiar de inmediato
Siguiente →
Solución de problemas de rendimiento en Proxmox: CPU steal, IO wait, ZFS ARC y vecinos ruidosos

Deja un comentario