socat y el reenvio de puertos UDP por medio de SSH

Publicado por Diego Córdoba en

En esta oportunidad veremos cómo tunelizar o realizar el reenvío de «conexiones» UDP por encima de un túnel SSH previamente establecido haciendo uso de socat, una interesante herramienta que establece dos flujos de datos bidireccionales y transfiere datos entre ellos.

Motivación

En el vlog de @juncotic ya he mencionado en su momento cómo «tunelizar» el tráfico a un servidor TCP (particularmente una base de datos MySQL/MariaDB remota) por medio de una conexión segura SSH. De hecho, también escribí un artículo al respecto, por lo que no profundizaré ese tema aquí.

Estos contenidos también los agregué, junto a documentación adicional, al curso de SSH que imparto online, y por medio del que me llegó una muy interesante consulta por parte de un alumno:

Ahora, tengo una consulta sobre el re direccionamiento de puertos por SSH, por lo que he visto, solo se puede reenviar puertos TCP pero no UDP, hay alguna forma de poder reenviar ese protocolo a través del túnel.

Gustavo (alumno de SSH para reenvío local: -L

La opción «-L» del cliente SSH permite, como sabemos, reenviar una conexión local de TCP a un servidor remoto, es decir, si suponemos que en un ordenador remoto tenemos un servicio TCP corriendo, como puede ser HTTP, MYSQL, FTP, etc, podríamos conectar nuestra computadora con el servidor remoto mediante un túnel de SSH, y a su vez reenviar un puerto local hacia el servicio remoto.

De esta forma podríamos, por ejemplo, «abrir» localmente el puerto 1234 de tcp, y que este puerto reenvíe, o sea un espejo, del puerto 3306 de tcp remoto (mysql por ejemplo), por lo que podríamos conectar un cliente local de mysql al puerto en localhost, y esa conexión se reenviaría al host remoto en el que realmente está corriendo el servicio.

Ahora bien, este mecanismo que nos brinda (amablemente) SSH únicamente está disponible para conexiones TCP, por lo que no podríamos usarlo para UDP, como dice la pregunta. Esto está especificado en el propio «man» del cliente ssh:

Por lo que si queremos reenviar un puerto UDP, deberíamos utilizar alguna otra herramienta que nos permita extender la funcionalidad. En mi caso he optado por socat, una utilidad de línea de comandos que establece dos flujos de bytes bidireccionales y transfiere datos entre ellos, y puede ser utilizada en una interesante cantidad de propósitos.

Socat: logrando el objetivo

Si bien SSH no nos permite «directamente» reenviar un puerto UDP desde un origen a un destino, sí nos permite reenviar tráfico TCP, por lo que crearemos un túnel SSH para mantener una comunicación segura entre cliente y servidor, y haremos uso de socat para convertir las consultas UDP locales para que viajen por el túnel SSH, y en el otro extremo volveremos a usar socat para tomar los datos TCP recibidos, desencapsularlos y armar datagramas UDP nuevamente.

El esquema final será este:

socat ssh tunnel udp tunel tcp networking tcpip

En este caso el puerto NN al que el cliente UDP intenta conectarse puede ser cualquier puerto UDP de servicio, como el 53 para DNS, o 161 para SNMP por ejemplo… entendido, ¿verdad? 😛

Entendido esto, vamos ahora a ensuciarnos los dedos en el teclado para hacerlo andar.

Configurando el túnel SSH + socat

Los pasos son mas o menos estos:

  • Conectar un túnel SSH entre cliente y servidor para reenviar un determinado puerto. En este ejemplo, voy a reenviar el puerto TCP 1234 del cliente hacia el TCP 4321 del servidor (los números de los puertos son a gusto de cada uno… los elegí así por costumbre)
  • Una vez establecido el túnel, vamos a usar socat en el cliente para reenviar el puerto NN cliente hacia el puerto 1234 del túnel SSH en el lado del cliente.
  • Finalmente, vamos a usar socat en el servidor para el reenvío desde el puerto 4321 donde llegarán las consultas TCP desde el cliente hacia el puerto NN destino.

Conexión SSH y reenvío TCP

Suponiendo que ya tenemos el servidor SSH configurado, vamos a conectar el cliente para reenviar el puerto 1234 hacia el 4321 del servidor. Para ello en el cliente corremos:

ssh -L 1234:localhost:4321 admin@ip_servidor

Cabe señalar que el nombre de usuario, la ip del servidor, y demás opciones (puerto, clave privada, etc) deberán agregarse según necesidad de cada uno.

Con esto hecho, ya tendremos el puerto 1234 abierto del lado del cliente, y podremos verlo con comandos como «ss -npltu«.

socat y el reenvío UDP/TCP

Siguiente paso, reenviar vía socat el puerto NN de del cliente hacia el puerto tcp 1234 que acabamos de abrir. Para ello:

sudo socat UDP4-LISTEN:NN,fork TCP4:localhost:1234

Nótese el puerto NN en el cliente, ahí deberíamos reemplazar 53, 161, o cualquier puerto que quisiéramos.

Por último nos vamos al servidor, y realizamos el reenvío, vía socat, en sentido inverto, lo que recibimos por TCP 4321 vamos a reenviarlo al puerto NN del servidor que corresponda.

socat -T10 TCP4-LISTEN:4321,fork UDP4:ip_servicio:NN

Esta línea va a reenviar, en el servidor, todo el tráfico recibido en el puerto TCP 4321 (la salida del túnel SSH) hacia el puerto NN del servidor ip_servicio. ip_servicio será localhost o 127.0.0.1 si el servicio está corriendo localmente, o puede ser cualquier IP pública de un servidor remoto.

Por ejemplo, si NN es 53 y estamos hablando de reenviar las consultas DNS desde el cliente hacia el servidor DNS de CloudFlare (1.1.1.1), el comando quedaría algo así (en el servidor):

socat -T10 TCP4-LISTEN:4321,fork UDP4:1.1.1.1:53

En ese caso, las consultas que hagamos a DNS en el cliente (usando el puerto NN en el cliente) irán dirigidas por socat hacia el túnel SSH, que lo transmitirá de manera segura hacia el servidor de SSH, que recibirá los datos y los reenviará, vía UDP, hacia el servidor 1.1.1.1 en el puerto 53… ufff.

Y… ¿cómo lo probamos?

Particularmente para DNS podemos usar un cliente como dig de esta forma:

dig @127.0.0.1 -p NN juncotic.com

Siendo NN el puerto que hayamos especificado en el socat del cliente.

Y con esto llegamos al final! Espero que les sirva, y cualquier duda o sugerencia espero sus comentarios!!

Hasta la próxima!


¿Preguntas? ¿Comentarios?

Si tenés dudas, o querés dejarnos tus comentarios y consultas, sumate al grupo de Telegram de la comunidad JuncoTIC!
¡Te esperamos!


Diego Córdoba

- Ingeniero en Informática - Mg. Teleinformática - Tesis pendiente - Docente universitario - Investigador