Cambiar el puerto SSH sin quedarte fuera: sshd, activación por socket y SELinux

Publicado el 17 min de lectura

Cambiar el puerto SSH falla casi siempre por el orden de los pasos. Esta guía deja el servidor escuchando en ambos puertos mientras dura el cambio, de modo que el antiguo solo desaparece cuando el nuevo funciona de forma demostrable.

Cambiar el puerto SSH es una de las recomendaciones más repetidas y peor justificadas de la administración de servidores. Sirve para algo, sí, pero no para lo que suele atribuírsele, y tiene un efecto secundario: entre el momento en que el servicio suelta el puerto antiguo y aquel en que el nuevo es accesible a través de todos los filtros de paquetes se abre un hueco. Quien no lo tiene previsto, lo encuentra.

Esta guía plantea el cambio de forma que ese hueco no llegue a existir: durante toda la maniobra el servidor escucha a la vez en el puerto antiguo y en el nuevo, y el antiguo solo desaparece cuando el nuevo funciona de forma demostrable.

Todos los datos se refieren a Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS y Ubuntu 22.04 LTS. Un apartado propio trata SELinux en sistemas RHEL como AlmaLinux y Rocky Linux. Los comandos están escritos para trabajar como root. Si trabajas como usuario normal, antepón sudo a cada comando. Como puerto de ejemplo se usa 2222, y como dirección de ejemplo 203.0.113.10.

Qué aporta otro puerto y qué no

No aporta seguridad. Un escaneo completo de los 65.535 puertos encuentra el servicio igualmente, y el identificador de versión que OpenSSH envía al establecer la conexión delata de inmediato de qué se trata, también en el puerto 51022. Cambiar de puerto no sustituye a ninguna de las medidas que sí funcionan: inicio de sesión por clave en lugar de contraseña, autenticación por contraseña desactivada y un filtro de paquetes estricto.

Menos ruido en los logs, que no es poco. La mayor parte de lo que llega al puerto 22 es escaneo masivo sin objetivo concreto. Esas herramientas prueban el puerto 22 y nada más, porque un escaneo completo de todo internet no sale a cuenta. Si mueves el servicio, esa clase de tráfico desaparece del journal, y solo entonces llama la atención el intento de acceso puntual y dirigido.

Costes que hay que contar. A partir de ahora, todas las herramientas necesitan el puerto: scripts de backup, despliegues, monitorización. Y un punto que casi ninguna guía menciona: las redes corporativas, el wifi de los hoteles y algunas tarifas móviles solo permiten unos pocos puertos de salida, normalmente el 22, el 80 y el 443. Desde esas redes puede que después ya no llegues a tu servidor.

En resumen: hazlo si quieres logs tranquilos. No lo hagas creyendo que con ello has resuelto un problema de seguridad.

La vía de vuelta, antes de tocar nada

1. Abre una vez la consola en el área de cliente

Todos los servidores root KVM y todos los servidores dedicados de KernelHost tienen una consola VNC en el área de cliente. Está conectada a la salida de pantalla del sistema y es independiente de la pila de red del servidor, así que una regla de firewall equivocada no puede bloquearla. Entra ahí una vez antes de empezar y asegúrate de conocer la contraseña de root. Una vía de rescate que se prueba por primera vez en plena emergencia no es una vía de rescate.

2. Dos sesiones, y la primera se queda abierta

Inicia sesión una segunda vez antes de empezar. Las conexiones SSH existentes sobreviven tanto a un reinicio del servicio como al cierre del puerto antiguo en el firewall, porque el estado de la conexión ya está establecido. Conservas por tanto una shell de root incluso cuando hace rato que te has quedado fuera para las conexiones nuevas. Justo ahí está la trampa: todo parece correcto hasta que cierras la ventana.

3. Un temporizador que deshace el cambio por sí solo

Si se caen las dos sesiones, por ejemplo porque tu conexión falla, esta tarea revierte el cambio de puerto al cabo de quince minutos:

systemd-run --on-active=15min --unit=ssh-portrollback /bin/sh -c 'rm -f /etc/ssh/sshd_config.d/20-port.conf /etc/systemd/system/ssh.socket.d/override.conf; systemctl daemon-reload; systemctl try-restart ssh.socket ssh.service'

