Saltar al contenido
Luis ZambranaDevOps · Seguridad · Forense

Túnel SSH paso a paso: entrá a un servicio que solo escucha en 127.0.0.1

Cómo usar un túnel SSH (-L) para entrar a un panel, una base de datos o una copia de prueba que escucha solo en 127.0.0.1, sin abrir puertos en el firewall. Con Docker, ~/.ssh/config, PuTTY, los errores típicos y los cuidados.

Túnel SSH paso a paso: localhost:9102 de tu PC lleva a 127.0.0.1:9002 del servidor

En la entrada de Roundcube y PHP2Shell hubo un paso que pasó rápido pero que vale oro: probar una copia del webmail en el servidor de producción, con datos reales, sin publicarla en ningún lado. La copia escuchaba solo en 127.0.0.1 y se entraba por un túnel SSH.

Es una de esas herramientas que, una vez que la usás, aparece en todos lados: entrar a un panel de administración que no querés exponer, conectarte a una base de datos desde tu editor o probar algo nuevo sin abrir un solo puerto en el firewall. Acá va paso a paso, con todo probado en un laboratorio.

La idea en una línea

Un túnel SSH (local port forwarding) hace que un puerto de tu máquina se comporte como si fuera un puerto del servidor. Lo que mandás a localhost:9102 en tu PC viaja cifrado por la conexión SSH y sale del otro lado hacia 127.0.0.1:9002 del servidor.

Tu PC                                   Servidor
navegador ─► localhost:9102 ══ SSH (22) ══► 127.0.0.1:9002 (la app)

Para el servidor, el pedido viene de él mismo. Por eso llega a un servicio que no acepta conexiones de afuera.

Lo que necesitás

  • Acceso SSH al servidor (usuario y clave, o mejor, llave).
  • Un servicio escuchando en el servidor. Lo ideal: solo en 127.0.0.1.
  • En tu PC, el cliente ssh. Linux y macOS lo traen. Windows 10 y 11 también (OpenSSH viene incluido; se usa igual desde PowerShell).

Paso 1: que el servicio escuche solo en 127.0.0.1

Este es el punto que hace que todo sea seguro: si el servicio escucha solo en la interfaz local, desde Internet no existe.

Con Docker, eso se define en ports, poniendo la IP delante:

services:
  webmail-prueba:
    image: roundcube/roundcubemail:1.6.20-apache
    ports:
      - "127.0.0.1:9002:80"   # solo local
      # - "9002:80"           # NO: queda abierto en todas las interfaces

Ojo con esto: en Linux, Docker publica los puertos saltándose UFW (escribe sus propias reglas de iptables). Un "9002:80" queda abierto a Internet aunque UFW diga que está cerrado. Poner 127.0.0.1: adelante es la forma segura.

Si no es Docker, cada programa tiene su opción: bind-address = 127.0.0.1 en MySQL, listen_addresses = 'localhost' en PostgreSQL, Listen 127.0.0.1:9002 en Apache, --bind 127.0.0.1 en muchos servidores de desarrollo.

Comprobalo en el servidor:

ss -ltn | grep 9002
LISTEN 0  128  127.0.0.1:9002  0.0.0.0:*

Tiene que decir 127.0.0.1:9002. Si dice 0.0.0.0:9002 o *:9002, está abierto a todas las interfaces.

Y desde afuera no tiene que responder:

curl -m 3 http://IP-DEL-SERVIDOR:9002/
curl: (7) Failed to connect to ... port 9002 ... Could not connect to server

Paso 2: abrir el túnel

Desde tu PC:

ssh -L 9102:127.0.0.1:9002 usuario@servidor

Cómo se lee -L 9102:127.0.0.1:9002:

Parte Qué es
9102 El puerto que se abre en tu PC
127.0.0.1 A quién se conecta el servidor, visto desde el servidor
9002 El puerto de destino en el servidor

El 127.0.0.1 del medio es lo que más confunde: no es tu PC, es el servidor hablando consigo mismo. Ahí también podés poner otra máquina de la red interna del servidor (lo vemos más abajo).

Usé 9102 en vez de 9002 a propósito, para no confundir cuál es cuál. Puede ser cualquier puerto libre de tu PC por encima de 1024.

Paso 3: usarlo

Mientras esa sesión SSH esté abierta, en el navegador de tu PC:

http://localhost:9102

Y aparece la aplicación del servidor. En el laboratorio:

curl http://localhost:9102/
<h1>Webmail de prueba (solo 127.0.0.1)</h1>

En tu PC, el que escucha en ese puerto es el propio ssh, y solo en la interfaz local:

ss -ltnp | grep 9102
LISTEN 0  128  127.0.0.1:9102  0.0.0.0:*  users:(("ssh",pid=45787,fd=5))
LISTEN 0  128      [::1]:9102     [::]:*  users:(("ssh",pid=45787,fd=4))

Cerrás la sesión SSH y el túnel desaparece. No queda nada abierto.

Paso 4: túnel sin terminal (opcional)

Si no necesitás la consola del servidor y solo querés el túnel:

