ipipou: más que solo un túnel sin cifrar

¿Qué le decimos a Dios IPv6?

ipipou: más que solo un túnel sin cifrar
Correcto, y hoy le diremos lo mismo a Dios de la encriptación.

Aquí se hablará de un túnel IPv4 sin cifrar, pero no de un "cálido y acogedor", sino de uno moderno "led". Y también se mencionan sockets crudos, y se trabaja con paquetes en el espacio de usuario.

Hay N protocolos de tunelización para todos los gustos y colores:

  • elegantes, modernos, juveniles WireGuard
  • multifuncionales, como cuchillos suizos, OpenVPN y SSH
  • el viejo y no maligno GRE
  • el IPIP, extremadamente simple, rápido y sin cifrar
  • en constante desarrollo GENEVE
  • y muchos otros.

Pero soy programador, por lo que aumentaré N solo un poco, dejando el desarrollo de verdaderos protocolos a los Ъ-desarrolladores.

En un proyecto aún no nacido en el que estoy trabajando actualmente, necesito conectarme a hosts detrás de NAT desde el exterior. Usando protocolos con criptografía avanzada, no podía deshacerme de la sensación de que esto era como usar un cañón para matar gorriones. Dado que el túnel se utiliza principalmente para hacer un agujero en NAT, el tráfico interno generalmente también está cifrado, todos abogan por HTTPS.Al investigar varios protocolos de tunelización, la atención de mi perfeccionista interno se vio atraída una y otra vez por IPIP debido a sus mínimos gastos generales. Sin embargo, tiene un par de desventajas sustanciales para mis tareas:

requiere IP públicas en ambos lados,

  • y no hay autenticación en absoluto.
  • Por lo tanto, el perfeccionista se sumió de nuevo en la oscura esquina de la caja craneal, o donde quiera que esté.

Y así, un día, leyendo artículos sobre

túneles soportados de forma nativa en Linux, me topé con FOU (Foo-over-UDP), es decir, algo que se envuelve en UDP. Hasta ahora, de lo que se apoya, solo se soportan IPIP y GUE (Encapsulación UDP Genérica). "¡Esa es la bala de plata! Con el simple IPIP tengo de sobra." - pensé.

En realidad, la bala no era completamente de plata. La encapsulación en UDP resuelve el primer problema: se puede conectar a los clientes detrás de NAT desde el exterior usando una conexión previamente establecida, pero aquí la mitad de la siguiente desventaja de IPIP florece de nuevo — detrás de las IP públicas visibles y el puerto del cliente puede estar cualquiera de la red privada (en IPIP puro no hay este problema).

Para resolver este problema de uno y medio, nació la utilidad

ipipou ipipou. Se ha implementado un mecanismo de autenticación de host remoto, sin interrumpir el funcionamiento del núcleo FOU, que procesará los paquetes de manera ágil y eficiente en el espacio del núcleo.

¡No necesitas tu script!

Bien, si conoces el puerto y la IP pública del cliente (por ejemplo, si no hay acceso aleatorio, NAT intenta mapear puertos 1 a 1), puedes crear un túnel IPIP-over-FOU con los siguientes comandos, sin necesidad de scripts.

en el servidor:

# Подгрузить модуль ядра FOU
modprobe fou

# Создать IPIP туннель с инкапсуляцией в FOU.
# Модуль ipip подгрузится автоматически.
ip link add name ipipou0 type ipip 
    remote 198.51.100.2 local 203.0.113.1 
    encap fou encap-sport 10000 encap-dport 20001 
    mode ipip dev eth0

# Добавить порт на котором будет слушать FOU для этого туннеля
ip fou add port 10000 ipproto 4 local 203.0.113.1 dev eth0

# Назначить IP адрес туннелю
ip address add 172.28.0.0 peer 172.28.0.1 dev ipipou0

# Поднять туннель
ip link set ipipou0 up

en el cliente:

modprobe fou

ip link add name ipipou1 type ipip 
    remote 203.0.113.1 local 192.168.0.2 
    encap fou encap-sport 10001 encap-dport 10000 encap-csum 
    mode ipip dev eth0

# Las opciones local, peer, peer_port, dev pueden no ser soportadas por núcleos antiguos, se pueden omitir.
# peer y peer_port se utilizan para establecer la conexión inmediatamente al crear el listener FOU.
ip fou add port 10001 ipproto 4 local 192.168.0.2 peer 203.0.113.1 peer_port 10000 dev eth0