Comprobación: systemctl list-timers ssh-portrollback.timer muestra el momento de disparo. Si todo ha funcionado, cancela la tarea, porque si no tu cambio se revertirá más tarde en plena producción:

systemctl stop ssh-portrollback.timer

El orden que no te deja fuera

Esta secuencia está pensada para que en ningún momento el camino antiguo esté ya cerrado y el nuevo todavía no abierto.

  1. Elegir el puerto y comprobar que está libre.
  2. Primero el firewall: abrir el puerto nuevo y dejar abierto el antiguo.
  3. Hacer que sshd escuche en ambos puertos.
  4. Con activación por socket, ajustar además la unidad de socket.
  5. Reiniciar y comprobar en el socket abierto.
  6. Iniciar sesión por el puerto nuevo con una tercera sesión recién abierta.
  7. Solo entonces quitar el puerto 22 y actualizar las herramientas.

Paso 1: elegir el puerto

El 2222 no. Es la alternativa más habitual y los escáneres masivos lo comprueban desde hace tiempo. Aquí lo usamos solo porque se lee bien como ejemplo.

Quédate por debajo de 32768. El rango del que el kernel toma los puertos de origen para las conexiones salientes está aquí:

cat /proc/sys/net/ipv4/ip_local_port_range

La salida habitual es 32768 60999. Un puerto de ese rango puede quedar ocupado temporalmente por una conexión saliente. Casi siempre sale bien, pero tras un reinicio, cuando otros servicios arrancan antes que sshd, el intento de bind falla con Address already in use y el servidor arranca sin SSH. Ocurre de forma esporádica y es incómodo de localizar.

Por debajo de 1024 hay una ventaja real. Los puertos inferiores a 1024 solo puede ocuparlos root. Si sshd se cae, ningún usuario sin privilegios puede colocarse ahí y levantar un servicio SSH falso que registre las credenciales. Con varias cuentas de usuario es un argumento de peso; con un único administrador queda más bien en lo teórico.

Comprobación: el puerto no debe estar ocupado ni reservado para un servicio que quieras usar más adelante. Ninguno de los dos comandos debe devolver nada:

ss -tlnp | grep -E ':2222 '
grep -w 2222 /etc/services

Paso 2: primero el firewall

Este paso va antes de la configuración de sshd, no después. Un puerto abierto sin servicio detrás es inofensivo; un servicio sin puerto abierto te deja fuera.

ufw allow 2222/tcp comment 'SSH nuevo'
ufw status verbose

Comprobación: en la salida deben aparecer ahora los dos puertos, y cada uno dos veces, una para IPv4 y otra con el añadido (v6). Si falta la línea de IPv6, el puerto nuevo no es accesible por IPv6, y los clientes modernos prueban IPv6 primero. Los detalles, en nuestra guía sobre el firewall UFW.

Si mantienes nftables a mano, añade el puerto en tu archivo de reglas, vuelve a cargarlo y comprueba el conjunto de reglas cargado con nft list ruleset | grep 2222. En sistemas RHEL con firewalld:

firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --reload
firewall-cmd --list-ports

Hay dos sitios que se pasan por alto. Primero fail2ban: si ahí actúa un bloqueo, después del cambio seguirá bloqueando solo el puerto 22, mientras los intentos contra el puerto nuevo pasan sin problema. Añade el puerto en /etc/fail2ban/jail.local:

[sshd]
enabled = true
port    = 2222

La detección sigue funcionando, porque fail2ban lee los intentos de acceso del journal y no se guía por el puerto. Solo el efecto del bloqueo depende de esa línea, como explicamos en nuestra guía de fail2ban. Segundo, un filtro de paquetes situado por delante del servidor, porque si no buscarás el fallo en el sitio equivocado.

Paso 3: sshd_config y la trampa de sshd_config.d

No toques /etc/ssh/sshd_config. Los cuatro sistemas leen configuración adicional de /etc/ssh/sshd_config.d/, y un archivo propio ahí sobrevive a las actualizaciones de paquetes sin preguntar nada. Mira primero qué hay ya:

