ipipou: máis que un túnel sen cifrar

Que lle estamos dicindo ao Deus de IPv6?

ipipou: máis que un túnel sen cifrar
É certo, hoxe diremos o mesmo ao deus do cifrado.

Aquí falaremos dun túnel IPv4 sen cifrar, pero non dun "lámpada quente", senón dun moderno "LED". E tamén hai sockets en bruto parpadeando aquí, e está a traballar con paquetes no espazo de usuario.

Hai N protocolos de túnel para cada gusto e cor:

  • elegante, de moda, xuvenil WireGuard
  • multifuncional, como coitelos suízos, OpenVPN e SSH
  • GRE vello e non malvado
  • o IPIP máis sinxelo, rápido e completamente sen cifrar
  • desenvolvendo activamente XÉNERO
  • moitos outros.

Pero eu son programador, polo que vou aumentar N só nunha fracción e deixarei o desenvolvemento de protocolos reais aos desenvolvedores de Kommersant.

Nun non nacido proxectoO que estou facendo agora é chegar aos anfitrións detrás de NAT desde fóra. Usando protocolos con criptografía para adultos para iso, non puiden sacudir a sensación de que era como disparar a pardais dun canón. Porque o túnel úsase na súa maior parte só para facer buracos en NAT-e, o tráfico interno adoita estar tamén cifrado, pero aínda se afogan en HTTPS.

Mentres investigaba varios protocolos de tunelización, a atención do meu perfeccionista interior chamoulle unha e outra vez a IPIP debido á súa mínima sobrecarga. Pero ten un inconveniente e medio significativo para as miñas tarefas:

  • require IPs públicas de ambos os dous lados,
  • e sen autenticación para ti.

Polo tanto, o perfeccionista foi conducido de volta ao recuncho escuro da caveira, ou onde queira que estea alí sentado.

E entón un día, mentres lía artigos sobre túneles soportados de forma nativa en Linux atopeime con FOU (Foo-over-UDP), é dicir. o que sexa, envolto en UDP. Ata agora, só se admiten IPIP e GUE (Encapsulación UDP xenérica).

"Aquí está a bala de prata! Un simple IPIP é suficiente para min". - Pensei.

De feito, a bala resultou non ser completamente prata. A encapsulación en UDP resolve o primeiro problema: pode conectarse aos clientes detrás de NAT desde o exterior mediante unha conexión preestablecida, pero aquí a metade do seguinte inconveniente de IPIP florece baixo unha nova luz: calquera persoa dunha rede privada pode esconderse detrás do visible. IP pública e porto cliente (en IPIP puro este problema non existe).

Para resolver este problema e medio, naceu a utilidade ipipou. Implementa un mecanismo feito na casa para autenticar un host remoto, sen interromper o funcionamento da FOU do núcleo, que procesará de forma rápida e eficiente os paquetes no espazo do núcleo.

Non necesitamos o teu guión!

Ok, se coñeces o porto público e a IP do cliente (por exemplo, todos os que están detrás del non van a ningún lado, NAT intenta mapear portos 1 en 1), podes crear un túnel IPIP sobre FOU co seguintes comandos, sen ningún script.

no 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

no 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

onde

  • ipipou* — nome da interface de rede local do túnel
  • 203.0.113.1 - Servidor IP público
  • 198.51.100.2 — IP pública do cliente
  • 192.168.0.2 — IP do cliente asignada á interface eth0
  • 10001 — porto de cliente local para FOU
  • 20001 — porto público cliente para FOU
  • 10000 — porto de servidor público para FOU
  • encap-csum — opción para engadir unha suma de verificación UDP aos paquetes UDP encapsulados; pode ser substituído por noencap-csum, sen mencionar que a integridade xa está controlada pola capa de encapsulación exterior (mentres o paquete está dentro do túnel)
  • eth0 — interface local á que se enlazará o túnel ipip
  • 172.28.0.1 — IP da interface do túnel do cliente (privada)
  • 172.28.0.0 - Interfaz do servidor de túnel IP (privado)

Mentres a conexión UDP estea activa, o túnel estará funcionando, pero se rompe, terás sorte -se o porto IP: do cliente segue a ser o mesmo - vivirá, se cambian - romperase.

A forma máis sinxela de darlle voltas atrás é descargar os módulos do núcleo: modprobe -r fou ipip

Aínda que non sexa necesaria a autenticación, a IP pública e o porto do cliente non sempre se coñecen e adoitan ser imprevisibles ou variables (dependendo do tipo de NAT). Se omites encap-dport no lado do servidor, o túnel non funcionará, non é o suficientemente intelixente para tomar o porto de conexión remota. Neste caso, ipipou tamén pode axudar, ou WireGuard e outros similares poden axudarche.

Como funciona isto?

O cliente (que adoita estar detrás de NAT) abre un túnel (como no exemplo anterior) e envía un paquete de autenticación ao servidor para que configure o túnel ao seu lado. Dependendo da configuración, este pode ser un paquete baleiro (só para que o servidor poida ver a IP pública: porto de conexión), ou con datos polos que o servidor pode identificar o cliente. Os datos poden ser unha simple frase de paso en texto claro (vénme á mente a analoxía con HTTP Basic Auth) ou datos especialmente deseñados asinados cunha clave privada (semellante a HTTP Digest Auth só é máis forte, consulte a función client_auth no código).