ip address add 172.28.0.1 peer 172.28.0.0 dev ipipou1

ip link set ipipou1 up

donde

  • ipipou* — nombre de la interfaz de red del túnel local
  • 203.0.113.1 — IP pública del servidor
  • 198.51.100.2 — IP pública del cliente
  • 192.168.0.2 — IP del cliente asignada a la interfaz eth0
  • 10001 — puerto local del cliente para FOU
  • 20001 — puerto público del cliente para FOU
  • 10000 — puerto público del servidor para FOU
  • encap-csum — opción para agregar un checksum UDP a los paquetes UDP encapsulados; se puede reemplazar por noencap-csum, para no calcular, la integridad se controla a través de la capa externa de encapsulación (mientras el paquete esté dentro del túnel)
  • eth0 — interfaz local a la que se vinculará el túnel ipip
  • 172.28.0.1 — IP de la interfaz del túnel del cliente (privada)
  • 172.28.0.0 — IP de la interfaz del túnel del servidor (privada)

Mientras la conexión UDP esté activa, el túnel seguirá en funcionamiento, pero si se rompe, la suerte dictará; si la IP: puerto del cliente permanece igual, seguirá funcionando, si cambian—se romperá.

Es más fácil revertir todo descargando los módulos del núcleo: modprobe -r fou ipip

Incluso si no se requiere autenticación, las IP públicas y el puerto del cliente no siempre son conocidos y a menudo son impredecibles o cambiantes (dependiendo del tipo de NAT). Si se omite encap-dport del lado del servidor, el túnel no funcionará, no es lo suficientemente inteligente como para deducir el puerto remoto de la conexión. En este caso, ipipou también podría ayudar, o WireGuard y similares te serán útiles.

¿Cómo funciona?

El cliente (que generalmente está detrás de un NAT) establece un túnel (como en el ejemplo anterior) y envía un paquete de autenticación al servidor para que este configure el túnel desde su lado. Dependiendo de la configuración, esto puede ser un paquete vacío (simplemente para que el servidor vea la IP pública: puerto de la conexión), o con datos que permitan al servidor identificar al cliente. Los datos pueden ser una frase de contraseña simple en texto claro (me viene a la mente la analogía con HTTP Basic Auth) o datos firmados con una clave privada, especialmente formateados (similar a HTTP Digest Auth pero más fuerte, ver la función client_auth en el código).

En el servidor (el lado con IP pública), al iniciar ipipou se crea un manejador de cola nfqueue y se configura netfilter de tal manera que los paquetes necesarios se dirijan donde corresponde: los paquetes que inicializan la conexión a la cola nfqueue, y [casi] todos los demás directamente al listener FOU.

Para aquellos que no están familiarizados, nfqueue (o NetfilterQueue) es una herramienta especial para aficionados, que no saben desarrollar módulos del kernel, que mediante netfilter (nftables/iptables) permite redirigir paquetes de red al espacio de usuario y procesarlos allí con métodos sencillos: modificarlos (opcionalmente) y devolverlos al núcleo, o descartarlos.

Para algunos lenguajes de programación, existen enlaces para trabajar con nfqueue; para bash no encontré (jeje, no sorprende), tuve que usar python: ipipou utiliza NetfilterQueue.

Si el rendimiento no es crítico, con esta herramienta se puede crear de manera relativamente rápida y sencilla una lógica propia para trabajar con paquetes a un nivel bastante bajo, por ejemplo, desarrollar protocolos experimentales de transmisión de datos o molestar servicios locales y remotos con comportamientos no estándar.

Los sockets crudos (raw sockets) funcionan junto con nfqueue; por ejemplo, cuando el túnel ya está configurado y FOU escucha en el puerto requerido, no se puede enviar un paquete de forma ordinaria desde ese mismo puerto — está ocupado, pero se puede generar y enviar un paquete aleatorio directamente a la interfaz de red usando un socket crudo, aunque se necesitará un poco más de trabajo en la generación de dicho paquete. Así es como se crean en ipipou los paquetes de autenticación.

Dado que ipipou solo procesa los primeros paquetes de la conexión (y aquellos que lograron filtrarse en la cola antes de establecer la conexión), el rendimiento casi no se ve afectado.