ls -l /etc/ssh/sshd_config.d/
grep -n '^Include' /etc/ssh/sshd_config

En Debian y Ubuntu la línea Include viene de fábrica arriba del todo, normalmente en la línea 12. Eso importa más de lo que parece: en la mayoría de las directivas gana el primer valor encontrado, no el último. Como el Include está arriba, los archivos del directorio se imponen a todo lo que venga más abajo en sshd_config. Si alguien lo movió al final del archivo, la cosa se invierte y tu archivo se queda sin efecto.

Con Port hay una excepción, y es justo la que hace posible el cambio sin riesgo: varias líneas Port no se sustituyen entre sí, se suman. sshd escucha entonces en todos los puertos indicados. La otra cara es igual de importante: el valor por defecto 22 solo rige mientras no exista ninguna línea Port. En cuanto escribes una, el 22 desaparece si no lo incluyes expresamente.

tee /etc/ssh/sshd_config.d/20-port.conf >/dev/null <<'EOF'
Port 22
Port 2222
EOF
chmod 644 /etc/ssh/sshd_config.d/20-port.conf

Comprobación: primero la sintaxis y después la configuración efectiva completa, con los archivos incluidos ya resueltos:

sshd -t
sshd -T | grep -E '^(port|listenaddress) '

Se esperan dos líneas, port 22 y port 2222. Si además aparece una línea listenaddress, ese es el segundo sitio donde pueden figurar puertos: ListenAddress puede indicar una dirección junto con su puerto y limita entonces dónde se escucha. Una línea pasada por alto como ListenAddress 127.0.0.1 explica la mayoría de los casos en los que el puerto está bien configurado pero desde fuera no llega nadie.

Si en cambio sshd -t informa de Missing privilege separation directory: /run/sshd, es que el servicio no ha llegado a ejecutarse desde el arranque del sistema, y mkdir -p /run/sshd lo resuelve.

Paso 4: activación por socket, cuando el puerto no está en sshd_config

Aquí las distribuciones se separan, y aquí es donde ocurren la mayoría de los accidentes. Desde la 22.10, Ubuntu apuesta por la activación por socket: quien mantiene el puerto no es sshd, sino systemd. Escucha en su lugar y solo arranca un proceso sshd cuando entra una conexión. El puerto figura entonces en la unidad ssh.socket bajo ListenStream, y una línea Port en la configuración de sshd puede quedarse sin efecto.

No lo adivines, pregúntaselo al sistema:

systemctl is-enabled ssh.socket ssh.service
SistemaActivo de fábricaDe dónde sale el puerto
Debian 13 (trixie)ssh.service/etc/ssh/sshd_config.d/
Debian 12 (bookworm)ssh.service/etc/ssh/sshd_config.d/
Ubuntu 24.04 LTSssh.socketListenStream en la unidad de socket
Ubuntu 22.04 LTSssh.service/etc/ssh/sshd_config.d/

La tabla describe el estado de fábrica, no necesariamente tu servidor: las imágenes de distintas procedencias difieren, y un sistema actualizado conserva su ajuste anterior. Si ssh.socket está activo, systemctl cat muestra la unidad junto con todos los archivos complementarios y sus rutas, también los generados automáticamente:

systemctl cat ssh.socket

El puerto nuevo se registra como archivo complementario propio. La primera línea ListenStream=, la que va vacía, borra los valores existentes; a partir de ahí solo cuentan los tuyos. Sin esa puesta a cero, los valores de fábrica se sumarían a los nuevos:

mkdir -p /etc/systemd/system/ssh.socket.d
tee /etc/systemd/system/ssh.socket.d/override.conf >/dev/null <<'EOF'
[Socket]
ListenStream=
ListenStream=0.0.0.0:22
ListenStream=[::]:22
ListenStream=0.0.0.0:2222
ListenStream=[::]:2222
EOF
systemctl daemon-reload

También aquí conviven los dos puertos, por el mismo motivo que en el paso 3. Un archivo en /etc/systemd/system/ tiene prioridad sobre todo lo que el propio sistema genera.

Comprobación: systemctl cat ssh.socket muestra ahora tu archivo como un bloque propio.

