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

¿Qué le estamos diciendo al Dios de IPv6?

ipipou: más que un simple túnel sin cifrar
Así es, hoy le diremos lo mismo al dios del cifrado.

Aquí hablaremos de un túnel IPv4 no cifrado, pero no de uno de “lámpara cálida”, sino de uno moderno “LED”. Y aquí también hay sockets sin formato parpadeando y se está trabajando con paquetes en el espacio del usuario.

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

  • elegante, de moda, juvenil WireGuard
  • multifuncional, como navajas suizas, OpenVPN y SSH
  • viejo y no malvado GRE
  • el IPIP más simple, rápido y completamente sin cifrar
  • desarrollando activamente GENEVE
  • muchos otros.

Pero soy programador, así que aumentaré N sólo una fracción y dejaré el desarrollo de protocolos reales a los desarrolladores de Kommersant.

en un no nacido proyectoLo que estoy haciendo ahora es llegar a los hosts detrás de NAT desde el exterior. Usando protocolos con criptografía para adultos para esto, no pude evitar la sensación de que era como disparar a los gorriones con un cañón. Porque el túnel se utiliza en su mayor parte sólo para perforar agujeros en NAT-e, el tráfico interno también suele estar cifrado, pero aún así se ahogan en HTTPS.

Mientras investigaba varios protocolos de tunelización, la atención de mi perfeccionista interior se centró en IPIP una y otra vez debido a su mínima sobrecarga. Pero tiene un inconveniente y medio importante para mis tareas:

  • requiere IP públicas en ambos lados,
  • y sin autenticación para usted.

Por lo tanto, el perfeccionista fue empujado de regreso al rincón oscuro del cráneo, o dondequiera que se encuentre allí.

Y entonces, un día, mientras leía artículos sobre túneles soportados de forma nativa en Linux me encontré con FOU (Foo-over-UDP), es decir. lo que sea, envuelto en UDP. Hasta el momento, sólo se admiten IPIP y GUE (encapsulación UDP genérica).

“¡Aquí está la solución milagrosa! Un simple IPIP es suficiente para mí”. - Pensé.

De hecho, la bala resultó no ser completamente plateada. La encapsulación en UDP resuelve el primer problema: puede conectarse a clientes detrás de NAT desde el exterior utilizando una conexión preestablecida, pero aquí la mitad del siguiente inconveniente de IPIP emerge bajo una nueva luz: cualquiera de una red privada puede esconderse detrás de lo visible. IP pública y puerto de cliente (en IPIP puro este problema no existe).

Para solucionar este problema y medio nació la utilidad. ipipou. Implementa un mecanismo casero para autenticar un host remoto, sin interrumpir el funcionamiento de la FOU del kernel, que procesará paquetes de manera rápida y eficiente en el espacio del kernel.

¡No necesitamos tu guión!

Ok, si conoce el puerto público y la IP del cliente (por ejemplo, todos los que están detrás de él no van a ninguna parte, NAT intenta asignar puertos 1 en 1), puede crear un túnel IPIP sobre FOU con el siguientes comandos, sin ningún script.

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

# Опции local, peer, peer_port, dev могут не поддерживаться старыми ядрами, можно их опустить.
# peer и peer_port используются для создания соединения сразу при создании FOU-listener-а.
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 la red del túnel local
  • 203.0.113.1 — servidor IP público
  • 198.51.100.2 — IP pública del cliente
  • 192.168.0.2 — IP del cliente asignada a la interfaz eth0
  • 10001 — puerto de cliente local para FOU
  • 20001 — puerto de cliente público para FOU
  • 10000 — puerto de servidor público para FOU
  • encap-csum — opción para agregar una suma de verificación UDP a los paquetes UDP encapsulados; puede ser reemplazado por noencap-csum, sin mencionar que la integridad ya está controlada por la capa de encapsulación externa (mientras el paquete está dentro del túnel)
  • eth0 — interfaz local a la que se unirá el túnel ipip
  • 172.28.0.1 — IP de la interfaz del túnel del cliente (privada)
  • 172.28.0.0 — Interfaz de servidor de túnel IP (privada)