No servidor (o lado coa IP pública), cando se inicia ipipou, crea un manejador de colas nfqueue e configura netfilter para que se envíen os paquetes necesarios onde deberían estar: paquetes que inician a conexión coa cola nfqueue, e [case] todo o resto vai directo ao oínte FOU.

Para os que non o saben, nfqueue (ou NetfilterQueue) é algo especial para os afeccionados que non saben como desenvolver módulos do núcleo, que usando netfilter (nftables/iptables) permite redirixir os paquetes de rede ao espazo do usuario e procesalos alí usando primitivo significa a man: modificar (opcional) e devolvelo ao núcleo, ou descartalo.

Para algunhas linguaxes de programación hai ligazóns para traballar con nfqueue, para bash non había ningunha (je, non é sorprendente), tiven que usar python: ipipou usa NetfilterQueue.

Se o rendemento non é crítico, usando isto podes inventar de xeito relativamente rápido e sinxelo a túa propia lóxica para traballar con paquetes a un nivel bastante baixo, por exemplo, crear protocolos experimentais de transferencia de datos ou buscar servizos locais e remotos cun comportamento non estándar.

Os sockets en bruto funcionan da man con nfqueue, por exemplo, cando o túnel xa está configurado e FOU está escoitando no porto desexado, non poderás enviar un paquete dende o mesmo porto do xeito habitual: está ocupado, pero pode levar e enviar un paquete xerado aleatoriamente directamente á interface de rede usando un socket en bruto, aínda que xerar un paquete deste tipo requirirá un pouco máis de retoques. Así é como se crean os paquetes con autenticación en ipipou.

Dado que ipipou só procesa os primeiros paquetes da conexión (e os que conseguiron filtrarse na cola antes de establecerse a conexión), o rendemento case non se ve afectado.

Tan pronto como o servidor ipipou recibe un paquete autenticado, créase un túnel e todos os paquetes posteriores da conexión xa son procesados ​​polo núcleo evitando nfqueue. Se a conexión falla, entón o primeiro paquete do seguinte enviarase á cola nfqueue, dependendo da configuración, se non se trata dun paquete con autenticación, senón do último IP e porto cliente lembrado, pódese pasar ou ben. activado ou descartado. Se un paquete autenticado procede dunha IP e un porto novos, o túnel reconfigurarase para usalos.

O IPIP-sobre-FOU habitual ten un problema máis cando se traballa con NAT: é imposible crear dous túneles IPIP encapsulados en UDP coa mesma IP, porque os módulos FOU e IPIP están bastante illados entre si. Eses. un par de clientes detrás da mesma IP pública non poderá conectarse simultaneamente ao mesmo servidor deste xeito. No futuro, quizais, resolverase a nivel do núcleo, pero isto non é certo. Mentres tanto, os problemas de NAT pódense resolver mediante NAT: se ocorre que un par de enderezos IP xa está ocupado por outro túnel, ipipou fará o NAT de IP pública a outra privada alternativa, ¡voíla! - podes crear túneles ata que se esgoten os portos.

Porque Non todos os paquetes da conexión están asinados, entón esta protección sinxela é vulnerable ao MITM, polo que se hai un malo á espreita no camiño entre o cliente e o servidor que pode escoitar o tráfico e manipulalo, pode redirixir os paquetes autenticados. a través doutro enderezo e cree un túnel desde un host non fiable.

Se alguén ten ideas sobre como solucionar isto mentres deixa a maior parte do tráfico no núcleo, non dubide en falar.

Por certo, a encapsulación en UDP demostrouse moi ben. En comparación coa encapsulación sobre IP, é moito máis estable e moitas veces máis rápido a pesar da sobrecarga adicional da cabeceira UDP. Isto débese ao feito de que a maioría dos servidores en Internet só funcionan ben cos tres protocolos máis populares: TCP, UDP e ICMP. A parte tanxible pode descartar completamente todo o demais, ou procesala máis lentamente, porque está optimizada só para estes tres.

Por exemplo, é por iso que QUICK, no que se basea HTTP/3, creouse enriba de UDP, e non de IP.

Ben, palabras suficientes, é hora de ver como funciona no "mundo real".

Batalla

Usado para emular o mundo real iperf3. En canto ao grao de proximidade á realidade, isto é aproximadamente o mesmo que emular o mundo real en Minecraft, pero por agora si.

Participantes no concurso:

  • canle principal de referencia
  • o heroe deste artigo é ipipou
  • OpenVPN con autenticación pero sen cifrado
  • OpenVPN en modo todo incluído
  • WireGuard sen PresharedKey, con MTU=1440 (só desde IPv4)

Datos técnicos para frikis
As métricas tómanse cos seguintes comandos:

no 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

no servidor (execútase simultaneamente co 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 do 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 (sen 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 openvpn-xestionar

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

Descubrimentos

Sinal feo húmido
A carga da CPU do servidor non é moi indicativa, porque... Hai moitos outros servizos que funcionan alí, ás 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

Canle de 20 Mbps

ipipou: máis que un túnel sen cifrar

ipipou: máis que un túnel sen cifrar

canle por 1 Gbps optimista

ipipou: máis que un túnel sen cifrar

ipipou: máis que un túnel sen cifrar

En todos os casos, ipipou está bastante preto no rendemento da canle base, o que é xenial!

O túnel openvpn sen cifrar comportouse de forma bastante estraña en ambos os casos.

Se alguén o vai probar, será interesante escoitar comentarios.

Que IPv6 e NetPrickle estean connosco!

Fonte: www.habr.com

Engadir un comentario