Guarda cada túnel en ~/.ssh/config
Volver a escribir las opciones -L y -R cada vez es la forma más fácil de confundir los puertos. Mejor dale un nombre a cada túnel, con su propio bloque Host:
Host db-tunnel
HostName 203.0.113.10
User deploy
LocalForward 127.0.0.1:5432 127.0.0.1:5432
ExitOnForwardFailure yes
Host dev-share
HostName 203.0.113.10
User deploy
RemoteForward 127.0.0.1:9000 127.0.0.1:3000
Host socks
HostName 203.0.113.10
User deploy
DynamicForward 127.0.0.1:1080
Cada línea es la forma de configuración de una opción de la línea de comandos: LocalForward es -L, RemoteForward es -R y DynamicForward es -D. Primero van la dirección y el puerto de escucha, luego un espacio y luego el destino. Igual que con las opciones, el destino de un reenvío local lo resuelve el servidor, así que ahí 127.0.0.1 es el propio servidor. Si los tres tipos son nuevos para ti, empieza por los túneles SSH local, remoto y dinámico explicados.
Usa un nombre aparte para los túneles, no el Host con el que entras a diario. Si no, cada inicio de sesión intenta abrir los mismos puertos y el segundo muestra Address already in use.
Inicia un túnel por su nombre
ssh -f -N db-tunnel
-N no ejecuta ningún comando remoto, así que la conexión solo lleva los reenvíos. -f manda ssh a segundo plano después de iniciar sesión, por lo que la petición de frase de contraseña o contraseña te llega antes. Tu herramienta de base de datos ya puede conectarse a 127.0.0.1:5432.
Con ExitOnForwardFailure yes, un puerto ya ocupado detiene ssh con el código de salida 255 en lugar de dejar una conexión sin túnel. Esto es lo que pasa al iniciar el mismo túnel dos veces:
bind [127.0.0.1]:5432: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 5432
Could not request local forwarding.
Para iniciar varios túneles guardados a la vez:
for t in db-tunnel socks; do ssh -f -N "$t"; done
Detén un túnel
Un túnel en segundo plano no es más que un proceso ssh. Sin nada más, lo buscas y lo matas:
ps ax | grep '[s]sh -f -N db-tunnel'
pkill -f 'ssh -f -N db-tunnel'
pkill -f afecta a cualquier proceso cuya línea de comandos contenga ese texto. En nuestras pruebas también mató el script de shell que había iniciado el túnel, porque el script contenía las mismas palabras. Un socket de control es más limpio. Añade tres líneas al bloque:
Host db-tunnel
HostName 203.0.113.10
User deploy
LocalForward 127.0.0.1:5432 127.0.0.1:5432
ExitOnForwardFailure yes
ControlMaster auto
ControlPath ~/.ssh/cm-%C
ControlPersist yes
Ahora el túnel responde a comandos por su nombre:
ssh -f -N db-tunnel
ssh -O check db-tunnel
ssh -O exit db-tunnel
Master running (pid=27)
Exit request sent.
El socket también permite que otros comandos ssh y scp hacia db-tunnel reutilicen la misma conexión. Los detalles están en multiplexación SSH con ControlMaster.
Añade o quita un reenvío sin reconectar
Con un socket de control puedes cambiar un túnel en marcha. La conexión y sus otros reenvíos siguen activos:
ssh -O forward -L 127.0.0.1:8081:127.0.0.1:8000 db-tunnel
ssh -O cancel -L 127.0.0.1:8081:127.0.0.1:8000 db-tunnel
cancel necesita exactamente la misma especificación que usaste para añadir el reenvío. También funciona con -R y -D. Con -R 0:..., el servidor elige un puerto libre y ssh -O forward lo muestra.
Mantén activo un túnel guardado
Un túnel iniciado con -f dura lo que dura su conexión SSH. La suspensión, un cambio de Wi-Fi o un reinicio del servidor lo terminan. Añade estas líneas al bloque para que ssh detecte una conexión muerta en unos 45 segundos y salga, en lugar de quedarse colgado:
ServerAliveInterval 15
ServerAliveCountMax 3
Después algo tiene que volver a iniciarlo: un bucle, un gestor de servicios o autossh para un túnel SSH persistente.
Errores que rompen los túneles guardados
- Dos puntos en lugar de un espacio.
LocalForward 5432:127.0.0.1:5432es el formato de la línea de comandos, no el del archivo de configuración. ssh respondeMissing target argument, y en nuestra prueba esa única línea mal escrita impidió conectar a todos los hosts del archivo. - Escuchar en todas las interfaces.
LocalForward 0.0.0.0:5432 ...oGatewayPorts yesdejan que otras máquinas de tu red usen el túnel. Mantén127.0.0.1salvo que lo hagas a propósito. - Dos túneles en el mismo puerto local. Solo arranca el primero. Da a cada túnel guardado su propio puerto.
- Reenvío rechazado por el servidor. Si el puerto se abre pero las conexiones fallan con
administratively prohibited, el servidor tieneAllowTcpForwardingdesactivado para ti.
Preguntas
¿Cómo veo qué túneles SSH están activos?
Ejecuta ps ax | grep '[s]sh -f -N' para listar los procesos ssh en segundo plano con sus opciones. Para un túnel con socket de control, ssh -O check nombre responde directamente. Dentro de una sesión interactiva, pulsa Intro y escribe ~# para ver sus conexiones reenviadas.
¿Puedo iniciar varios túneles con un solo comando?
Sí. Pon varias líneas LocalForward o DynamicForward en un mismo bloque Host y se abren todas con un solo ssh -f -N nombre. Si un puerto está ocupado y ExitOnForwardFailure está activado, no queda abierta ninguna.
¿Por qué mi túnel guardado se para tras la suspensión o un cambio de red?
El túnel vive dentro de una conexión SSH, y esa conexión muere cuando cae la red. Añade ServerAliveInterval para que ssh lo detecte, y reinícialo con un bucle o con autossh.
Fuentes
- ssh(1), -f, -N y -O, manual de OpenBSD
- ssh_config(5), LocalForward, RemoteForward, DynamicForward, ControlMaster
- sshd_config(5), AllowTcpForwarding y GatewayPorts