Tan pronto como el servidor ipipou recibe un paquete que ha pasado la autenticación, se crea un túnel y todos los paquetes subsecuentes en la conexión son procesados por el núcleo, evitando nfqueue. Si la conexión ha caducado, el primer paquete siguiente será dirigido a la cola nfqueue, dependiendo de la configuración; si no es un paquete de autenticación, sino del último IP y puerto registrados del cliente, puede ser pasado más adelante o descartado. Si un paquete autenticado llega desde un nuevo IP y puerto, el túnel se reconfigura para su uso.

IPIP sobre FOU tiene otro problema al trabajar con NAT: no se puede crear dos túneles IPIP encapsulados en UDP con las mismas IP, ya que los módulos FOU e IPIP están suficientemente aislados entre sí. Es decir, un par de clientes detrás de una misma IP pública no podrán conectarse simultáneamente a un mismo servidor de esta manera. En el futuro, posiblemente, se resolverá a nivel del núcleo, aunque no es seguro. Mientras tanto, los problemas de NAT se pueden resolver con NAT: si resulta que un par de direcciones IP ya están ocupadas por otro túnel, ipipou hará NAT desde la IP pública a una IP privada alternativa, ¡voilà! — Se pueden crear túneles hasta que se agoten los puertos.

Dado que no todos los paquetes en la conexión están firmados, esta sencilla protección es vulnerable a MITM, así que si hay un villano entre el cliente y el servidor que puede escuchar y manejar el tráfico, puede redirigir los paquetes de autenticación a través de otra dirección y crear un túnel desde un host no confiable.

Si alguien tiene ideas sobre cómo solucionar esto manteniendo la mayor parte del tráfico en el núcleo, no dude en expresarse.

Cabe mencionar que la encapsulación en UDP ha demostrado ser muy eficaz. En comparación con la encapsulación sobre IP, es mucho más estable y a menudo más rápida a pesar de los costos adicionales del encabezado UDP. Esto se debe a que en Internet, la mayoría de los hosts funcionan razonablemente solo con los tres protocolos más populares: TCP, UDP e ICMP. Una parte significativa puede incluso descartar todo lo demás o procesarlo más lentamente, ya que está optimizada solo para esos tres.

Por ejemplo, por eso QUICK, sobre el cual se creó HTTP/3, se desarrolló sobre UDP y no sobre IP.

Bueno, basta de palabras, es hora de ver cómo funciona en el «mundo real».

Batalla

Para simular el mundo real se utiliza iperf3. En cuanto a la cercanía a la realidad, es aproximadamente como simular el mundo real en Minecraft, pero por ahora está bien.

En la competencia participan:

  • el canal base de referencia
  • el héroe de este artículo ipipou
  • OpenVPN con autenticación, pero sin cifrado
  • OpenVPN en modo «todo incluido»
  • WireGuard sin PresharedKey, con MTU=1440 (debido a que es solo IPv4)

Datos técnicos para los geeks
Las métricas se obtienen con estos comandos

en el cliente:

UDP

CPULOG=NAME.udp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -c SERVER_IP -4 -t 60 -f m -i 10 -B LOCAL_IP -P 2 -u -b 12M; tail -1 "$CPULOG"
# Donde "-b 12M" es el ancho de banda del canal base, dividido por el número de hilos "-P", para no generar paquetes innecesarios y no afectar el rendimiento.

TCP

CPULOG=NAME.tcp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -c SERVER_IP -4 -t 60 -f m -i 10 -B LOCAL_IP -P 2; tail -1 "$CPULOG"

latencia ICMP

ping -c 10 SERVER_IP | tail -1

en el servidor (se ejecuta a la vez que el cliente):

UDP

CPULOG=NAME.udp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -s -i 10 -f m -1; tail -1 "$CPULOG"

TCP

CPULOG=NAME.tcp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -s -i 10 -f m -1; tail -1 "$CPULOG"

Configuración de túneles

ipipou
servidor
/etc/ipipou/server.conf:

servidor
número 0
fou-dev eth0
fou-local-port 10000
tunl-ip 172.28.0.0
auth-remote-pubkey-b64 eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-secret topsecret
auth-lifetime 3600
reply-on-auth-ok
verb 3

systemctl start ipipou@server

cliente
/etc/ipipou/client.conf:

cliente
número 0
fou-local @eth0
fou-remote SERVER_IP:10000
tunl-ip 172.28.0.1
# clave pública de auth-key-b64: eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-key-b64 RuBZkT23na2Q4QH1xfmZCfRgSgPt5s362UPAFbecTso=
auth-secret topsecret
keepalive 27
verb 3