Si ssh.service y ssh.socket están activos a la vez, se pelean por el mismo puerto. Eso se manifiesta como fatal: Cannot bind any address. o ssh.socket: Socket service ssh.service already active, refusing. Decídete entonces por uno de los dos modos de funcionamiento; el trasfondo lo tratamos en nuestra guía para asegurar SSH.

Paso 5: reiniciar y comprobar en el socket

Un comando que es correcto en los cuatro sistemas, porque try-restart solo reinicia lo que está realmente en marcha y deja intacta la otra unidad:

sshd -t && systemctl try-restart ssh.socket ssh.service

No uses reload: en sistemas con activación por socket responde con fatal: Cannot bind any address., y el servicio suelta después su puerto. Y tampoco un systemctl restart ssh.socket aislado: en Debian falla, y en Ubuntu 22.04 pasa el host a modo socket sin que te enteres. Las sesiones existentes sobreviven al reinicio en cualquier caso.

La comprobación, y es la única que de verdad cuenta:

ss -tlnp | grep -E ':(22|2222) '

Se esperan cuatro líneas: 0.0.0.0:22, [::]:22, 0.0.0.0:2222 y [::]:2222. Si faltan las líneas de IPv6, no llegarás al servidor por IPv6.

Una particularidad que provoca sustos innecesarios: con activación por socket, en la última columna puede aparecer systemd en lugar de sshd, porque systemd mantiene el socket a la escucha mientras no haya ningún proceso sshd en marcha. Filtra por tanto por el número de puerto y no por el nombre del proceso, o darás por muerto un servicio que funciona.

Paso 6: la tercera sesión, la prueba de verdad

Ahora, y ni un paso antes, abre un terminal nuevo. Las dos sesiones antiguas se quedan abiertas.

ssh -p 2222 root@203.0.113.10

Si no funciona, ssh -vv -p 2222 root@203.0.113.10 muestra en qué punto falla.

Que te pregunte es normal: SSH vuelve a preguntar por la autenticidad de la clave del host aunque en el servidor no haya cambiado nada. Las entradas de known_hosts están ligadas al puerto y, para puertos distintos del estándar, se guardan con la forma [203.0.113.10]:2222. Consúltalas y, en caso de duda, elimínalas:

ssh-keygen -F '[203.0.113.10]:2222'
ssh-keygen -R '[203.0.113.10]:2222'

Lo mejor es comprobar ahora si el cambio sobrevive a un reinicio. Como la regla para el puerto 22 sigue abierta, no hay peligro.

Paso 7: cerrar el puerto 22 y actualizar las herramientas

Solo cuando el paso 6 haya funcionado, quita el puerto antiguo. En 20-port.conf queda una única línea:

tee /etc/ssh/sshd_config.d/20-port.conf >/dev/null <<'EOF'
Port 2222
EOF
sshd -t && systemctl try-restart ssh.socket ssh.service

Con activación por socket, borra además las dos líneas con el puerto 22 de /etc/systemd/system/ssh.socket.d/override.conf y ejecuta a continuación systemctl daemon-reload. Después, el firewall:

ufw delete allow 22/tcp
ufw status verbose

Comprobación: ss -tlnp | grep -E ':(22|2222) ' muestra ahora solo el puerto nuevo, otra vez en las dos familias de direcciones.

Queda la parte que más cola trae: todas las herramientas que se conectan al servidor. Lo más cómodo es resolverlo de una vez por todas en ~/.ssh/config:

Host mi-servidor
    HostName 203.0.113.10
    Port 2222
    User kernel

Donde indiques la dirección directamente necesitas el puerto, y la forma de escribirlo cambia según la herramienta. Ese es el tropiezo clásico:

HerramientaIndicación del puertoEjemplo
ssh-p (minúscula)ssh -p 2222 kernel@203.0.113.10
scp-P (mayúscula)scp -P 2222 archivo.tar.gz kernel@203.0.113.10:/tmp/
sftp-P (mayúscula)sftp -P 2222 kernel@203.0.113.10
ssh-copy-id-p (minúscula)ssh-copy-id -p 2222 kernel@203.0.113.10
rsyncmediante -ersync -av -e 'ssh -p 2222' ./datos/ kernel@203.0.113.10:/srv/
giten la URLssh://git@203.0.113.10:2222/srv/repo.git

