Cómo configurar un túnel SSH inverso detrás de NAT

En la máquina detrás de NAT, ejecuta ssh -N -R 2222:localhost:22 user@server, donde server tiene una dirección pública. Luego, en ese servidor, ejecuta ssh -p 2222 user@localhost para llegar a la máquina oculta. Por defecto el puerto escucha solo en el loopback del servidor, así que no queda expuesto a internet a menos que configures GatewayPorts.

Actualizada el . Cliente y servidor OpenSSH 10.0 en Debian 13, en contenedores desechables dentro de una misma red, con un contenedor haciendo de máquina detrás de NAT.

Cómo funciona

Una máquina detrás de NAT no puede aceptar conexiones entrantes, pero sí puede hacer conexiones salientes. Así que se conecta hacia fuera a un servidor con dirección pública y le pide que escuche en un puerto. Las conexiones a ese puerto vuelven por la conexión SSH ya existente.

Para los otros dos tipos de reenvío, consulta túneles SSH locales, remotos y dinámicos.

Entrar por SSH a la máquina oculta

La máquina oculta necesita su propio servidor SSH en marcha. En la máquina oculta:

ssh -N -o ExitOnForwardFailure=yes -R 2222:localhost:22 user@server

-N no ejecuta ningún comando remoto. -R 2222:localhost:22 significa que el puerto 2222 del servidor lleva al puerto 22 de esta máquina. Añade -f para ponerlo en segundo plano.

En el servidor, puedes ver el puerto y usarlo:

$ ss -ltn | grep 2222
LISTEN 0  128  127.0.0.1:2222  0.0.0.0:*
LISTEN 0  128      [::1]:2222     [::]:*
$ ssh -p 2222 hiddenuser@localhost

Desde tu portátil, pasa por el servidor en un solo paso:

ssh -J user@server -p 2222 hiddenuser@localhost

Para no mezclar las claves de host, ya que localhost podría significar muchas máquinas, pon esto en ~/.ssh/config:

Host home
  HostName localhost
  Port 2222
  User hiddenuser
  ProxyJump user@server
  HostKeyAlias home

Mostrar una aplicación web local

ssh -N -R 8080:localhost:3000 user@server

Ahora curl http://localhost:8080/ en el servidor llega al puerto 3000 de tu máquina. En nuestra prueba, desde otra máquina, curl http://server:8080/ fue rechazado hasta que cambiamos GatewayPorts.

Con el puerto 0, el servidor elige un puerto libre y ssh lo muestra:

$ ssh -N -R 0:localhost:3000 user@server
Allocated port 33341 for remote forward to localhost:3000

GatewayPorts: quién puede llegar al puerto

Este ajuste está en el sshd_config del servidor y por defecto vale no. Lo que medimos:

GatewayPorts-R 8080:...-R 0.0.0.0:8080:...
noSolo loopbackSolo loopback, y la dirección se ignora
clientspecifiedSolo loopbackTodas las direcciones, accesible desde otras máquinas
yesTodas las direccionesTodas las direcciones

Con yes, incluso -R localhost:8080:... escuchó en todas las direcciones en nuestra prueba. Si necesitas acceso desde fuera, clientspecified deja la decisión al cliente, y el cortafuegos todavía tiene que permitir el puerto.

Mantenerlo privado

  • Deja GatewayPorts no y usa -J como se muestra arriba. Así solo puede llegar al puerto quien pueda iniciar sesión en el servidor.
  • Recuerda a los demás usuarios del servidor. El loopback lo comparten todos los que están conectados ahí. Pueden llegar al puerto, y el servicio que hay detrás sigue necesitando su propio inicio de sesión.
  • Usa una clave y una cuenta dedicadas para el túnel, ya que la máquina detrás de NAT guarda una clave que puede entrar en tu servidor. La guía de autossh muestra una configuración restringida y probada.

Cuando el puerto está ocupado

Con ExitOnForwardFailure=yes, ssh se detiene y sale con código 255:

Error: remote port forwarding failed for listen port 2222

Sin esa opción, solo recibes un Warning y ssh sigue conectado sin túnel, algo fácil de pasar por alto. Una causa común es que una conexión antigua siga ocupando el puerto en el servidor. AllowTcpForwarding no o una regla PermitListen en el servidor producen el mismo tipo de fallo.

Mantenerlo en marcha

Un ssh -R simple termina cuando se cae la conexión, y nada lo reinicia. Para un túnel permanente, sigue autossh y systemd.

Preguntas

¿Qué diferencia hay entre ssh -L y ssh -R?

-L abre un puerto en tu máquina que lleva a algo que el servidor puede alcanzar. -R hace lo contrario: abre un puerto en el servidor que vuelve a tu máquina.

¿Por qué otras máquinas no llegan al puerto del túnel inverso?

Con el valor por defecto GatewayPorts no, el servidor solo escucha en su loopback. Pon GatewayPorts clientspecified o yes en sshd_config y abre el puerto en el cortafuegos.

¿Cómo mantengo activo un túnel SSH inverso?

Un ssh -R simple termina cuando se cae la conexión. Ejecútalo con autossh y systemd para que se reinicie solo.

Fuentes

Más en Túneles y jump hosts

Todas las guías