Mientras la conexión UDP esté activa, el túnel funcionará correctamente, pero si se rompe, tendrá suerte: si la IP del cliente: el puerto sigue siendo el mismo, funcionará, si cambian, se romperá.

La forma más sencilla de revertir todo es descargar los módulos del kernel: modprobe -r fou ipip

Incluso si no se requiere autenticación, la IP pública y el puerto del cliente no siempre se conocen y, a menudo, son impredecibles o variables (según el tipo de NAT). si omites encap-dport en el lado del servidor, el túnel no funcionará, no es lo suficientemente inteligente como para tomar el puerto de conexión remota. En este caso, ipipou también puede ayudar, o WireGuard y otros similares pueden ayudarle.

Como funciona?

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

En el servidor (el lado con la IP pública), cuando se inicia ipipou, crea un controlador de cola nfqueue y configura netfilter para que los paquetes necesarios se envíen a donde deberían estar: paquetes que inicializan la conexión a la cola nfqueue, y [casi] todo el resto va directamente al oyente FOU.

Para aquellos que no lo saben, nfqueue (o NetfilterQueue) es algo especial para los aficionados que no saben cómo desarrollar módulos del kernel, que usando netfilter (nftables/iptables) le permite redirigir paquetes de red al espacio del usuario y procesarlos allí usando medios primitivos disponibles: modificar (opcional) y devolverlo al núcleo, o descartarlo.

Para algunos lenguajes de programación hay enlaces para trabajar con nfqueue, para bash no había ninguno (je, no es sorprendente), tuve que usar python: ipipou usa Cola de filtro de red.

Si el rendimiento no es crítico, al utilizar esto, puede crear de manera relativamente rápida y sencilla su propia lógica para trabajar con paquetes a un nivel bastante bajo, por ejemplo, crear protocolos experimentales de transferencia de datos o controlar servicios locales y remotos con un comportamiento no estándar.

Los sockets sin formato funcionan de la mano con nfqueue, por ejemplo, cuando el túnel ya está configurado y FOU está escuchando en el puerto deseado, no podrá enviar un paquete desde el mismo puerto de la manera habitual: está ocupado, pero puede tomar y enviar un paquete generado aleatoriamente directamente a la interfaz de red utilizando un socket sin formato, aunque generar dicho paquete requerirá un poco más de retoques. Así se crean los paquetes con autenticación en ipipou.

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

Tan pronto como el servidor ipipou recibe un paquete autenticado, se crea un túnel y el kernel ya procesa todos los paquetes posteriores en la conexión sin pasar por nfqueue. Si la conexión falla, el primer paquete del siguiente se enviará a la cola nfqueue, dependiendo de la configuración, si no es un paquete con autenticación, pero desde la última IP y puerto del cliente recordados, se puede pasar encendido o descartado. Si un paquete autenticado proviene de una nueva IP y puerto, el túnel se reconfigura para usarlos.

El IPIP sobre FOU habitual tiene un problema más cuando se trabaja con NAT: es imposible crear dos túneles IPIP encapsulados en UDP con la misma IP, porque los módulos FOU e IPIP están bastante aislados entre sí. Aquellos. un par de clientes detrás de la misma IP pública no podrán conectarse simultáneamente al mismo servidor de esta manera. En el futuro, tal vez, se resolverá a nivel del kernel, pero esto no es seguro. Mientras tanto, los problemas de NAT se pueden resolver mediante NAT: si sucede que un par de direcciones IP ya están ocupadas por otro túnel, ipipou realizará NAT desde una IP pública a una privada alternativa, ¡listo! - Puedes crear túneles hasta que se agoten los puertos.

