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 -V para 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:

TipoFlagDirecciónCaso de uso
Reenvío local-LMáquina local → destino remotoAcceder a una base de datos, interfaz web o servicio remoto
Reenvío remoto-RServidor remoto → máquina localExponer un servidor de desarrollo local, permitir acceso remoto a servicios locales
Reenvío dinámico-DProxy SOCKS local → cualquier destino vía remotoNavegació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ísticaSSH TunnelingWireGuard VPNCloudflare Tunnelngrok
CifradoSí (SSH)Sí (WireGuard)Sí (TLS)Sí (TLS)
Requiere software en el servidorsshd (ubicuo)Módulo kernel WireGuardDemonio cloudflaredAgente ngrok
Reenvío por puertoNo (red completa)
Proxy SOCKSSí (-D)NoNoNo
Sobrecarga de velocidadModerada (TCP sobre TCP)Baja (UDP, espacio kernel)BajaBaja
Reconexión persistenteCon autosshIntegradaIntegradaIntegrada
CosteGratuitoGratuitoNivel gratuito disponibleNivel gratuito disponible
Ideal paraTúneles ad-hoc rápidos, acceso a puertos específicosAcceso a red completa o sitio a sitioExponer servicios a InternetDemos 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 autossh para 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

Artículos Relacionados