- La tunelización SSH transporta tráfico TCP sobre una conexión SSH segura, y se usa para abrir de forma segura rutas restringidas, como cifrar protocolos heredados, acceder a paneles de administración o conectarse a servidores detrás de NAT
- En el lado del servidor se necesita
AllowTcpForwarding yes, y para abrir puertos en interfaces que no sean loopback hace falta configurar GatewayPorts yes y reiniciar el servidor SSH
ssh -J, ssh -L, ssh -R, ssh -D se encargan respectivamente de jump hosts, reenvío local, reenvío remoto y reenvío dinámico basado en SOCKS5
- Para mantener un túnel en segundo plano se usa
ssh -fN, y la detección de cortes se ajusta con configuraciones de heartbeat como ClientAliveInterval y ClientAliveCountMax
- La tunelización SSH no admite UDP de forma directa y puede sufrir problemas de latencia y rendimiento en TCP-over-TCP, por lo que se parece más a una herramienta para abrir rutas TCP específicas que a un reemplazo de VPN
Cuándo usar tunelización SSH
- La tunelización SSH y el reenvío de puertos permiten transportar tráfico TCP desde el cliente al servidor, o del servidor al cliente, a través de una conexión SSH
- Se pueden usar puertos TCP y sockets UNIX, pero los ejemplos se centran en puertos TCP
- Los usos más representativos se dividen en seguridad, solución de problemas y conectividad
- Seguridad
- Cifra conexiones inseguras o protocolos heredados como FTP
- Permite acceder a paneles web de administración mediante túneles SSH basados en autenticación por clave pública
- Se puede abrir solo el puerto 22 sin exponer puertos adicionales como 80/443
- Solución de problemas
- Permite evadir firewalls o filtros de contenido
- Permite elegir otra ruta de red
- Conectividad
- Permite acceder a servidores detrás de NAT
- Permite entrar desde Internet a un servidor interno a través de un jump host
- Permite exponer un puerto local a Internet
Configuración a revisar antes del reenvío de puertos
- Las opciones de los ejemplos se pueden combinar según el entorno
- Si no se especifica
bind_address, el valor predeterminado es localhost
- El usuario local y el remoto necesitan permisos para abrir puertos en cada máquina
- Los puertos
0-1024 requieren privilegios de root salvo que exista una configuración adicional
- Los demás puertos pueden ser configurados por usuarios normales
- El cliente y los firewalls de red también deben estar abiertos de acuerdo con la ruta del reenvío
- En el servidor SSH, el reenvío de puertos debe estar habilitado
AllowTcpForwarding yes
- En muchos casos ya viene habilitado por defecto, pero hay que verificar la configuración del servidor
- Para reenviar puertos a una interfaz distinta de
127.0.0.1, hay que habilitar GatewayPorts en el servidor SSH
GatewayPorts yes
- Después de cambiar la configuración, hay que reiniciar el servicio del servidor SSH
SSH jump host y túneles multinivel
ssh -J se usa para conectarse de forma transparente a un host remoto pasando por uno o más hosts intermedios
ssh -J user@REMOTE-MACHINE:22 -p 22 user@10.99.99.1
- Si se usa el puerto predeterminado
22, se puede omitir la indicación del puerto
- Si se usa REMOTE-MACHINE como jump host, la conexión se puede verificar así
[user@REMOTE-MACHINE]$ ss | grep -i ssh
tcp ESTAB 0 0 167.135.173.108:ssh 192.160.140.207:45960
tcp ESTAB 0 0 10.99.99.2:49770 10.99.99.1:ssh
- El papel de las direcciones de ejemplo es el siguiente
167.135.173.108: IP pública de REMOTE-MACHINE
192.160.140.207: IP pública de LOCAL-MACHINE
10.99.99.2: IP interna de REMOTE-MACHINE
10.99.99.1: IP interna de REMOTE-WEBAPP
- Cuando se usan varios jump hosts, se separan con comas
ssh -J user@REMOTE-MACHINE:22,user@ANOTHER-REMOTE-MACHINE:22 -p 22 user@10.99.99.1
Reenvío local de puertos
- El reenvío local de puertos usa la opción
ssh -L
- El primer ejemplo reenvía
10.10.10.1:8001 de LOCAL-MACHINE a localhost:8000 de REMOTE-MACHINE
ssh -L 10.10.10.1:8001:localhost:8000 user@REMOTE-MACHINE
- En REMOTE-MACHINE, el log de acceso del servidor web enlazado solo a
127.0.0.1 se ve así
127.0.0.1 - - [30/Dec/2022 18:05:15] "GET / HTTP/1.1" 200
- Esta solicitud se origina en LOCAL-MACHINE
- El segundo ejemplo reenvía el puerto local
8001 a 10.99.99.1:8000 a través de REMOTE-MACHINE
ssh -L 8001:10.99.99.1:8000 user@REMOTE-MACHINE
- En el log de acceso del servidor web de REMOTE-WEBAPP, la solicitud queda registrada como proveniente de la IP interna de REMOTE-MACHINE
10.99.99.2 - - [30/Dec/2022 21:28:42] "GET / HTTP/1.1" 200
Reenvío remoto de puertos
- El reenvío remoto de puertos usa la opción
ssh -R
- El ejemplo reenvía el puerto
8000 de REMOTE-MACHINE hacia localhost:8001 o 10.10.10.2:8001 del lado local
ssh -R 8000:localhost:8001 user@REMOTE-MACHINE
ssh -R 8000:10.10.10.2:8001 user@REMOTE-MACHINE
- También se puede configurar para escuchar en una interfaz remota específica,
10.99.99.2:8000
ssh -R 10.99.99.2:8000:10.10.10.2:8001 user@REMOTE-MACHINE
- Para escuchar fuera de la interfaz loopback, debe estar habilitado
GatewayPorts yes en el servidor SSH
Reenvío dinámico de puertos y SOCKS5
- Cuando se reenvían varios puertos, SSH usa el protocolo SOCKS
- SOCKS es un protocolo de proxy transparente, y SSH usa la versión más reciente, SOCKS5
- El puerto predeterminado del servidor SOCKS5 es
1080, definido en RFC 1928
- El cliente debe configurarse para usar un proxy SOCKS en la capa de aplicación o en la capa del sistema operativo
- El ejemplo con
ssh -D abre un proxy SOCKS en 10.10.10.1:5555
ssh -D 10.10.10.1:5555 user@REMOTE-MACHINE
- En el cliente LOCAL se puede probar la ruta de conexión con
curl
curl -L -x socks5://10.10.10.1:5555 brrl.net/ip
- Si funciona correctamente, devolverá la IP pública de REMOTE-MACHINE
Tunelización SSH TUN/TAP
- Con la bandera
-w se puede crear un túnel bidireccional
- La interfaz debe haberse creado de antemano
- Se mantiene la advertencia de que este método no fue probado
-w local_tun[:remote_tun]
Ejecución en segundo plano y finalización
- Para ejecutar un túnel en segundo plano de forma nativa se usa
-fN
-f: ejecutar en segundo plano
-N: no abrir un shell
ssh -fN -L 8001:127.0.0.1:8000 user@REMOTE-MACHINE
- También se puede usar
screen u otras herramientas
- Para cerrar un SSH que se está ejecutando en segundo plano, se busca el proceso y se termina por PID
user@pleasejustwork:~$ ps -ef | grep ssh
[...]
user 19255 1 0 11:40 ? 00:00:00 ssh -fN -L 8001:127.0.0.1:8000 user@REMOTE-MACHINE
[...]
kill 19255
Mantener la conexión SSH y reconectar
- Hay varias formas de mantener una conexión SSH activa
- Las opciones de heartbeat para manejar timeouts se pueden configurar en el cliente, en el servidor o en ambos
ClientAliveInterval envía una solicitud cada n segundos para mantener viva la conexión
ClientAliveInterval 15
ClientAliveCountMax es la cantidad de solicitudes de heartbeat que se envían antes de cerrar la conexión cuando no hay respuesta del otro lado
ClientAliveCountMax 3
3 es el valor predeterminado, y si se establece en 0 se desactiva el cierre de la conexión
- En la configuración de ejemplo, si no hay respuesta, la conexión se corta después de unos 45 segundos
- Para reconectarse tras un corte se pueden usar
autossh, scripts, cronjobs, etc.
Limitaciones y consideraciones de seguridad
-
UDP
- SSH depende de una entrega confiable para realizar el descifrado correctamente
- Como UDP no ofrece confiabilidad, no está soportado ni recomendado en túneles SSH
- Existen métodos que tratan UDP over SSH tunneling, pero se mantiene la advertencia de que no fueron probados
-
TCP-over-TCP
- Por la sobrecarga, baja el rendimiento y aumenta la latencia
- En conexiones con pérdida de paquetes o alta latencia puede ocurrir TCP meltdown
- Se puede consultar este texto sobre TCP over TCP
- Al usar OpenVPN-over-TCP durante un tiempo, el rendimiento fue menor que con UDP pero funcionó de forma estable, aunque el resultado depende mucho de la configuración
-
Limitaciones al usarlo como reemplazo de VPN
- La tunelización SSH puede usarse como una VPN, pero para mejor rendimiento una VPN es más adecuada
-
Riesgos de seguridad
- Si esta función no es necesaria, se recomienda desactivarla
- Un atacante puede usar tunelización SSH y reenvío de puertos para evadir firewalls y otras medidas de seguridad
-
Documentación de referencia
1 comentarios
Opiniones de Hacker News
En 2024, en vez de escribir comandos SSH largos a mano, conviene configurar LocalForward, RemoteForward y ProxyJump en
~/.ssh/configReduce mucho el tiempo al acceder con
ssh,scporsynca servidores remotos que requieren pasar por varias conexiones SSH intermediasPor ejemplo, se pueden encadenar
jump-host-1,jump-host-2yjump-host-3conProxyJumpy conectarse atarget-servermediante un aliasLocalForward 0.0.0.0:8080 0.0.0.0:80reenvía el puerto remoto 80 al 8080 local, yRemoteForward 0.0.0.0:9022 0.0.0.0:22reenvía las solicitudes SSH que lleguen al 9022 remoto al puerto local 22En
LocalForwardyRemoteForward, el lado izquierdo es el servidor de destino y el derecho es el local; también se sugiere usar0.0.0.0en vez delocalhosto127.0.0.1ssh_config, resulta útil tener una configuración que, dentro de casa o de una VPN autoalojada, haga SSH directo a cada equipo, y desde afuera pase automáticamente por un jump hostSe puede detectar primero si estás en casa/VPN por la dirección del router, con algo como
Match host *.example.org exec "getent ahosts router.example.org | grep -q ^10.0.0.1", y hacer que useProxyJump jump.example.orgsi la dirección MAC obtenida conarpno coincide con el valor esperadoEl orden es importante: primero hay que detectar la red de la VPN/casa y después aplicar el jump host cuando se está afuera
Si se crea un VPS para usarlo brevemente como proxy SOCKS por SSH y saltarse restricciones regionales, o si hay que ayudar por un rato a otro equipo entrando una sola vez a un servidor al que normalmente no se accede, las opciones de línea de comandos parecen mejores
0.0.0.0puede ser peligrosoPuede abrir el puerto en todas las interfaces de red y, sobre todo si el servidor remoto no tiene firewall, terminar exponiendo algo a todo Internet
Para túneles o reenvíos de puertos frecuentes, el archivo de configuración está bien; para tareas únicas o poco comunes, las opciones de línea de comandos parecen mejores
Busco algo parecido a GitOps, adecuado para un solo usuario con varias máquinas, que traiga la configuración desde un lugar como GitHub sin necesidad de mantener siempre encendido un servidor aparte
bash/fishpara tener que recordar solo los argumentos mínimosSi se me olvida, miro la definición de la función que dejé documentada
En el entorno corporativo absurdo donde trabajo, el túnel SSH es indispensable
A veces la burocracia hace que conseguir permisos de acceso, abrir puertos u obtener excepciones de firewall/VPN tome semanas
El artículo menciona
-D, pero no explica del todo su potenciaSi ejecutas
ssh -D 8888 someservery configuras el proxy SOCKS del navegador comolocalhost:8888, todo el tráfico del navegador se enruta a través desomeserverFirefox sigue permitiendo hacer esta configuración sin cambiar los valores predeterminados del sistema, lo cual es muy útil
Pero al entrar a una empresa, por una allowlist de IPs ni siquiera podía hacer conexiones SSH a servidores arbitrarios de Internet, y fue tan frustrante que llegué a investigar cómo crear un túnel HTTP y meter en la allowlist un servidor que yo controlaba, hasta que finalmente cambié de trabajo
Esas restricciones existen por una razón, y evadirlas puede verse como falta de conocimiento y desprecio por los procesos y la seguridad de la organización
Si obtener acceso tarda semanas o meses, toca esperar; nadie quiere cargar con la responsabilidad de abrir una puerta trasera a información confidencial o debilitar la seguridad
El hack de túneles SSH más sucio que hice a las 3 de la mañana fue conectar tres centros de datos
Las tres instalaciones estaban en la misma área metropolitana y conectadas a la red troncal de Internet, pero por políticas de enrutamiento raras, A solo podía conectarse a B, y solo B podía conectarse a C
Al final tuve que mezclar rsync + túnel SSH + claves + trucos de enrutamiento para mover los datos de A a C pasando por B, y después de ajustar varias veces un comando que parecía un conjuro, ver que todo se movía de una sola vez fue como magia
Hoy me parece muy claro cómo lo haría, pero para mí, que entonces era principiante, fue un gran logro, y todavía recuerdo la emoción de comprobar que la sincronización había terminado
~/.ssh/configy ProxyJump se puede saltar, en la práctica, por tantos pasos como A, B, C, D, EMe gustan los detalles de la visualización
En redes, especialmente en conexiones de nivel más bajo, hacen mucha falta más herramientas para representar visualmente el tráfico
Claro, son solo representaciones estáticas de conceptos, y la mayoría de los proveedores de red también ofrece alguna forma de visualización de tráfico, pero por lo general se queda en métricas o gráficas individuales
Si un servidor Linux o un dispositivo IoT está detrás de un firewall, no tiene IP fija y quieres conectarte por SSH, puedes usar un servicio de túneles como https://sshreach.me
He usado bastante túneles durante años, pero no conocía la opción
-JMe gustaría que hubiera una herramienta visual para configurar túneles, en vez de pasar 30 minutos configurándolo cada vez que necesito uno cada pocos meses
Se explica que TCP-over-TCP aumenta la sobrecarga, reduce el throughput y eleva la latencia, y puede provocar un meltdown de TCP en conexiones con pérdida de paquetes o alta latencia, como redes satelitales.
Pero en túneles SSH, en la práctica no es un problema salvo que se use TAP/TUN.
Esto se debe a que SSH desempaqueta y reenvía el stream TCP.
Sin embargo, al usar varios canales, el rendimiento puede degradarse por el bloqueo de cabeza de línea.
Hace unos 15 años aprendí túneles SSH para saltarme el firewall de la red de la universidad, y tuve que cambiar el puerto predeterminado al 443.
Desde entonces lo sigo usando para muchos más fines que solo evadir firewalls.
Si pruebas
sshuttle, el tunneling se vuelve mucho más cómodo.Al usar algo como
sshuttle -r user@host 10.0.0.0/8, el rango10/8se tuneliza automáticamente y, en la práctica, funciona como una VPN sobre SSH.Me pregunto si SSH en sí tiene alguna función de redirección.
Por ejemplo, si A intenta hacer SSH a B, B le indica que se conecte a C, A se conecta directamente a C de forma transparente y B ya no queda en la ruta principal de datos.
Mi firewall/router no reenvía correctamente DHCP option 67 y siempre envía su propia dirección, así que tuve que configurar una IP virtual/reglas para que, cuando el tráfico de arranque PXE de ese puerto fuera a la IP del router, se enrutara al servidor real de arranque PXE.
Si no es así, se puede usar la función de jump:
ssh -J B C.Si B no necesita estar en la ruta y puedes conectarte directamente a C, simplemente conéctate directo a C; si no se puede, entonces B de todos modos tiene que entrar como salto.
Si explicas más el problema, quizá haya una solución más adecuada.
Si se trata de un host más o menos embebido, podrías hacer que DNS se encargue del “routing”, pero en ese caso tendrías que gestionar por tu cuenta la verificación de la huella de la clave del host.