Cómo mantener un túnel SSH siempre activo con autossh y systemd

Ejecuta el túnel como servicio de systemd con Restart=always y usa como comando autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes. Las opciones de keepalive hacen que ssh detecte una conexión muerta, autossh la reinicia y systemd lo reinicia todo tras una caída o un arranque. Usa una clave dedicada en una cuenta que solo pueda abrir ese túnel.

Actualizada el . autossh 1.4g, systemd 257 y OpenSSH 10.0 en Debian 13, en contenedores desechables con privilegios.

1. Crea una clave para el túnel

En la máquina que inicia el túnel, crea una cuenta local y una clave sin frase de contraseña, porque el servicio arranca sin supervisión. Después guarda la clave de host del servidor, para que la primera conexión no se detenga a preguntar:

sudo apt install autossh
sudo useradd -m -s /usr/sbin/nologin tunnel
sudo -u tunnel mkdir -m 700 /home/tunnel/.ssh
sudo -u tunnel ssh-keygen -t ed25519 -N "" -C tunnel -f /home/tunnel/.ssh/id_ed25519
ssh-keyscan -t ed25519 server | sudo -u tunnel tee /home/tunnel/.ssh/known_hosts

Compara esa clave de host con la huella real del servidor antes de fiarte de ella. Consulta known_hosts explicado.

2. Restringe la cuenta en el servidor

Una clave sin frase de contraseña en una máquina remota debería abrir solo este túnel. En el servidor, crea una cuenta llamada tunnel y pon la clave pública en su ~/.ssh/authorized_keys:

restrict,port-forwarding ssh-ed25519 AAAA... tunnel

Después añade esto al final de /etc/ssh/sshd_config:

Match User tunnel
  AllowTcpForwarding remote
  PermitListen localhost:2222
  X11Forwarding no
  ForceCommand /usr/sbin/nologin
sudo sshd -t && sudo systemctl reload ssh

Probamos el resultado con esta clave:

IntentoResultado
-R 2222:localhost:22Funciona
-R 2223:localhost:22remote port forwarding failed for listen port 2223
Ejecutar un comando o un shellThis account is currently not available.
-L 9000:localhost:22Conexión cerrada, no pasa nada

La última línea necesita el bloque Match. Con solo permitlisten="localhost:2222" en authorized_keys, un reenvío -L seguía funcionando en nuestra prueba, lo que permitía a la clave llegar a servicios del loopback del servidor. Hay más sobre las opciones de clave en cómo restringir una clave SSH.

3. Crea el servicio

Guarda esto como /etc/systemd/system/autossh-tunnel.service:

[Unit]
Description=Reverse SSH tunnel to server
Wants=network-online.target
After=network-online.target

[Service]
User=tunnel
Environment=AUTOSSH_GATETIME=0
ExecStart=/usr/bin/autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 -R 2222:localhost:22 tunnel@server
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now autossh-tunnel.service
systemctl status autossh-tunnel

Qué hace cada parte

  • -M 0 desactiva el puerto de monitorización propio de autossh, de modo que reinicia ssh solo cuando ssh termina. El README de autossh sugiere las opciones de keepalive como sustituto del puerto de monitorización.
  • ServerAliveInterval=30 y ServerAliveCountMax=3 hacen que ssh termine tras unos 90 a 120 segundos de silencio del servidor. En nuestra prueba, un intervalo de 5 segundos con el mismo contador tardó 20 segundos, y una congelación de 100 segundos con estos valores no lo activó.
  • ExitOnForwardFailure=yes hace que ssh termine si no se puede abrir el puerto. Sin ello, puedes tener una conexión viva sin túnel.
  • AUTOSSH_GATETIME=0 hace que autossh siga intentándolo aunque falle el primer intento, lo que importa en el arranque.
  • Restart=always recupera el servicio si el propio autossh muere.
  • No uses aquí el -f de autossh, porque systemd espera que el proceso se quede en primer plano.

4. Mira cómo se recupera

Qué hicimosQué pasó
Matamos la sesión en el servidorautossh inició un nuevo ssh de inmediato
Congelamos el servidor durante 28 segundos, con un intervalo de 5 segundosTimeout, server server not responding. a los 20 segundos, y una nueva conexión cuando el servidor volvió
Matamos autossh con kill -9, y su sshsystemd reinició el servicio y NRestarts pasó a 1

Compruébalo tú mismo con journalctl -u autossh-tunnel -f. En el servidor, ss -ltn | grep 2222 muestra el puerto.

Si el puerto sigue ocupado tras una caída de red

Tras una caída real, el servidor puede seguir teniendo la conexión antigua y su puerto, así que la nueva falla con remote port forwarding failed hasta que la antigua caduque. En el bloque Match del servidor, añade:

  ClientAliveInterval 30
  ClientAliveCountMax 3

Así el servidor descarta a los clientes muertos tras unos 90 segundos y los reintentos funcionan.

Para otros motivos por los que el puerto puede estar ocupado, consulta túnel SSH: Address already in use y remote port forwarding failed.

Si de verdad hace falta autossh

Con systemd y las opciones de keepalive, también funciona ssh simple. ssh termina y Restart=always lo inicia de nuevo. Sustituye el comando de la unidad por:

ExecStart=/usr/bin/ssh -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 -R 2222:localhost:22 tunnel@server

En nuestra prueba se recuperó de una sesión eliminada con un reinicio de systemd, tras los 10 segundos de RestartSec. autossh reinicia antes, y es la opción si no tienes systemd.

Una diferencia que vimos: systemctl restart registró status=1/FAILURE para autossh al detenerse. El servicio volvió a arrancar con normalidad.

Preguntas

¿Qué hace autossh -M 0?

Desactiva el puerto de monitorización. autossh reinicia ssh solo cuando ssh termina, y las opciones ServerAliveInterval y ServerAliveCountMax hacen que ssh termine cuando la conexión muere.

¿Hace falta autossh si uso systemd?

No siempre. Un ssh normal con las opciones keepalive y Restart=always también vuelve. autossh se reinicia antes y funciona sin systemd.

¿Por qué falla mi túnel inverso tras un corte de red?

El servidor puede seguir con la conexión antigua y su puerto. Añade en el servidor ClientAliveInterval 30 y ClientAliveCountMax 3 para que cierre los clientes muertos tras unos 90 segundos.

Fuentes

Más en Túneles y jump hosts

Todas las guías