ipipou: meer dan alleen een niet-versleutelde tunnel

Wat zeggen we tegen de God van IPv6?

ipipou: meer dan alleen een niet-versleutelde tunnel
Dat klopt, we zullen vandaag hetzelfde zeggen tegen de god van de encryptie.

Hier zullen we het hebben over een niet-gecodeerde IPv4-tunnel, maar niet over een ‘warme lamp’-tunnel, maar over een moderne ‘LED’-tunnel. En er knipperen hier ook raw sockets, en er wordt gewerkt met pakketten in de gebruikersruimte.

Er zijn N tunnelingprotocollen voor elke smaak en kleur:

  • stijlvol, modieus, jeugd WireGuard
  • multifunctioneel, zoals Zwitserse messen, OpenVPN en SSH
  • oud en niet slecht GRE
  • de meest eenvoudige, snelle, volledig ongecodeerde IPIP
  • actief ontwikkelen GENEVA
  • vele anderen.

Maar ik ben een programmeur, dus ik zal N slechts met een fractie verhogen en de ontwikkeling van echte protocollen overlaten aan Kommersant-ontwikkelaars.

In één ongeboren projectWat ik nu doe, is hosts achter NAT van buitenaf bereiken. Door hiervoor protocollen met cryptografie voor volwassenen te gebruiken, kon ik het gevoel niet van me afschudden dat het was alsof ik mussen uit een kanon schoot. Omdat de tunnel wordt grotendeels alleen gebruikt om gaten in NAT-e te prikken, intern verkeer is meestal ook versleuteld, maar ze verdrinken nog steeds in HTTPS.

Tijdens het onderzoeken van verschillende tunnelprotocollen werd de aandacht van mijn innerlijke perfectionist keer op keer gevestigd op IPIP vanwege de minimale overhead. Maar het heeft anderhalf belangrijke nadelen voor mijn taken:

  • het vereist openbare IP's aan beide kanten,
  • en geen authenticatie voor jou.

Daarom werd de perfectionist teruggedreven naar de donkere hoek van de schedel, of waar hij daar ook zit.

En dan op een dag, terwijl ik artikelen las over natuurlijk ondersteunde tunnels in Linux kwam ik FOU (Foo-over-UDP) tegen, d.w.z. wat dan ook, verpakt in UDP. Tot nu toe worden alleen IPIP en GUE (Generic UDP Encapsulation) ondersteund.

“Hier is de zilveren kogel! Voor mij is een simpele IPIP voldoende.” - Ik dacht.

Sterker nog, de kogel bleek niet helemaal zilver te zijn. Inkapseling in UDP lost het eerste probleem op: je kunt van buitenaf verbinding maken met clients achter NAT via een vooraf tot stand gebrachte verbinding, maar hier komt de helft van het volgende nadeel van IPIP in een nieuw licht te staan: iedereen uit een particulier netwerk kan zich verschuilen achter het zichtbare openbaar IP-adres en clientpoort (in puur IPIP bestaat dit probleem niet).

Om dit anderhalf probleem op te lossen werd het hulpprogramma geboren ipipou. Het implementeert een zelfgemaakt mechanisme voor het authenticeren van een externe host, zonder de werking van de kernel-FOU te verstoren, die pakketten in de kernelruimte snel en efficiënt zal verwerken.

We hebben je script niet nodig!

Oké, als je de openbare poort en het IP-adres van de client kent (iedereen erachter gaat bijvoorbeeld nergens heen, NAT probeert poorten 1-in-1 in kaart te brengen), kun je een IPIP-over-FOU-tunnel maken met de volgende opdrachten, zonder scripts.

op server:

# Подгрузить модуль ядра 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

op de cliënt:

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

waar

  • ipipou* — naam van de lokale tunnelnetwerkinterface
  • 203.0.113.1 — openbare IP-server
  • 198.51.100.2 — openbaar IP-adres van de client
  • 192.168.0.2 — client-IP toegewezen aan interface eth0
  • 10001 — lokale clientpoort voor FOU
  • 20001 — openbare clientpoort voor FOU
  • 10000 — openbare serverpoort voor FOU
  • encap-csum — optie om een ​​UDP-controlesom toe te voegen aan ingekapselde UDP-pakketten; vervangen kan worden door noencap-csumOm nog maar te zwijgen van het feit dat de integriteit al wordt gecontroleerd door de buitenste inkapselingslaag (terwijl het pakket zich in de tunnel bevindt)
  • eth0 — lokale interface waaraan de ipip-tunnel zal worden gekoppeld
  • 172.28.0.1 — IP van de clienttunnelinterface (privé)
  • 172.28.0.0 — IP-tunnelserverinterface (privé)