Por cierto, rsync --port se refiere al daemon de rsync y no a SSH, aquí no tiene ningún efecto. En Ansible el puerto se indica como ansible_port en el inventario.

SELinux en sistemas RHEL

En AlmaLinux, Rocky Linux y RHEL, SELinux viene de fábrica en modo enforcing, y la política solo permite a sshd los puertos etiquetados como ssh_port_t. De fábrica eso es únicamente el puerto 22. Sin este paso adicional el servicio no arranca, y el mensaje despista, porque parece un problema de permisos:

error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.

Primero, un vistazo al estado y a las etiquetas existentes:

getenforce
semanage port -l | grep ssh_port_t

Si falta semanage, está en el paquete policycoreutils-python-utils:

dnf install -y policycoreutils-python-utils

Añade el puerto nuevo, y hazlo antes de reiniciar sshd:

semanage port -a -t ssh_port_t -p tcp 2222

Si el puerto ya está asignado a otro tipo, -a falla avisando de que ya está definido. En ese caso cambia la asignación con -m en lugar de añadir una nueva.

Comprobación: semanage port -l | grep ssh_port_t lista ahora los dos puertos. Si aun así algo se atasca, los accesos denegados se ven aquí:

ausearch -m avc -ts recent

Debian y Ubuntu no tienen una restricción comparable en la instalación estándar, así que ahí este apartado no aplica.

Errores frecuentes y soluciones

ssh: connect to host 203.0.113.10 port 2222: Connection refused: los paquetes llegan, pero no escucha nadie. O el servicio no se reinició, o la configuración no llegó a aplicarse, o una línea ListenAddress lo ata a otra dirección.

ssh: connect to host 203.0.113.10 port 2222: Connection timed out: los paquetes ni siquiera llegan, y eso casi siempre es un filtro de paquetes en el servidor o en tu propia red. La diferencia con el mensaje anterior es el dato de diagnóstico más valioso que hay: rechazo significa que falta el servicio, tiempo agotado significa filtro.

error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.: en sistemas RHEL es SELinux, como se explica arriba. En Debian y Ubuntu este mensaje aparece prácticamente solo cuando sshd no corre como root y debe ocupar un puerto por debajo de 1024.

error: Bind to port 2222 on 0.0.0.0 failed: Address already in use.: otro proceso tiene el puerto, y ss -tlnp | grep ':2222 ' te dice cuál. Si el fallo solo aparece de forma esporádica tras un reinicio, el puerto cae en el rango de los puertos de origen dinámicos, como se explica en el paso 1.

fatal: Cannot bind any address.: ninguna de las direcciones solicitadas se pudo ocupar. En Debian y Ubuntu eso significa casi siempre que ssh.socket ya mantiene el puerto y que además quieres arrancar ssh.service sobre él.

Job for ssh.service failed because the control process exited with error code.: la causa está en el journal, no en esa línea. journalctl -u ssh -n 50 --no-pager la muestra, sea cual sea la versión de OpenSSH.

/etc/ssh/sshd_config.d/20-port.conf line 1: Bad configuration option: prot: una errata. sshd -t indica el archivo y el número de línea, y justo para eso va ese comando delante de cada reinicio.

sshd: no hostkeys available -- exiting.: faltan las claves de host o no se pueden leer; ssh-keygen -A genera las que falten. sshd -t y sshd -T necesitan permiso de lectura sobre ellas y por eso tienen que ejecutarse como root.

ssh: connect to host 203.0.113.10 port 22: Connection refused después de un cambio correcto: tu cliente sigue intentando el puerto estándar. Completa ~/.ssh/config o pasa -p en la llamada.

Si aun así te has quedado fuera

  1. No reinicies. Un reinicio restablece las reglas del firewall y la configuración del servicio, y el servidor vuelve igual de cerrado.
  2. Abre la consola VNC en el área de cliente y entra como root. El acceso por consola no pasa por SSH y tu cambio no le afecta.
  3. Determina el tipo de fallo. ss -tlnp lo responde al instante: si el puerto aparece, es un problema de filtrado. Si no aparece, es un problema del servicio, y journalctl -u ssh -n 50 --no-pager dice por qué.
  4. Revierte en lugar de reparar. rm -f /etc/ssh/sshd_config.d/20-port.conf y, si lo creaste, rm -f /etc/systemd/system/ssh.socket.d/override.conf, después systemctl daemon-reload y systemctl try-restart ssh.socket ssh.service.
  5. Si la causa fue el firewall, ayuda ufw disable, seguido de una reconstrucción limpia con la regla de puerto correcta.