Porque No todos los paquetes en la conexión están firmados, entonces esta protección simple es vulnerable a MITM, por lo que si hay un villano acechando en el camino entre el cliente y el servidor que puede escuchar el tráfico y manipularlo, puede redirigir los paquetes autenticados a través de otra dirección y crear un túnel desde un host que no sea de confianza.

Si alguien tiene ideas sobre cómo solucionar este problema dejando la mayor parte del tráfico en el núcleo, no dude en hablar.

Por cierto, la encapsulación en UDP ha demostrado su eficacia. En comparación con la encapsulación sobre IP, es mucho más estable y, a menudo, más rápida a pesar de la sobrecarga adicional del encabezado UDP. Esto se debe al hecho de que la mayoría de los hosts de Internet funcionan bien sólo con los tres protocolos más populares: TCP, UDP, ICMP. La parte tangible puede descartar por completo todo lo demás, o procesarla más lentamente, porque está optimizada sólo para estos tres.

Por ejemplo, esta es la razón por la que QUICK, en el que se basa HTTP/3, se creó sobre UDP y no sobre IP.

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

Batalla

Se utiliza para emular el mundo real. iperf3. En términos del grado de cercanía a la realidad, esto es aproximadamente lo mismo que emular el mundo real en Minecraft, pero por ahora servirá.

Participantes en el concurso:

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

Datos técnicos para frikis
Las métricas se toman con los siguientes 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"
# Где "-b 12M" это пропускная способность основного канала, делённая на число потоков "-P", чтобы лишние пакеты не плодить и не портить производительность.

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 simultáneamente con 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 del túnel

ipipou
servidor
/etc/ipipou/server.conf:

server
number 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:

client
number 0
fou-local @eth0
fou-remote SERVER_IP:10000
tunl-ip 172.28.0.1
# pubkey of 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  # Затем надо передать ovpn.key клиенту
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, vía UDP, todo como se esperaba)
Configurado usando administración-openvpn

protector de cable
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

Signo feo y húmedo
La carga de la CPU del servidor no es muy indicativa, porque... Hay muchos otros servicios ejecutándose allí, a veces consumen recursos:

proto bandwidth[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps канал с микрокомпьютера (4 core) до VPS (1 core) через Атлантику
# pure
UDP 20.4      99.80 93.34
TCP 19.2      99.67 96.68
ICMP latency min/avg/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
ICMP latency min/avg/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (auth only, no encryption)
UDP 19.3      99.89 72.90
TCP 16.1      95.95 88.46
ICMP latency min/avg/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (full encryption, auth, etc)
UDP 19.6      99.75 72.35
TCP 17.0      94.47 87.99
ICMP latency min/avg/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
ICMP latency min/avg/max/mdev = 217.925/223.601/230.696/3.266 ms

## около-1Gbps канал между VPS Европы и США (1 core)
# pure
UDP 729      73.40 39.93
TCP 363      96.95 90.40
ICMP latency min/avg/max/mdev = 106.867/106.994/107.126/0.066 ms
# ipipou
UDP 714      63.10 23.53
TCP 431      95.65 64.56
ICMP latency min/avg/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (auth only, no encryption)
UDP 193      17.51  1.62
TCP  12      95.45 92.80
ICMP latency min/avg/max/mdev = 107.191/107.334/107.559/0.116 ms
# wireguard
UDP 629      22.26  2.62
TCP 198      77.40 55.98
ICMP latency min/avg/max/mdev = 107.616/107.788/108.038/0.128 ms

Canal de 20Mbps

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

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

canal por 1 Gbps optimista

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

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

En todos los casos, ipipou tiene un rendimiento bastante cercano al del canal base, ¡lo cual es genial!

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

Si alguien va a probarlo, será interesante escuchar sus comentarios.

¡Que IPv6 y NetPrickle nos acompañen!

Fuente: habr.com

Añadir un comentario