Zolang de UDP-verbinding actief is, zal de tunnel in goede staat functioneren, maar als deze kapot gaat, heb je geluk - als de IP-poort van de klant hetzelfde blijft - zal hij blijven werken, als ze veranderen - zal hij kapot gaan.

De eenvoudigste manier om alles terug te draaien is door de kernelmodules te verwijderen: modprobe -r fou ipip

Zelfs als authenticatie niet vereist is, zijn het openbare IP-adres en de poort van de client niet altijd bekend en zijn deze vaak onvoorspelbaar of variabel (afhankelijk van het NAT-type). Als je het nalaat encap-dport aan de serverzijde zal de tunnel niet werken, hij is niet slim genoeg om de externe verbindingspoort te gebruiken. In dit geval kan ipipou ook helpen, of kunnen WireGuard en soortgelijke anderen u helpen.

Hoe werkt het?

De client (die zich meestal achter NAT bevindt) opent een tunnel (zoals in het bovenstaande voorbeeld) en stuurt een authenticatiepakket naar de server zodat deze de tunnel op zijn kant configureert. Afhankelijk van de instellingen kan dit een leeg pakketje zijn (alleen maar zodat de server het publieke IP-adres kan zien: verbindingspoort), of met gegevens waarmee de server de client kan identificeren. De gegevens kunnen een eenvoudige wachtwoordzin in duidelijke tekst zijn (denk aan de analogie met HTTP Basic Auth) of speciaal ontworpen gegevens ondertekend met een privésleutel (vergelijkbaar met HTTP Digest Auth, alleen sterker, zie functie client_auth in de code).

Op de server (de kant met het publieke IP) creëert ipipou, wanneer het start, een nfqueue-wachtrijhandler en configureert het netfilter zodat de benodigde pakketten worden verzonden waar ze moeten zijn: pakketten die de verbinding met de nfqueue-wachtrij initialiseren, en [bijna] de rest gaat rechtstreeks naar de luisteraar FOU.

Voor degenen die het niet weten: nfqueue (of NetfilterQueue) is iets speciaals voor amateurs die niet weten hoe ze kernelmodules moeten ontwikkelen, waarmee je met behulp van netfilter (nftables/iptables) netwerkpakketten kunt omleiden naar gebruikersruimte en ze daar kunt verwerken met behulp van primitief betekent bij de hand: wijzigen (optioneel) en teruggeven aan de kernel, of weggooien.

Voor sommige programmeertalen zijn er bindingen voor het werken met nfqueue, voor bash was er geen (heh, niet verrassend), ik moest python gebruiken: ipipou gebruikt NetfilterQueue.

Als prestaties niet cruciaal zijn, kun je met dit ding relatief snel en eenvoudig je eigen logica bedenken voor het werken met pakketten op een redelijk laag niveau, bijvoorbeeld experimentele protocollen voor gegevensoverdracht maken, of lokale en externe services met niet-standaard gedrag bedriegen.

Raw sockets werken hand in hand met nfqueue. Als de tunnel bijvoorbeeld al is geconfigureerd en FOU op de gewenste poort luistert, kun je niet op de gebruikelijke manier een pakket vanaf dezelfde poort verzenden - het is druk, maar je kunt een willekeurig gegenereerd pakket rechtstreeks naar de netwerkinterface nemen en verzenden met behulp van een onbewerkte socket, hoewel het genereren van zo'n pakket wat meer sleutelwerk vereist. Dit is hoe pakketten met authenticatie worden aangemaakt in ipipou.

Omdat ipipou alleen de eerste pakketten van de verbinding verwerkt (en de pakketten die erin zijn geslaagd in de wachtrij te lekken voordat de verbinding tot stand is gebracht), lijden de prestaties vrijwel niet.

Zodra de ipipou-server een geverifieerd pakket ontvangt, wordt er een tunnel aangemaakt en worden alle daaropvolgende pakketten in de verbinding al verwerkt door de kernel, waarbij nfqueue wordt omzeild. Als de verbinding mislukt, wordt het eerste pakket van het volgende naar de nfqueue-wachtrij gestuurd, afhankelijk van de instellingen. Als het geen pakket met authenticatie is, maar vanaf het laatst onthouden IP-adres en de clientpoort, kan het worden doorgegeven aan of weggegooid. Als een geverifieerd pakket afkomstig is van een nieuw IP-adres en een nieuwe poort, wordt de tunnel opnieuw geconfigureerd om deze te gebruiken.