Resumen

  • Primero el firewall, sshd después. El puerto antiguo sigue abierto hasta que el nuevo funcione de forma demostrable.
  • Varias líneas Port se suman, y eso hace que la transición no tenga riesgo. El valor por defecto 22 desaparece en cuanto existe cualquier línea Port.
  • Con activación por socket, que en Ubuntu 24.04 viene de fábrica, el puerto está en ListenStream.
  • Lo que vale nunca es el archivo de configuración, sino ss -tlnp, filtrado por número de puerto.
  • La vía de rescate es la consola VNC del área de cliente; ten preparada la contraseña de root.

Si acabas de poner el servidor en marcha, nuestra lista de comprobación para servidores root nuevos encaja este paso dentro del resto de la configuración inicial. Y una última puntualización: otro puerto hace tus logs legibles. Lo que asegura el acceso es el inicio de sesión por clave y la autenticación por contraseña desactivada. Si solo vas a aplicar una de las dos cosas, aplica la segunda.

Preguntas frecuentes

¿Un puerto SSH distinto aporta más seguridad?
No. Un escaneo completo de los 65.535 puertos encuentra el servicio igualmente, y el identificador de versión que OpenSSH envía al establecer la conexión delata de inmediato de qué se trata, también en el puerto 51022. La ganancia real está en otro sitio: la mayor parte de lo que llega al puerto 22 es escaneo masivo sin objetivo concreto, y esas herramientas prueban el puerto 22 y nada más. Después del cambio esa clase de tráfico desaparece del journal, y solo entonces llama la atención el intento de acceso puntual y dirigido. Lo que asegura el acceso es el inicio de sesión por clave, la autenticación por contraseña desactivada y un filtro de paquetes estricto.
¿Qué puerto debo elegir?
El 2222 no, porque es la alternativa más habitual y los escáneres masivos lo comprueban desde hace tiempo. Quédate por debajo de 32768: ahí suele empezar el rango del que el kernel toma los puertos de origen para las conexiones salientes, y puedes consultarlo en /proc/sys/net/ipv4/ip_local_port_range. Un puerto de ese rango puede estar ocupado temporalmente y, tras un reinicio, el intento de bind falla entonces de forma esporádica con Address already in use, mientras el servidor arranca sin SSH. Los puertos por debajo de 1024 solo puede ocuparlos root, así que ahí ningún usuario sin privilegios puede colocarse con un servicio SSH falso y registrar las credenciales. Comprueba de antemano con ss -tlnp y con grep -w 2222 /etc/services que el puerto está libre y no está reservado para ningún otro servicio.
¿Cómo cambio el puerto sin quedarme fuera?
Haciendo que el servidor escuche en ambos puertos mientras dura la maniobra. El orden es: abrir el puerto nuevo en el firewall y dejar abierto el antiguo, hacer luego que sshd escuche en los dos puertos, reiniciar, iniciar sesión por el puerto nuevo con una tercera sesión recién abierta y solo después quitar el puerto 22. Lo hace posible una particularidad de OpenSSH: varias líneas Port no se sustituyen entre sí, se suman. La otra cara es igual de importante: el valor por defecto 22 solo rige mientras no exista ninguna línea Port. En cuanto escribes una, tienes que incluir el 22 expresamente o desaparecerá.
Mi línea Port no surte efecto, sshd sigue escuchando solo en el 22. ¿A qué se debe?
Hay dos causas posibles. Primera, la activación por socket: Ubuntu apuesta por ella desde la 22.10 y viene activada de fábrica en Ubuntu 24.04. En ese caso quien mantiene el puerto no es sshd, sino systemd, y lo que manda es ListenStream en la unidad ssh.socket, no la línea Port de la configuración de sshd. Qué modo tienes en marcha te lo dice systemctl is-enabled ssh.socket ssh.service. Segunda, el orden del Include: en la mayoría de las directivas gana el primer valor encontrado, y la línea Include para /etc/ssh/sshd_config.d/ viene de fábrica arriba del todo en Debian y Ubuntu. Si alguien la movió al final del archivo, tu archivo se queda sin efecto. Lo que se aplica realmente lo muestra sshd -T, filtrado por las líneas port y listenaddress. Si ahí aparece una línea listenaddress, limita además dónde se escucha.
¿Cuál es la diferencia entre Connection refused y Connection timed out?
Es el dato de diagnóstico más valioso que hay. ssh: connect to host 203.0.113.10 port 2222: Connection refused significa que los paquetes llegan, pero no escucha nadie: o el servicio no se reinició, o la configuración no llegó a aplicarse, o una línea ListenAddress lo ata a otra dirección. ssh: connect to host 203.0.113.10 port 2222: Connection timed out significa, en cambio, que los paquetes ni siquiera llegan, y eso casi siempre es un filtro de paquetes en el servidor o en tu propia red. En resumen: rechazo significa que falta el servicio, tiempo agotado significa filtro.
En AlmaLinux y Rocky Linux sshd no arranca: error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.
Eso es SELinux y no un problema de permisos, aunque el mensaje lo parezca. En sistemas RHEL, SELinux funciona de fábrica en modo enforcing, y la política solo permite a sshd los puertos etiquetados como ssh_port_t. De fábrica eso es únicamente el puerto 22. Etiqueta el puerto nuevo antes de reiniciar el servicio: semanage port -a -t ssh_port_t -p tcp 2222. Si falta semanage, está en el paquete policycoreutils-python-utils. Si el puerto ya está asignado a otro tipo, -a falla avisando de que ya está definido, y entonces cambias la asignación con -m. Los accesos denegados los muestra ausearch -m avc -ts recent. Debian y Ubuntu no tienen una restricción comparable en la instalación estándar.
¿Qué más tengo que ajustar después del cambio?
Primero fail2ban: si ahí actúa un bloqueo, seguirá bloqueando solo el puerto 22 mientras los intentos contra el puerto nuevo pasan sin problema. Añade el puerto en el apartado sshd de /etc/fail2ban/jail.local. La detección sigue funcionando de todos modos, porque fail2ban lee los intentos de acceso del journal; solo el efecto del bloqueo depende de esa línea. Después, todas las herramientas que se conectan al servidor: lo más cómodo es resolverlo de una vez por todas con una entrada de host que incluya el puerto en ~/.ssh/config. Donde indiques la dirección directamente, la forma de escribirlo cambia según la herramienta: ssh y ssh-copy-id llevan -p en minúscula, scp y sftp -P en mayúscula, rsync recibe el puerto mediante la opción -e, git en la URL y Ansible como ansible_port en el inventario. rsync --port, en cambio, se refiere al daemon de rsync y aquí no tiene ningún efecto. Que SSH vuelva a preguntar por la autenticidad de la clave del host en la primera conexión es normal: las entradas de known_hosts están ligadas al puerto y se guardan con la forma [203.0.113.10]:2222.
Me he quedado fuera. ¿Y ahora qué?
No reinicies el servidor: restablece las reglas del firewall y la configuración del servicio, y vuelve igual de cerrado. Abre en su lugar la consola VNC de tu área de cliente y entra como root, porque el acceso por consola no pasa por SSH y tu cambio no le afecta. El tipo de fallo lo responde ss -tlnp al instante: si el puerto aparece, es un problema de filtrado; si no aparece, un problema del servicio, y journalctl -u ssh -n 50 --no-pager dice por qué. Después revierte en lugar de reparar: borra /etc/ssh/sshd_config.d/20-port.conf y, si lo creaste, /etc/systemd/system/ssh.socket.d/override.conf, a continuación systemctl daemon-reload y systemctl try-restart ssh.socket ssh.service. Si la causa fue el firewall, ayuda ufw disable, seguido de una reconstrucción limpia con la regla de puerto correcta.

SSH OpenSSH sshd Seguridad de servidores Debian Ubuntu systemd SELinux