--- title: "Cómo crear túneles SSH: reenvío local, remoto y SOCKS" description: "Aprende a crear túneles SSH con reenvío de puertos local, remoto y dinámico (SOCKS), guardarlos en ~/.ssh/config y mantenerlos activos con autossh y systemd." category: "Configuración de Red" language: es source: https://cubepath.com/es/docs/configuración-de-red/configuracion-de-tunel-ssh-port-forwarding updated: 2026-09-25 --- # Cómo crear túneles SSH: reenvío local, remoto y SOCKS Un túnel SSH reenvía un puerto TCP a través de una conexión SSH cifrada. Sirve para llegar a servicios que no están expuestos a Internet (una base de datos que solo escucha en `localhost`), publicar temporalmente un servicio de tu equipo en un servidor remoto o navegar a través de un servidor como si fuera un proxy. En este tutorial usarás los tres tipos de reenvío (local, remoto y dinámico), los guardarás en `~/.ssh/config` y dejarás un túnel permanente con `autossh` y systemd en Ubuntu 24.04. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS accesible por SSH, por ejemplo un VPS de CubePath. En los ejemplos se llama `your_server_ip`. - Un usuario no root con privilegios `sudo` en ese servidor (`your_user`) y acceso con clave SSH. - Un equipo local con cliente OpenSSH (Linux, macOS o Windows 10/11 con OpenSSH). - Para los ejemplos de base de datos, un servicio escuchando solo en local en el servidor, como PostgreSQL en `127.0.0.1:5432`. Puedes sustituirlo por cualquier otro servicio TCP. Los tres tipos de túnel que verás: | Tipo | Opción | Dirección | Uso típico | |------|--------|-----------|------------| | Local | `-L` | Tu equipo → servidor → destino | Acceder a una base de datos o panel interno | | Remoto | `-R` | Servidor → tu equipo | Enseñar un servicio local desde el servidor | | Dinámico | `-D` | Tu equipo → cualquier destino vía servidor | Proxy SOCKS para el navegador o `curl` | ## Paso 1: Comprobar que el servidor permite el reenvío El reenvío de puertos lo controla el demonio `sshd` del servidor. En Ubuntu está permitido por defecto, pero conviene comprobarlo antes de buscar errores en otro sitio. Conéctate al servidor y muestra la configuración efectiva: ```bash sudo sshd -T | grep -Ei 'allowtcpforwarding|gatewayports|permitopen' ``` ```output gatewayports no allowtcpforwarding yes permitopen any ``` - `allowtcpforwarding yes`: se permiten los túneles. Si ves `no`, algún archivo lo está desactivando. - `gatewayports no`: los puertos de un túnel remoto (`-R`) solo escuchan en `127.0.0.1` del servidor. Lo cambiarás en el paso 4 si lo necesitas. Si `allowtcpforwarding` aparece como `no`, busca dónde se define: ```bash sudo grep -ri 'AllowTcpForwarding' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/ ``` Cambia el valor a `yes` en ese archivo, valida la sintaxis y reinicia el servicio: ```bash sudo sshd -t && sudo systemctl restart ssh ``` `sshd -t` no muestra nada si la configuración es correcta. ## Paso 2: Crear un túnel local (-L) Un túnel local abre un puerto en tu equipo y reenvía todo lo que llega a él hacia un destino visto desde el servidor. La sintaxis es: ```text ssh -L [dirección_local:]puerto_local:host_destino:puerto_destino usuario@servidor ``` El `host_destino` se resuelve **en el servidor**, así que `localhost` significa el propio servidor. Para llegar a PostgreSQL, que en el servidor solo escucha en `127.0.0.1:5432`, ejecuta en tu equipo local: ```bash ssh -N -L 5433:localhost:5432 your_user@your_server_ip ``` - `-N` indica que no quieres ejecutar ningún comando remoto, solo mantener el túnel. - Se usa el puerto local `5433` para no chocar con un PostgreSQL que tengas instalado en tu equipo. El comando se queda en primer plano mientras el túnel esté abierto. En otra terminal, comprueba que el puerto escucha: ```bash ss -tln | grep 5433 ``` ```output LISTEN 0 128 127.0.0.1:5433 0.0.0.0:* LISTEN 0 128 [::1]:5433 [::]:* ``` Ahora conéctate como si la base de datos fuera local: ```bash psql -h 127.0.0.1 -p 5433 -U your_db_user your_db ``` Por defecto el puerto local solo escucha en `127.0.0.1`, que es lo que quieres casi siempre. Si otras máquinas de tu red deben usar el túnel, indica la dirección de escucha explícitamente (por ejemplo `-L 0.0.0.0:5433:localhost:5432`), sabiendo que cualquiera que llegue a ese puerto accederá al servicio. Cierra el túnel con `Ctrl+C`. ### Reenviar varios puertos en una conexión Puedes repetir `-L` tantas veces como necesites: ```bash ssh -N -L 5433:localhost:5432 -L 6380:localhost:6379 your_user@your_server_ip ``` ### Llegar a un host interno a través de un servidor de salto El destino no tiene que ser el propio servidor. Si `10.0.0.20` es una base de datos en la red privada del servidor, el servidor hace de intermediario: ```bash ssh -N -L 5433:10.0.0.20:5432 your_user@your_server_ip ``` El tráfico va cifrado de tu equipo al servidor; el último tramo (servidor → `10.0.0.20`) viaja sin el cifrado de SSH por la red privada. ## Paso 3: Ejecutar el túnel en segundo plano Para no ocupar una terminal, añade `-f` para que SSH pase a segundo plano tras autenticarse, y `ExitOnForwardFailure` para que falle si el puerto local ya está ocupado en lugar de quedarse conectado sin túnel: ```bash ssh -f -N -o ExitOnForwardFailure=yes -L 5433:localhost:5432 your_user@your_server_ip ``` Localiza el proceso para cerrarlo más tarde: ```bash pgrep -af 'ssh -f -N' ``` ```output 48211 ssh -f -N -o ExitOnForwardFailure=yes -L 5433:localhost:5432 your_user@your_server_ip ``` ```bash kill 48211 ``` ## Paso 4: Crear un túnel remoto (-R) Un túnel remoto hace lo contrario: abre un puerto **en el servidor** y lo reenvía a un destino visto desde tu equipo. Es útil para enseñar una aplicación que corre en tu portátil (por ejemplo en `localhost:3000`) o para dar acceso a una máquina que está detrás de NAT. Desde tu equipo local: ```bash ssh -N -R 8080:localhost:3000 your_user@your_server_ip ``` En el servidor, el puerto `8080` escucha solo en local porque `GatewayPorts` vale `no`: ```bash ss -tln | grep 8080 ``` ```output LISTEN 0 128 127.0.0.1:8080 0.0.0.0:* ``` Desde el propio servidor ya puedes probarlo con `curl http://127.0.0.1:8080`. Si quieres que el puerto sea accesible desde Internet, crea un archivo de configuración en el servidor: ```bash sudo nano /etc/ssh/sshd_config.d/10-gatewayports.conf ``` ```text GatewayPorts clientspecified ``` `clientspecified` deja que el cliente elija la dirección de escucha, en lugar de abrirla siempre a todas las interfaces como haría `yes`. Valida y reinicia: ```bash sudo sshd -t && sudo systemctl restart ssh ``` Abre el puerto en el firewall del servidor si usas UFW: ```bash sudo ufw allow 8080/tcp ``` Vuelve a crear el túnel indicando la dirección pública de escucha: ```bash ssh -N -R 0.0.0.0:8080:localhost:3000 your_user@your_server_ip ``` Ahora `http://your_server_ip:8080` muestra la aplicación de tu equipo. Cuando termines, cierra el túnel y elimina la regla con `sudo ufw delete allow 8080/tcp`. > Advertencia: un puerto remoto abierto a Internet expone directamente el servicio de tu equipo. Úsalo solo de forma temporal o con un proxy inverso con TLS y autenticación delante. ## Paso 5: Crear un proxy SOCKS (-D) El reenvío dinámico abre en tu equipo un proxy SOCKS5. Cada aplicación que lo use decide a qué destino conectarse y el servidor hace las conexiones por ella. Sirve para navegar con la IP del servidor o proteger tu tráfico en una wifi pública. ```bash ssh -N -D 1080 your_user@your_server_ip ``` Compruébalo consultando tu IP pública a través del proxy. Deberías ver la IP del servidor, no la tuya: ```bash curl --socks5-hostname 127.0.0.1:1080 https://api.ipify.org ``` ```output your_server_ip ``` `--socks5-hostname` hace que también la resolución DNS se haga en el servidor. En Firefox, configura el proxy en **Ajustes > Configuración de red** como SOCKS v5, host `127.0.0.1`, puerto `1080`, y marca **Usar proxy DNS cuando se utilice SOCKS v5**. ## Paso 6: Guardar los túneles en ~/.ssh/config Escribir las opciones cada vez es propenso a errores. Define los túneles en el archivo de configuración del cliente, en tu equipo local: ```bash nano ~/.ssh/config ``` ```text Host db-tunnel HostName your_server_ip User your_user IdentityFile ~/.ssh/id_ed25519 LocalForward 5433 localhost:5432 LocalForward 6380 localhost:6379 ExitOnForwardFailure yes ServerAliveInterval 30 ServerAliveCountMax 3 Host socks-proxy HostName your_server_ip User your_user DynamicForward 1080 Host internal-db HostName 10.0.0.20 User your_user ProxyJump your_user@your_server_ip ``` - `ServerAliveInterval` y `ServerAliveCountMax` cierran la conexión si el servidor deja de responder durante unos 90 segundos, en lugar de dejar un túnel colgado. - `ProxyJump` permite abrir una sesión SSH directamente en un host interno pasando por el servidor, sin túnel manual. Equivale a `ssh -J`. Ahora abrir un túnel es tan corto como: ```bash ssh -N db-tunnel ``` Y la conexión a un host interno: ```bash ssh internal-db ``` ## Paso 7: Mantener un túnel permanente con autossh y systemd Para un túnel que debe estar siempre activo (por ejemplo, una aplicación que lee de una base de datos remota), usa `autossh`, que vuelve a lanzar SSH si la conexión se cae, y gestiónalo con systemd para que arranque con el sistema. Estos pasos se hacen en la máquina que abre el túnel, que aquí es un equipo con Ubuntu 24.04. Instala autossh: ```bash sudo apt update sudo apt install autossh ``` El servicio se ejecutará sin interacción, así que necesita una clave sin frase de paso y el host ya presente en `known_hosts`. Crea una clave dedicada para el túnel: ```bash ssh-keygen -t ed25519 -f ~/.ssh/tunnel_ed25519 -N '' -C 'autossh tunnel' ssh-copy-id -i ~/.ssh/tunnel_ed25519.pub your_user@your_server_ip ``` Conéctate una vez con esa clave para aceptar la huella del servidor y confirmar que funciona: ```bash ssh -i ~/.ssh/tunnel_ed25519 your_user@your_server_ip exit ``` Crea la unidad de systemd: ```bash sudo nano /etc/systemd/system/ssh-tunnel-db.service ``` ```ini [Unit] Description=Túnel SSH a PostgreSQL en your_server_ip After=network-online.target Wants=network-online.target [Service] User=your_user Environment=AUTOSSH_GATETIME=0 ExecStart=/usr/bin/autossh -M 0 -N \ -o ServerAliveInterval=30 \ -o ServerAliveCountMax=3 \ -o ExitOnForwardFailure=yes \ -i /home/your_user/.ssh/tunnel_ed25519 \ -L 5433:localhost:5432 \ your_user@your_server_ip Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` - `-M 0` desactiva el puerto de monitorización propio de autossh y delega la detección de caídas en `ServerAliveInterval`, que es la forma recomendada hoy. - `AUTOSSH_GATETIME=0` hace que autossh reintente aunque el primer intento falle, algo habitual justo después del arranque. Carga la unidad y arráncala: ```bash sudo systemctl daemon-reload sudo systemctl enable --now ssh-tunnel-db.service ``` Comprueba su estado y que el puerto escucha: ```bash systemctl status ssh-tunnel-db.service --no-pager ss -tln | grep 5433 ``` ```output ● ssh-tunnel-db.service - Túnel SSH a PostgreSQL en your_server_ip Loaded: loaded (/etc/systemd/system/ssh-tunnel-db.service; enabled; preset: enabled) Active: active (running) since Thu 2026-09-24 10:12:03 UTC; 8s ago LISTEN 0 128 127.0.0.1:5433 0.0.0.0:* ``` Para limitar lo que puede hacer esa clave en el servidor, añade restricciones delante de ella en `~/.ssh/authorized_keys` del servidor: ```text restrict,port-forwarding,permitopen="localhost:5432" ssh-ed25519 AAAA... autossh tunnel ``` Con esto la clave no puede abrir una shell ni reenviar a ningún destino que no sea `localhost:5432`. ## Solución de problemas **`bind [127.0.0.1]:5433: Address already in use`**: el puerto local ya está ocupado, a menudo por un túnel anterior que sigue en segundo plano. Localízalo con `ss -tlnp | grep 5433` y ciérralo, o usa otro puerto. **`channel 2: open failed: administratively prohibited`**: el servidor rechaza el reenvío. Revisa `AllowTcpForwarding` (paso 1) y, si usas `permitopen` en `authorized_keys`, que el destino coincida exactamente. **`channel 2: open failed: connect failed: Connection refused`**: el túnel funciona, pero en el destino no hay nada escuchando en ese puerto. Compruébalo en el servidor con `sudo ss -tlnp | grep 5432`. **El túnel remoto solo responde desde el servidor**: `GatewayPorts` sigue en `no` o no has indicado `0.0.0.0` en `-R`. Revisa el paso 4 y el firewall. **El servicio de autossh se reinicia en bucle**: revisa `journalctl -u ssh-tunnel-db -n 50 --no-pager`. Lo más habitual es `Host key verification failed` (no aceptaste la huella con ese usuario) o permisos incorrectos en la clave (`chmod 600 ~/.ssh/tunnel_ed25519`). Para ver en detalle qué hace el cliente, añade `-v` a cualquier comando `ssh`. ## Conclusión Has creado túneles locales para llegar a servicios internos, túneles remotos para publicar un servicio de tu equipo, un proxy SOCKS y un túnel permanente gestionado por systemd con una clave restringida. Los túneles SSH son ideales para accesos puntuales o para unos pocos servicios concretos. Como siguientes pasos puedes: - Usar `sshuttle` si necesitas enrutar subredes completas a través de SSH sin configurar cada puerto. - Montar una VPN con WireGuard cuando varios equipos necesiten acceso permanente a una red privada. - Reforzar el servidor SSH desactivando el acceso por contraseña y el login de root.