El reenvío de puertos SSH es una de esas funciones que la gente copia de Stack Overflow, consigue que funcione una vez, y nunca llega a entender del todo —así que la próxima vez que la necesita, vuelve a adivinar cuál de -L, -R y -D usar. Los tres flags no son difíciles; solo están mal nombrados. Aquí tienes el único modelo mental que los vuelve obvios, un ejemplo de cada uno, y los flags y trampas que deciden si el túnel sobrevive a la tarde.
La única pregunta que elige el flag
Todo túnel SSH abre un puerto de escucha en una máquina y envía en silencio lo que le llega a algún sitio alcanzable desde la otra máquina. Así que la única pregunta es: ¿qué lado abre el puerto al que te conectas?
-L(local) —el puerto se abre en tu máquina. Te conectas localmente; el tráfico sale por el lado del servidor.-R(remoto) —el puerto se abre en el servidor. Alguien se conecta ahí; el tráfico sale por tu lado.-D(dinámico) —el puerto se abre en tu máquina, pero es un proxy SOCKS: el destino se decide por conexión, no fijado de antemano.
Todo lo demás es detalle. Ten «qué lado abre el puerto» en la cabeza y nunca volverás a coger el flag equivocado.
Reenvío local (-L) — trae un servicio remoto a localhost
El más común. Una base de datos, un panel interno, una UI de métricas —algo atado a 127.0.0.1 en el servidor, o en una máquina que solo el servidor puede alcanzar. Lo quieres en tu portátil como si fuera local.
ssh -L 5432:localhost:5432 deploy@db.example.com
Léelo de izquierda a derecha: abre 5432 en mi máquina, y reenvía lo que llegue a localhost:5432 tal como lo ve el servidor. Ahora psql -h 127.0.0.1 en tu portátil habla con el Postgres del servidor (en inglés) —a través de la conexión SSH cifrada, sin exponer la base de datos a la red en absoluto.
El host del medio se resuelve en el lado del servidor, que es lo que hace esto potente:
# llega a una base de datos que solo el bastión puede ver
ssh -L 5432:10.0.0.9:5432 deploy@bastion.example.com
Aquí 10.0.0.9 es una dirección privada a la que tu portátil no puede enrutar —pero el bastión sí. El túnel salva la distancia.
Reenvío remoto (-R) — expón tu máquina al lado del servidor
La imagen reflejada, y la que la gente entiende al revés (tiene su propio artículo a fondo —túneles SSH inversos). El puerto se abre en el servidor; las conexiones a él salen por tu extremo. Dos usos clásicos:
# deja que el servidor (o su red) llegue a un servidor de desarrollo que corre en tu portátil
ssh -R 8080:localhost:3000 deploy@app.example.com
Ahora todo lo que pega en localhost:8080 del servidor se canaliza a localhost:3000 de tu portátil —útil para enseñar a un compañero en la máquina tu trabajo en curso, o dejar que un servicio remoto llame de vuelta a algo que solo tú corres.
Este es también el truco para llegar a una máquina atascada tras NAT sin acceso de entrada: haz que ella abra un túnel inverso hacia un servidor que controlas, y te conectas a través del servidor. La máquina que no puede aceptar conexiones hace la de salida en su lugar.
Un detalle: por defecto el servidor ata el puerto reenviado a localhost, así que solo el propio servidor puede usarlo. Para dejar que otras máquinas de la red del servidor lleguen a él, el sshd_config del servidor necesita GatewayPorts yes (o clientspecified) —un ajuste del lado del servidor, no algo que puedas forzar desde el cliente.
Reenvío dinámico (-D) — un proxy SOCKS en un flag
-L reenvía a un único destino fijo. -D abre un proxy SOCKS en su lugar, así que el destino se elige por petición:
ssh -D 1080 deploy@jump.example.com
Apunta tu navegador (o curl --socks5 127.0.0.1:1080) a 127.0.0.1:1080 y cada petición se enruta desde el servidor. Es la forma rápida de navegar por una red interna, probar qué aspecto tiene un servicio desde la IP de un centro de datos, o llegar a un puñado de hosts internos sin un -L para cada uno.
Los flags que lo hacen usable
Un comando de túnel pelado abre además una shell que no quieres y muere en cuanto la cierras. En la práctica:
ssh -N -L 5432:localhost:5432 deploy@db.example.com
-N—no ejecutes un comando remoto. Solo quieres el túnel, no una shell.-f—manda el proceso a segundo plano tras conectar (combínalo con-N).-o ExitOnForwardFailure=yes—si el puerto no se puede atar (ya en uso, permiso denegado), falla de forma ruidosa en vez de conectarse igualmente y reenviar nada en silencio.- Keepalives —un túnel de larga duración muere a tiempos de espera por inactividad sin
ServerAliveInterval 30. Añádelo o el túnel se cae en cuanto dejas de usarlo.
Hazlo permanente en ssh_config
Todo lo que pasarías con -L/-R/-D tiene una directiva de configuración, así que un host guardado lleva sus túneles:
Host db-tunnel
HostName db.example.com
User deploy
LocalForward 5432 localhost:5432
ServerAliveInterval 30
RequestTTY no
Ahora ssh -N db-tunnel trae la base de datos a 127.0.0.1:5432 sin flags que recordar. RemoteForward y DynamicForward funcionan igual. (Más sobre el propio archivo de configuración en la guía de ~/.ssh/config (en inglés).)
Tres trampas
- El bind address importa.
-L 5432:...escucha solo en127.0.0.1. Para dejar que otros dispositivos de tu LAN usen el túnel, antepón un bind address:-L 0.0.0.0:5432:...—y entiende que acabas de exponerlo a tu red local. - «Address already in use» significa que el puerto local está ocupado (a menudo un túnel anterior que no salió). Elige otro puerto local o mata el viejo;
ExitOnForwardFailureconvierte el fallo silencioso en un error que de verdad notarás. - El host del medio se resuelve en el lado lejano.
localhosten-L a:localhost:bsignifica el localhost del servidor, no el tuyo. Entenderlo al revés es el error de reenvío de puertos más común de todos.
Cuando el túnel no se ata
Si el comando falla con bind: Address already in use o bind: Permission denied, el puerto de escucha nunca se abrió —recorre la lista corta. Ya en uso: algo retiene el puerto, a menudo el túnel olvidado de ayer —ss -tlnp nombra el proceso, luego elige otro puerto o mata el viejo. Permiso denegado en local suele significar un puerto privilegiado: cualquiera por debajo de 1024 necesita root, así que escucha en 8443 en vez de 443. En el extremo remoto (-R), el servidor tiene veto: AllowTcpForwarding no en sshd_config bloquea el reenvío de plano, y sin GatewayPorts el puerto de escucha remoto se ata solo a localhost, sea cual sea el bind address que pidieras. Y en hosts de la familia RHEL endurecidos, SELinux puede denegar a sshd el bind aun cuando la configuración lo permita —revisa el registro de auditoría antes de culpar a tu comando.
Cuando hacer malabares con túneles a mano cansa
El reenvío es una primitiva estupenda, y todo desarrollador debería saberse los tres flags al dedillo. La fricción aparece después: mantienes un cuaderno de comandos ssh -N -L ... (en inglés), los re-ejecutas tras cada suspensión del portátil, olvidas qué puerto local mapea a qué servicio, y no puedes saber de un vistazo si un túnel sigue siquiera arriba.
Ahí es donde ayuda un cliente con un gestor de túneles en condiciones. Termalin mantiene los reenvíos locales, remotos y dinámicos atados al host como configuración guardada —arráncalos con un clic, ve cuáles están vivos, y deja que se restablezcan con la conexión en vez de reteclear el comando. La misma lista de hosts (con sus claves, jump hosts y keepalives) se sincroniza entre tus máquinas con cifrado de extremo a extremo, y cada servidor se verifica en la primera conexión. La línea de comandos te enseña cómo funcionan los túneles; un cliente evita que los reconstruyas a mano cada mañana.
Sea como sea, el modelo es todo el truco: decide qué lado abre el puerto, y el resto del flag se escribe solo.
Termalin es un cliente SSH gratis y multiplataforma con túneles integrados, SFTP y un gestor de claves — descárgalo, o mira cómo maneja tus claves de forma segura.