systemctl start ipipou@client

openvpn (sin cifrado, con autenticación)
servidor

openvpn --genkey --secret ovpn.key  # Luego hay que enviar ovpn.key al cliente
openvpn --dev tun1 --local SERVER_IP --port 2000 --ifconfig 172.16.17.1 172.16.17.2 --cipher none --auth SHA1 --ncp-disable --secret ovpn.key

cliente

openvpn --dev tun1 --local LOCAL_IP --remote SERVER_IP --port 2000 --ifconfig 172.16.17.2 172.16.17.1 --cipher none --auth SHA1 --ncp-disable --secret ovpn.key

openvpn (con cifrado, autenticación, a través de UDP, todo como se debe)
Configurado usando openvpn-manage

wireguard
servidor
/etc/wireguard/server.conf:

[Interface]
Address=172.31.192.1/18
ListenPort=51820
PrivateKey=aMAG31yjt85zsVC5hn5jMskuFdF8C/LFSRYnhRGSKUQ=
MTU=1440

[Peer]
PublicKey=LyhhEIjVQPVmr/sJNdSRqTjxibsfDZ15sDuhvAQ3hVM=
AllowedIPs=172.31.192.2/32

systemctl start wg-quick@server

cliente
/etc/wireguard/client.conf:

[Interface]
Address=172.31.192.2/18
PrivateKey=uCluH7q2Hip5lLRSsVHc38nGKUGpZIUwGO/7k+6Ye3I=
MTU=1440

[Peer]
PublicKey=DjJRmGvhl6DWuSf1fldxNRBvqa701c0Sc7OpRr4gPXk=
AllowedIPs=172.31.192.1/32
Endpoint=SERVER_IP:51820

systemctl start wg-quick@client

Resultados

Una tabla cruda y fea
La carga del CPU del servidor no es un buen indicador, ya que hay muchos otros servicios activos que a veces consumen recursos:

proto ancho de banda[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# Canal de 20 Mbps desde un microcomputador (4 núcleos) hasta VPS (1 núcleo) a través del Atlántico
# puro
UDP 20.4      99.80 93.34
TCP 19.2      99.67 96.68
Latencia ICMP min/prom/max/mdev = 198.838/198.997/199.360/0.372 ms
# ipipou
UDP 19.8      98.45 99.47
TCP 18.8      99.56 96.75
Latencia ICMP min/prom/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (solo autenticación, sin cifrado)
UDP 19.3      99.89 72.90
TCP 16.1      95.95 88.46
Latencia ICMP min/prom/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (cifrado completo, autenticación, etc)
UDP 19.6      99.75 72.35
TCP 17.0      94.47 87.99
Latencia ICMP min/prom/max/mdev = 202.168/202.377/202.900/0.451 ms
# wireguard
UDP 19.3      91.60 94.78
TCP 17.2      96.76 92.87
Latencia ICMP min/prom/max/mdev = 217.925/223.601/230.696/3.266 ms

## Canal de aproximadamente 1 Gbps entre VPS de Europa y EE. UU. (1 núcleo)
# puro
UDP 729      73.40 39.93
TCP 363      96.95 90.40
Latencia ICMP min/prom/max/mdev = 106.867/106.994/107.126/0.066 ms
# ipipou
UDP 714      63.10 23.53
TCP 431      95.65 64.56
Latencia ICMP min/prom/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (solo autenticación, sin cifrado)
UDP 193      17.51  1.62
TCP  12      95.45 92.80
Latencia ICMP min/prom/max/mdev = 107.191/107.334/107.559/0.116 ms
# wireguard
UDP 629      22.26  2.62
TCP 198      77.40 55.98
Latencia ICMP min/prom/max/mdev = 107.616/107.788/108.038/0.128 ms

canal de 20 Mbps

ipipou: más que solo un túnel sin cifrar

ipipou: más que solo un túnel sin cifrar

canal de 1 Gbps optimista

ipipou: más que solo un túnel sin cifrar

ipipou: más que solo un túnel sin cifrar

En todos los casos, ipipou se acerca bastante a los indicadores del canal base, ¡y eso es genial!

El túnel de openvpn sin cifrado se comportó de manera bastante extraña en ambos casos.

Si alguien está por probarlo, sería interesante escuchar comentarios.

¡Que IPv6 y NetPrickle estén con nosotros!

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster