El tunneling SSH en Linux crea canales cifrados que reenvían tráfico de red entre máquinas, permitiéndote acceder a servicios detrás de firewalls, cifrar protocolos que de otro modo no estarían protegidos y construir rutas seguras a través de redes no confiables. Ya sea que necesites llegar a una base de datos en una red privada, exponer un servidor de desarrollo local a un colega o enrutar el tráfico del navegador a través de un punto de salida de confianza, el port forwarding SSH maneja todo esto con herramientas ya instaladas en cualquier sistema Linux. Esta guía cubre los tres tipos de reenvío con configuraciones probadas en producción.
Requisitos Previos
- Dos máquinas Linux con acceso SSH (o un cliente Linux y cualquier servidor SSH)
- Cliente OpenSSH instalado localmente (
ssh -Vpara verificar) - Servidor OpenSSH en ejecución en la máquina remota (
sshd) - Conocimientos básicos de puertos TCP y redes cliente-servidor
- Autenticación por clave SSH configurada (recomendado — ver artículo relacionado)
Tipos de Túneles SSH
SSH soporta tres modos de reenvío distintos. Cada uno responde a un caso de uso diferente:
| Tipo | Flag | Dirección | Caso de uso |
|---|---|---|---|
| Reenvío local | -L | Máquina local → destino remoto | Acceder a una base de datos, interfaz web o servicio remoto |
| Reenvío remoto | -R | Servidor remoto → máquina local | Exponer un servidor de desarrollo local, permitir acceso remoto a servicios locales |
| Reenvío dinámico | -D | Proxy SOCKS local → cualquier destino vía remoto | Navegación segura, enrutar todo el tráfico por SSH |
Los tres cifran el tráfico de extremo a extremo entre tu cliente SSH y el servidor SSH. El tráfico entre el servidor SSH y el destino final no está cifrado por SSH (viaja por la red local del servidor).
Reenvío de Puertos Local (-L)
El reenvío local es el tipo más común. Vincula un puerto en tu máquina local y envía el tráfico a través del túnel SSH hacia un destino específico accesible desde el servidor remoto.
Sintaxis
ssh -L [bind_address:]local_port:destination:dest_port user@ssh_server
Ejemplo: Acceder a una Base de Datos Remota
Tienes una base de datos PostgreSQL en db.internal en el puerto 5432 que solo es accesible desde el servidor de aplicaciones app.example.com. Tu laptop no puede llegar a db.internal directamente.
# Reenvía el puerto local 5432 a db.internal:5432 a través de app.example.com
ssh -L 5432:db.internal:5432 admin@app.example.com
Ahora conéctate a la base de datos localmente:
psql -h localhost -p 5432 -U myuser mydb
La ruta del tráfico: laptop:5432 → túnel SSH → app.example.com → db.internal:5432
Ejemplo: Acceder a una Interfaz Web Remota
Un panel de monitoreo se ejecuta en http://monitor.internal:3000 detrás de un firewall:
# Reenvía el puerto local 8080 al panel remoto
ssh -L 8080:monitor.internal:3000 admin@jumpbox.example.com
# Abre en tu navegador
# http://localhost:8080
Túnel en Segundo Plano (Sin Shell)
Agrega -f -N para ejecutar el túnel en segundo plano sin abrir una shell remota:
# -f = ejecutar en segundo plano tras la autenticación
# -N = sin comando remoto (solo túnel)
ssh -f -N -L 8080:monitor.internal:3000 admin@jumpbox.example.com
Para cerrar un túnel en segundo plano, busca y mata el proceso:
ps aux | grep 'ssh -f -N -L'
kill <PID>
Vincular a Todas las Interfaces
Por defecto, -L se vincula solo a localhost. Para permitir que otras máquinas de tu red usen el túnel:
# Vincular a todas las interfaces (0.0.0.0)
ssh -L 0.0.0.0:8080:monitor.internal:3000 admin@jumpbox.example.com
# O vincular a una interfaz específica
ssh -L 192.168.1.100:8080:monitor.internal:3000 admin@jumpbox.example.com
Advertencia de seguridad: Vincular a 0.0.0.0 expone el túnel a toda tu red local. Hazlo solo en redes de confianza.
Reenvío de Puertos Remoto (-R)
El reenvío remoto funciona en sentido contrario: vincula un puerto en el servidor remoto y envía el tráfico de regreso a tu máquina local.
Sintaxis
ssh -R [bind_address:]remote_port:destination:dest_port user@ssh_server
Ejemplo: Exponer un Servidor de Desarrollo Local
Estás desarrollando una aplicación web en localhost:3000 y quieres que un colega en el servidor remoto pueda acceder a ella:
# Vincula el puerto 9000 en el servidor remoto, reenviando a tu puerto local 3000
ssh -R 9000:localhost:3000 admin@remote.example.com
Ahora cualquiera en remote.example.com puede acceder a tu servidor de desarrollo local en http://localhost:9000.
Ejemplo: Acceso Remoto a un Servicio Local
Tu máquina de la oficina ejecuta un servicio en el puerto 8443 y quieres acceder a él desde casa a través de un servidor en la nube:
# Desde la máquina de la oficina:
ssh -R 8443:localhost:8443 user@cloud-server.example.com
Desde casa, conéctate por SSH al servidor en la nube y accede a localhost:8443.
Permitir Conexiones Externas
Por defecto, los puertos de reenvío remoto se vinculan a localhost en el servidor remoto. Para permitir acceso externo, el servidor SSH debe tener GatewayPorts habilitado:
# En el servidor SSH, edita /etc/ssh/sshd_config:
GatewayPorts yes # Permite vincular a todas las interfaces
# o
GatewayPorts clientspecified # Deja que el cliente elija la dirección de enlace
Luego reinicia sshd y especifica la dirección de enlace:
ssh -R 0.0.0.0:9000:localhost:3000 admin@remote.example.com
Reenvío Dinámico (-D) — Proxy SOCKS
El reenvío dinámico crea un proxy SOCKS5 local. Cualquier aplicación configurada para usar este proxy enrutará todo su tráfico a través del túnel SSH, con el servidor remoto actuando como punto de salida.
Sintaxis
ssh -D [bind_address:]port user@ssh_server
Ejemplo: Navegación Web Segura
Estás en una red Wi-Fi no confiable y quieres enrutar todo el tráfico del navegador a través de tu servidor doméstico:
# Crea un proxy SOCKS5 en el puerto local 1080
ssh -D 1080 -f -N user@home-server.example.com
Luego configura tu navegador:
- Firefox: Configuración → Configuración de red → Proxy manual → Host SOCKS:
localhost, Puerto:1080, SOCKS v5. Marca “Usar DNS remoto cuando se utilice SOCKS v5”. - Chrome (línea de comandos):
google-chrome --proxy-server="socks5://localhost:1080" - Global (variable de entorno):
export ALL_PROXY=socks5://localhost:1080
Ejemplo: Enrutar Herramientas de Línea de Comandos a través del Proxy
# Usar con curl
curl --proxy socks5h://localhost:1080 https://ifconfig.me
# Usar con git
git -c http.proxy=socks5h://localhost:1080 clone https://github.com/user/repo.git
# La 'h' en socks5h significa que la resolución DNS ocurre en el lado remoto
Importante: Usa socks5h:// (con la h) para resolver DNS a través del proxy. El prefijo socks5:// sin la h resuelve DNS localmente, lo que filtra tus consultas DNS en la red local.
Túneles Persistentes con autossh
Los túneles SSH se interrumpen cuando la conexión se cae (corte de red, suspensión del laptop, reinicio del servidor). autossh monitorea la conexión y se reconecta automáticamente.
Instalación
# Debian / Ubuntu
sudo apt install autossh
# RHEL / Fedora
sudo dnf install autossh
# Arch Linux
sudo pacman -S autossh
Uso
# Reenvío local persistente con autossh
autossh -M 0 -f -N -L 5432:db.internal:5432 admin@jumpbox.example.com
# Proxy SOCKS persistente
autossh -M 0 -f -N -D 1080 user@home-server.example.com
# Reenvío remoto persistente
autossh -M 0 -f -N -R 9000:localhost:3000 admin@remote.example.com
El flag -M 0 desactiva el puerto de monitoreo propio de autossh y delega en el mecanismo keepalive de SSH (más fiable y sencillo). Combínalo con los keepalives de SSH en tu configuración.
Ejecutar como Servicio systemd
Para túneles que deben sobrevivir a reinicios, crea una unidad systemd:
# /etc/systemd/system/ssh-tunnel-db.service
[Unit]
Description=SSH Tunnel to Database
After=network-online.target
Wants=network-online.target
[Service]
User=tunneluser
ExecStart=/usr/bin/autossh -M 0 -N -L 5432:db.internal:5432 admin@jumpbox.example.com
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now ssh-tunnel-db.service
sudo systemctl status ssh-tunnel-db.service
Configuración SSH para Túneles Reutilizables
En lugar de escribir comandos largos cada vez, define los túneles en ~/.ssh/config:
Host db-tunnel
HostName jumpbox.example.com
User admin
LocalForward 5432 db.internal:5432
ServerAliveInterval 60
ServerAliveCountMax 3
IdentityFile ~/.ssh/id_ed25519
Host socks-proxy
HostName home-server.example.com
User myuser
DynamicForward 1080
ServerAliveInterval 60
ServerAliveCountMax 3
IdentityFile ~/.ssh/id_ed25519
Host expose-dev
HostName remote.example.com
User admin
RemoteForward 9000 localhost:3000
ServerAliveInterval 60
ServerAliveCountMax 3
Ahora úsalos por nombre:
ssh -f -N db-tunnel
ssh -f -N socks-proxy
ssh -f -N expose-dev
Comparativa: SSH Tunneling frente a Alternativas
| Característica | SSH Tunneling | WireGuard VPN | Cloudflare Tunnel | ngrok |
|---|---|---|---|---|
| Cifrado | Sí (SSH) | Sí (WireGuard) | Sí (TLS) | Sí (TLS) |
| Requiere software en el servidor | sshd (ubicuo) | Módulo kernel WireGuard | Demonio cloudflared | Agente ngrok |
| Reenvío por puerto | Sí | No (red completa) | Sí | Sí |
| Proxy SOCKS | Sí (-D) | No | No | No |
| Sobrecarga de velocidad | Moderada (TCP sobre TCP) | Baja (UDP, espacio kernel) | Baja | Baja |
| Reconexión persistente | Con autossh | Integrada | Integrada | Integrada |
| Coste | Gratuito | Gratuito | Nivel gratuito disponible | Nivel gratuito disponible |
| Ideal para | Túneles ad-hoc rápidos, acceso a puertos específicos | Acceso a red completa o sitio a sitio | Exponer servicios a Internet | Demos rápidas, webhooks |
Usa SSH tunneling cuando necesites acceder rápidamente a uno o dos puertos específicos y ya tienes acceso SSH: no hace falta software adicional. Cambia a WireGuard cuando necesites acceso completo a la red o alto rendimiento. Usa Cloudflare Tunnels o ngrok cuando necesites exponer servicios a usuarios externos con URLs públicas.
Solución de Problemas
El túnel conecta pero no fluye tráfico
# Verifica que el túnel está escuchando
ss -tlnp | grep <local_port>
# Comprueba si el destino es accesible desde el servidor SSH
ssh admin@jumpbox.example.com "nc -zv db.internal 5432"
Si ss muestra el puerto en escucha pero las conexiones se quedan bloqueadas, el servicio de destino puede estar rechazando conexiones desde la IP del servidor SSH. Revisa las reglas del firewall en el host de destino.
”bind: Address already in use"
# Busca qué está usando el puerto
sudo ss -tlnp | grep <port>
# Mata el túnel anterior o elige un puerto local diferente
ssh -L 15432:db.internal:5432 admin@jumpbox.example.com
"channel 3: open failed: administratively prohibited”
El servidor SSH tiene el reenvío TCP deshabilitado. El administrador del servidor debe configurar en /etc/ssh/sshd_config:
AllowTcpForwarding yes
# o solo para reenvío local:
AllowTcpForwarding local
El túnel se desconecta tras un período de inactividad
Agrega keepalives a tu ~/.ssh/config:
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Esto envía un keepalive cada 60 segundos y desconecta tras 3 respuestas fallidas (3 minutos). Evita que los dispositivos NAT y los firewalls con estado cierren la conexión TCP inactiva.
El puerto de reenvío remoto no es accesible externamente
Comprueba que GatewayPorts está habilitado en el servidor SSH:
grep GatewayPorts /etc/ssh/sshd_config
Si dice no o no aparece, los puertos de reenvío remoto solo se vinculan a 127.0.0.1 en el servidor.
Consideraciones de Seguridad
-
Restringe el reenvío en authorized_keys: Limita lo que puede hacer una clave específica:
no-agent-forwarding,no-X11-forwarding,permitopen="db.internal:5432" ssh-ed25519 AAAA...Esta clave solo puede crear túneles hacia
db.internal:5432. -
Deshabilita el reenvío para usuarios específicos: En
/etc/ssh/sshd_config:Match User restricteduser AllowTcpForwarding no -
Rendimiento TCP sobre TCP: Los túneles SSH sufren el problema de TCP sobre TCP: las pilas TCP interna y externa gestionan las retransmisiones de forma independiente, lo que puede provocar un backoff exponencial en conexiones con pérdidas. Para tráfico de alto rendimiento o sensible a la latencia, usa WireGuard.
-
Audita el uso de túneles: Monitorea los túneles activos en tus servidores:
# Muestra todos los port forwards SSH activos sudo ss -tlnp | grep sshd # Muestra sesiones SSH conectadas con reenvío sudo lsof -i -n | grep ssh | grep LISTEN
Resumen
- Reenvío local (
-L) trae servicios remotos a tu máquina local — el caso de uso más habitual para acceder a bases de datos, paneles y APIs detrás de firewalls - Reenvío remoto (
-R) expone servicios locales en un servidor remoto — útil para compartir entornos de desarrollo y proporcionar acceso externo a herramientas internas - Reenvío dinámico (
-D) crea un proxy SOCKS5 — enruta el tráfico de cualquier aplicación a través del túnel SSH para navegar de forma segura en redes no confiables - Usa
autosshpara túneles persistentes que sobrevivan a interrupciones de red, y servicios systemd para túneles que deban sobrevivir a reinicios - Los archivos de configuración SSH (
~/.ssh/config) eliminan los flags repetitivos en línea de comandos y hacen que los túneles sean reutilizables con nombres simples - Agrega keepalives (
ServerAliveInterval 60) para evitar que los firewalls cierren conexiones de túnel inactivas