De gebruikelijke IPIP-over-FOU heeft nog een probleem bij het werken met NAT: het is onmogelijk om twee IPIP-tunnels te creëren die zijn ingekapseld in UDP met hetzelfde IP-adres, omdat de FOU- en IPIP-modules behoorlijk geïsoleerd van elkaar zijn. Die. een paar clients achter hetzelfde openbare IP-adres kunnen op deze manier niet tegelijkertijd verbinding maken met dezelfde server. In de toekomst, misschien, zal het op kernelniveau worden opgelost, maar dit is niet zeker. In de tussentijd kunnen NAT-problemen worden opgelost door NAT - als het gebeurt dat een paar IP-adressen al bezet zijn door een andere tunnel, zal ipipou NAT doen van openbaar naar een alternatief privé-IP, voila! - je kunt tunnels maken totdat de poorten opraken.

Omdat Niet alle pakketten in de verbinding zijn ondertekend, dus deze eenvoudige bescherming is kwetsbaar voor MITM, dus als er een slechterik op de loer ligt op het pad tussen de client en de server die naar het verkeer kan luisteren en het kan manipuleren, kan hij geauthenticeerde pakketten omleiden via een ander adres en maak een tunnel van een niet-vertrouwde host.

Als iemand ideeën heeft om dit op te lossen terwijl het grootste deel van het verkeer in de kern blijft, aarzel dan niet om iets te zeggen.

Overigens heeft inkapseling in UDP zich zeer goed bewezen. Vergeleken met inkapseling via IP is het veel stabieler en vaak sneller, ondanks de extra overhead van de UDP-header. Dit komt door het feit dat de meeste hosts op internet alleen goed werken met de drie populairste protocollen: TCP, UDP, ICMP. Het tastbare deel kan al het andere volledig weggooien, of langzamer verwerken, omdat het alleen voor deze drie is geoptimaliseerd.

Dit is bijvoorbeeld de reden waarom QUICK, waarop HTTP/3 is gebaseerd, bovenop UDP is gemaakt, en niet bovenop IP.

Nou ja, genoeg woorden, het is tijd om te zien hoe het werkt in de ‘echte wereld’.

Strijd

Wordt gebruikt om de echte wereld na te bootsen iperf3. In termen van de mate van nabijheid tot de werkelijkheid is dit ongeveer hetzelfde als het emuleren van de echte wereld in Minecraft, maar voorlopig is het voldoende.

Deelnemers aan de competitie:

  • referentie hoofdkanaal
  • de held van dit artikel is ipipou
  • OpenVPN met authenticatie maar geen encryptie
  • OpenVPN in all-inclusive modus
  • WireGuard zonder PresharedKey, met MTU=1440 (sinds IPv4-only)

Technische gegevens voor nerds
Metrische gegevens worden verzameld met de volgende opdrachten:

op de cliënt:

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"

ICMP-latentie

ping -c 10 SERVER_IP | tail -1

op de server (draait gelijktijdig met de client):

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"

Tunnelconfiguratie

ipipou
server
/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

cliënt
/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 (geen encryptie, met authenticatie)
server

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

cliënt

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 (met encryptie, authenticatie, via UDP, alles zoals verwacht)
Geconfigureerd met openvpn-beheer

afscherming
server
/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

cliënt
/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

Bevindingen

Vochtig lelijk teken
CPU-belasting van server is niet erg indicatief, omdat... Er zijn daar veel andere diensten actief, die soms middelen opslokken:

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

20 Mbps-kanaal

ipipou: meer dan alleen een niet-versleutelde tunnel

ipipou: meer dan alleen een niet-versleutelde tunnel

kanaal per 1 optimistische Gbps

ipipou: meer dan alleen een niet-versleutelde tunnel

ipipou: meer dan alleen een niet-versleutelde tunnel

In alle gevallen komt ipipou qua prestaties vrij dicht in de buurt van het basiskanaal, wat geweldig is!

De niet-versleutelde openvpn-tunnel gedroeg zich in beide gevallen nogal vreemd.

Als iemand het gaat testen, zal het interessant zijn om feedback te horen.

Mogen IPv6 en NetPrickle bij ons zijn!

Bron: www.habr.com

Voeg een reactie