6 puntos por GN⁺ 2024-09-20 | 1 comentarios | Compartir por WhatsApp
  • 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

 
GN⁺ 2024-09-20
Opiniones de Hacker News
  • En 2024, en vez de escribir comandos SSH largos a mano, conviene configurar LocalForward, RemoteForward y ProxyJump en ~/.ssh/config
    Reduce mucho el tiempo al acceder con ssh, scp o rsync a servidores remotos que requieren pasar por varias conexiones SSH intermedias
    Por ejemplo, se pueden encadenar jump-host-1, jump-host-2 y jump-host-3 con ProxyJump y conectarse a target-server mediante un alias
    LocalForward 0.0.0.0:8080 0.0.0.0:80 reenvía el puerto remoto 80 al 8080 local, y RemoteForward 0.0.0.0:9022 0.0.0.0:22 reenvía las solicitudes SSH que lleguen al 9022 remoto al puerto local 22
    En LocalForward y RemoteForward, el lado izquierdo es el servidor de destino y el derecho es el local; también se sugiere usar 0.0.0.0 en vez de localhost o 127.0.0.1

    • Como tip de ssh_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 host
      Se 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 use ProxyJump jump.example.org si la dirección MAC obtenida con arp no coincide con el valor esperado
      El orden es importante: primero hay que detectar la red de la VPN/casa y después aplicar el jump host cuando se está afuera
    • A veces es algo de un solo uso y no dan ganas de crear un archivo de configuración
      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
    • Usar 0.0.0.0 puede ser peligroso
      Puede 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
    • Me da curiosidad cómo gestionan la estandarización y distribución de este tipo de configuración SSH en varias máquinas locales
      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
    • Como no siempre quiero que se abra una shell, armé funciones básicas de bash/fish para tener que recordar solo los argumentos mínimos
      Si 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 potencia
    Si ejecutas ssh -D 8888 someserver y configuras el proxy SOCKS del navegador como localhost:8888, todo el tráfico del navegador se enruta a través de someserver
    Firefox sigue permitiendo hacer esta configuración sin cambiar los valores predeterminados del sistema, lo cual es muy útil

    • A mediados de los 2000 lo usaba casi como método estándar para navegar desde fuera de casa
      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
    • No es buena idea evadir la red corporativa
      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

    • Con ~/.ssh/config y ProxyJump se puede saltar, en la práctica, por tantos pasos como A, B, C, D, E
  • Me 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

    • Estos diagramas también valen la pena: https://www.nathanhandy.blog/articles/osi-model-revisited.ht...
      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 -J
    Me 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.

    • Me da curiosidad saber para qué otros usos le sacaron provecho, además de evadir firewalls o exponer servicios locales más allá de la red.
  • 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 rango 10/8 se 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.

    • Creo que se podría lograr algo parecido con una IP virtual.
      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 A no sabe que el destino real es C, puede prestarse a malentendidos.
      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.
    • B podría reenviar puertos hacia C, es decir, enrutar paquetes, pero no parece haber una función equivalente a una redirección permanente de HTTP.
      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.