Tengo un servidor Wireguard ejecutándose en casa para poder ingresar a mi red doméstica desde el exterior. Wireguard está utilizando UDP. Desafortunadamente, no puedo simplemente abrir un puerto UDP IPv4 en mi Fritz Box como solía funcionar en los viejos tiempos porque mi proveedor me puso detrás de CG-NAT, tengo que usar IPv6 pero IPv6 aún no se puede utilizar en mi lugar de trabajo.
Entonces, en casa tengo IPv6 y en el trabajo tengo IPv4 y no hay forma de llegar a mi red doméstica.
También tengo un VPS con un IPv4 público al que podría establecer túneles ssh inversos para el tráfico TCP. Desafortunadamente, los túneles ssh solo admiten TCP y de todos modos no se deben realizar conexiones VPN a través de TCP, razón por la cual Wireguard solo usa UDP.
VPN-Server VPN-Client
(UDP) | | (IPv4 UDP)
| | | not possible |
-----------|--------| <-------------------
| |
NAT CG-NAT
VPN-Server VPN-Client
(UDP) (IPv4 UDP)
| |
| |
inside agent outside agent
tunnel tunnel
|| | | ||
|| punch| punch| ||
| --------->|-------->|-------------------- |
-----------|<--------|<--------------------
| |
| |
NAT CG-NAT
El agente externo se ejecuta en un VPS con IPv4 público
El agente interno enviará datagramas UDP a la IP pública y al puerto del agente externo, esto perforará ambos NAT, el agente externo recibirá estos datagramas y aprenderá de su dirección de origen y puerto cómo enviar datagramas de regreso al agente interno. .
El cliente VPN puede enviar UDP al agente externo y estos datagramas se reenviarán al agente interno y de allí al servidor VPN, el servidor VPN puede responder al agente interno, estos se reenviarán al agente externo y de allí a el cliente VPN.
El agente externo aparecerá como el servidor VPN para el cliente y el agente interno aparecerá como el cliente VPN para el servidor.
Son posibles múltiples conexiones de clientes porque utilizará un túnel y un nuevo socket en el agente interno para cada nuevo cliente que se conecte en el exterior, por lo que para el servidor aparecerá como si se estuvieran ejecutando múltiples clientes en el host del agente interno.
Clona o descomprime el código en ambas máquinas y compílalo. Necesitas al menos make y gcc. Ingrese al directorio de origen y use el comando
$ make
Después de una compilación exitosa, terminará con el udp-tunnel
binario en la misma carpeta. Ahora puedes iniciarlo directamente desde una terminal (con las opciones correctas, por supuesto) para realizar algunas pruebas rápidas, o puedes instalarlo con la ayuda del archivo MAKE.
El udp-tunnel
binario compilado puede actuar como agente interno o como agente externo, dependiendo de las opciones que pase en la línea de comando.
Supongamos como ejemplo que el servidor VPN está escuchando en UDP 1234, ejecutándose en localhost (igual que el agente interno) y la máquina externa es jump.example.com y queremos que escuche en el puerto UDP 9999.
En el host interno lo comenzamos con
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999
En el host externo lo comenzamos con
$ ./udp-tunnel -l 9999
El archivo MAKE contiene 3 objetivos de instalación: install
para instalar solo el binario, install-outside
e install-inside
para instalar también los archivos de servicio systemd. Los dos últimos necesitan que se pasen variables para que funcionen correctamente.
Para instalar el agente externo en el host de salto (suponiendo que desee el puerto 9999), ejecute este comando:
$ sudo make install-outside listen=9999
Esto instalará el binario en /usr/local/bin/
e instalará un archivo de servicio systemd en /etc/systemd/system/
que contiene el comando necesario para iniciarlo en modo agente externo con el puerto 9999.
Para instalar el agente interno en la máquina interna, use el siguiente comando (asumiendo, como ejemplo, que su servidor vpn es localhost:1234 y su host de salto es jump.example.com):
$ sudo make install-inside service=localhost:1234 outside=jump.example.com:9999
Esto instalará nuevamente el binario en /usr/local/bin/
y un archivo de unidad systemd en /etc/systemd/system/
En este punto, es posible que desee echar un vistazo rápido a los archivos de la unidad systemd para ver cómo se usa el binario y verificar si las opciones son correctas. Las opciones deberían verse como se describe arriba en la prueba rápida.
Una vez que los archivos systemd estén instalados y se confirme que son correctos, aún no están habilitados para el inicio automático, debe habilitarlos e iniciarlos. En la máquina interior:
$ sudo systemctl enable udp-tunnel-inside.service
$ sudo systemctl start udp-tunnel-inside.service
y en la máquina exterior
$ sudo systemctl enable udp-tunnel-outside.service
$ sudo systemctl start udp-tunnel-outside.service
No hay cifrado. Los paquetes se reenvían tal como están, se supone que cualquier servicio que esté tunelizando sabe cómo proteger o cifrar sus datos por sí solo. Normalmente este es el caso de las conexiones VPN.
Además, un atacante podría querer falsificar los paquetes keepalive del agente interno para confundir al agente externo y desviar el túnel a su propia máquina, lo que provocaría una interrupción del servicio. Para evitar este ataque tan simple, los datagramas keepalive se pueden autenticar con un código de autenticación de mensajes basado en hash. Puede utilizar una contraseña previamente compartida utilizando la opción -k en ambos extremos del túnel para activar esta función.
En el host interno lo usarías así
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999 -k mysecretpassword
En el host externo lo comenzarías con
$ ./udp-tunnel -l 9999 -k mysecretpassword
Después de que los pasos de instalación anteriores funcionen correctamente, es posible que desee editar manualmente los archivos systemd en ambos extremos y agregar una opción -k, luego recargar y reiniciar en ambos extremos.
El mensaje keepalive contendrá un SHA-256 sobre esta contraseña y sobre un nonce estrictamente creciente que solo se puede usar exactamente una vez para evitar ataques simples de repetición.
Ambos agentes mantienen una lista de conexiones, cada conexión almacena direcciones de socket y identificadores de socket asociados con esa conexión de cliente en particular.
Al iniciar, el agente interno iniciará un túnel de salida hacia el agente externo, lo marcará como no utilizado y enviará paquetes de mantenimiento a través de él. El agente externo verá estos paquetes y agregará este túnel con su dirección de origen a su propia lista de conexiones. Los paquetes keepalive están firmados con un nonce y un código de autenticación, por lo que no pueden falsificarse ni reproducirse.
Cuando un cliente se conecta, el agente externo enviará los datos del cliente por ese túnel de repuesto no utilizado, el agente interno verá esto, marcará el túnel como activo, creará un socket para comunicarse con el servicio y reenviará datos en ambas direcciones. Luego, también creará inmediatamente otro nuevo túnel de repuesto saliente para estar listo para el próximo cliente entrante.
Cuando el nuevo túnel de repuesto llegue al agente externo, lo agregará a su propia lista de conexiones para poder atender al siguiente cliente que se conecte inmediatamente.
En este punto, ambos agentes tienen 2 túneles en su lista, uno está activo y el otro está libre, esperando la siguiente conexión del cliente.
Los tiempos de espera de inactividad garantizarán que los túneles inactivos (aquellos que se marcaron como activos en el pasado pero sin datos del cliente durante algún tiempo) se eliminarán y sus sockets se cerrarán. El agente interno detectará la falta de datos reenviados durante un tiempo prolongado, los eliminará de su propia lista y cerrará sus sockets, luego, algún tiempo después, el agente externo detectará que ya no llegan keepalives para esta conexión y los eliminará de su propia lista. lista también.
Este código aún es altamente experimental, así que no base un negocio multimillonario en él, al menos no todavía. Para mí cumple perfectamente su propósito, pero podría fallar, quemar y explotar su servidor. Has sido advertido.