ssh -f -N -L 9102:127.0.0.1:9002 usuario@servidor
  • -N: no ejecuta nada en el servidor, solo el túnel.
  • -f: se va a segundo plano después de conectar (y de pedirte la clave, si hace falta).

Para cerrarlo:

pgrep -af "ssh .*-L 9102"     # ver cuál es
pkill -f "ssh .*-L 9102"      # cerrarlo

Varios túneles en una sola conexión

Se pueden poner varios -L. Por ejemplo, el webmail de prueba y su base de datos, para mirarla con DBeaver o psql desde tu PC:

ssh -N -L 9102:127.0.0.1:9002 -L 15432:127.0.0.1:5432 usuario@servidor
psql -h localhost -p 15432 -U roundcube roundcube

Llegar a una máquina que está detrás del servidor

El destino no tiene que ser el servidor mismo. Si el servidor ve una red interna, el túnel puede terminar en otra máquina de esa red:

ssh -L 8006:10.0.0.20:8006 usuario@servidor

Ahora https://localhost:8006 en tu PC es el panel de 10.0.0.20, una máquina que desde tu casa no se ve. El tramo entre el servidor y 10.0.0.20 no va por SSH: va como iría normalmente dentro de esa red.

Y si para llegar al servidor primero tenés que pasar por un bastión (jump host):

ssh -J usuario@bastion -L 9102:127.0.0.1:9002 usuario@servidor

Dejarlo guardado en ~/.ssh/config

Si lo vas a usar seguido, no hace falta recordar el comando. En ~/.ssh/config de tu PC:

Host webmail-prueba
    HostName servidor.ejemplo.com
    User usuario
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 9102 127.0.0.1:9002
    LocalForward 15432 127.0.0.1:5432
    ExitOnForwardFailure yes

Y después:

ssh -N webmail-prueba

ExitOnForwardFailure yes hace que, si el túnel no se puede abrir, ssh termine con error en vez de conectarse igual sin túnel (y dejarte pensando que anda).

Desde Windows con PuTTY

Si usás PuTTY en vez de la consola:

  1. Cargá la sesión del servidor como siempre.
  2. Andá a Connection → SSH → Tunnels.
  3. En Source port poné 9102 y en Destination 127.0.0.1:9002. Dejá marcado Local.
  4. Tocá Add. Tiene que aparecer L9102 127.0.0.1:9002 en la lista.
  5. Volvé a Session, guardá y conectá.

Con la sesión abierta, http://localhost:9102 en el navegador.

Los errores que vas a ver (y qué significan)

Todos estos los provoqué a propósito en el laboratorio.

El puerto de tu PC ya está ocupado:

bind [127.0.0.1]:9102: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 9102
Could not request local forwarding.

Otro programa (o un túnel anterior que quedó en segundo plano) ya usa el 9102. Cerralo o elegí otro puerto. Sin ExitOnForwardFailure, ssh se conecta igual y solo muestra este aviso.

El servicio del otro lado no está escuchando:

channel 2: open failed: connect failed: Connection refused

El túnel anda, pero en el servidor no hay nada en 127.0.0.1:9002. Revisá con ss -ltn en el servidor que la app esté levantada y en ese puerto.

El servidor no permite túneles:

channel 2: open failed: administratively prohibited: open failed

El sshd del servidor tiene AllowTcpForwarding no. Es una decisión del administrador: si el servidor es tuyo y lo necesitás, se habilita en /etc/ssh/sshd_config; si no es tuyo, hay que pedirlo.

La página carga, pero redirige a HTTPS o a otro dominio:

La aplicación está configurada para un nombre y protocolo de producción. En la copia de prueba, desactivá la redirección o ajustá la URL base. En el caso del webmail se usó un archivo de configuración extra que existía solo en la prueba.

Cuidados

  • El túnel abre el puerto solo en tu PC (127.0.0.1:9102). No lo cambies por -L 0.0.0.0:9102:...: eso lo comparte con toda tu red, y con cualquiera en el Wi-Fi del bar.
  • Usá llave, no contraseña, para entrar por SSH. Y si el servidor es tuyo, desactivá el login con contraseña.
  • Cerralo al terminar. Un túnel olvidado en segundo plano es una puerta abierta hacia el servidor desde tu PC.
  • En el servidor, se pueden permitir túneles solo a ciertos destinos con PermitOpen en sshd_config (por ejemplo, PermitOpen 127.0.0.1:9002). Útil si le das acceso a otra persona.
  • Al terminar la prueba, borrá lo que levantaste: en el caso del webmail, docker-compose -p prueba down -v, que elimina contenedores y volúmenes de la copia.

Los otros dos túneles, en una línea

  • -R (remote forwarding): al revés. Un puerto del servidor lleva a tu PC. Sirve, por ejemplo, para mostrarle a alguien algo que corre en tu máquina.
  • -D (dynamic): arma un proxy SOCKS. Configurás el navegador para usarlo y navegás como si estuvieras en el servidor, hacia cualquier destino.

Pero el -L es el del día a día. Con eso, el panel que no querías exponer, la base de datos y la copia de prueba quedan a un localhost de distancia, sin abrir nada al mundo.

Comentarios

Todavía no hay comentarios. ¡Sé el primero!

Seguí leyendo