Wat zeggen we tegen de god van IPv6?

Klopt, en tegen de god van encryptie zullen we vandaag hetzelfde zeggen.
Hier gaat het over een onversleutelde IPv4-tunnel, maar niet over een 'warme lamp', eerder over een moderne 'LED'. En er verschijnen ruwe sockets, en er wordt gewerkt met pakketten in de gebruikersruimte.
Er zijn N tunnelingprotocollen in alle smaken en kleuren:
- stijlvol, trendy, jong
- multifunctioneel, zoals Zwitserse zakmessen, OpenVPN en SSH
- oud en niet kwaad, GRE
- maximaal eenvoudig, snel, helemaal niet versleuteld IPIP
- actief in ontwikkeling
- en vele anderen.
Maar ik ben een programmeur, dus ik zal N slechts met een fractie verhogen, en de ontwikkeling van echte protocollen laat ik aan de Ī-developers over.
In een nog ongeboren , waar ik momenteel aan werk, moet ik buiten naar hosts achter NAT tankeren. Bij het gebruik van protocollen met volwassen cryptografie had ik steeds het gevoel dat dit als met een kanon op een mus schieten was. Aangezien de tunnel voornamelijk wordt gebruikt voor het prullen van een gat in de NAT, is het interne verkeer meestal ook versleuteld, iedereen is voor HTTPS.
Bij het onderzoeken van verschillende tunnelingprotocollen werd de aandacht van mijn innerlijke perfectionist keer op keer getrokken door IPIP vanwege de minimale overhead. Maar het heeft anderhalve significante tekortkoming voor mijn taken:
- het vereist openbare IP's aan beide zijden,
- en geen enkele vorm van authenticatie.
Dus de perfectionist trok zich weer terug in de donkere hoek van de schedel, of waar hij ook zit.
En op een keer, terwijl ik artikelen las over in Linux stuitte ik op FOU (Foo-over-UDP), d.w.z. dat wat dan ook, ingekapseld in UDP. Voorlopig worden alleen IPIP en GUE (Generic UDP Encapsulation) ondersteund vanuit wat dan ook.
"Daar is het, de zilveren kogel! Gewoon IPIP is meer dan genoeg voor mij," dacht ik.
In de praktijk bleek de kogel niet helemaal zilver te zijn. Inslag in UDP lost het eerste probleem op – we kunnen met clients achter NAT verbinding maken van buitenaf met behulp van een vooraf ingesteld connection, maar hier bloeit de halve volgende tekortkoming van IPIP in een nieuw licht— achter de zichtbare openbare IP en poort van de client kan iedereen uit het privénetwerk schuilgaan (die probleem bestaat in puur IPIP niet).
Om deze anderhalve probleem op te lossen, is de tool Er is een zelfgemaakt mechanisme voor de authenticatie van de externe host geïmplementeerd, zonder de werking van de kern FOU te verstoren, die snel en efficiënt pakketten in de kernruimte zal verwerken.
Je hebt je script niet nodig!
Oké, als je de publieke poort en IP van de client kent (bijvoorbeeld omdat ze niet zomaar overal naartoe gaan, NAT probeert poorten 1-op-1 te mappen), kun je een IPIP-over-FOU tunnel maken met de volgende commando's, zonder scripts.
op de 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 client:
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
# De opties local, peer, peer_port, dev worden mogelijk niet ondersteund door oude kernels en kunnen worden weggelaten.
# peer en peer_port worden gebruikt om verbinding te maken bij het creëren van de FOU-luisteraar.
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 tunnel netwerkinterface203.0.113.1— publieke IP van de server198.51.100.2— publieke IP van de client192.168.0.2— IP van de client, toegewezen aan de interface eth010001— lokale poort van de client voor FOU20001— publieke poort van de client voor FOU10000— publieke poort van de server voor FOUencap-csum— optie voor het toevoegen van een controle som UDP in de ingekapselde UDP-pakketten; kan worden vervangen doornoencap-csum, zodat er niet wordt geteld, de integriteit wordt nog steeds gecontroleerd door de externe laag van ingekapseling (zolang het pakket zich binnen de tunnel bevindt)eth0— lokale interface waaraan de ipip-tunnel zal worden gekoppeld172.28.0.1— IP van de tunnelinterface van de client (privé)172.28.0.0— IP van de tunnelinterface van de server (privé)
Zolang de UDP-verbinding actief is, zal de tunnel operationeel blijven, en als deze verbroken wordt, hangt het af — als het IP: poort van de client hetzelfde blijft — zal het blijven bestaan, verandert het — dan is het voorbij.
Alles terugdraaien is het eenvoudigst door de kernelmodules te unloaden: modprobe -r fou ipip
Zelfs als authenticatie niet vereist is, zijn publieke IP's en poorten van de client niet altijd bekend en vaak onvoorspelbaar of veranderlijk (afhankelijk van het type NAT). Als je encap-dport aan de serverkant weglaat, zal de tunnel niet werken, hij is niet slim genoeg om de externe poort van de verbinding te nemen. In dit geval kan ipipou ook helpen, of WireGuard en soortgelijke tools staan tot je dienst.
How does it work?
De client (die meestal achter NAT zit) richt een tunnel op (zoals in het bovenstaande voorbeeld) en stuurt een authenticatiepakket naar de server zodat deze de tunnel aan zijn kant kan instellen. Afhankelijk van de instellingen kan dit een leeg pakket zijn (gewoon zodat de server de publieke IP:poort van de verbinding ziet), of met gegevens waarmee de server de client kan identificeren. De gegevens kunnen een eenvoudige wachtwoordzin in platte tekst zijn (de analogie met HTTP Basic Auth komt in me op) of speciaal opgemaakte gegevens ondertekend met een privésleutel (vergelijkbaar met HTTP Digest Auth, maar iets sterker, zie functie client_auth in de code).
Aan de serverzijde (de kant met het publieke IP) creëert ipipou bij opstarten een verwerker voor de nfqueue en configureert netfilter zodat de benodigde pakketten naar de juiste plaats worden geleid: de pakketten die de verbinding initiëren gaan naar de nfqueue, terwijl [bijna] alle andere pakketten direct naar de FOU-luisteraar gaan.
Voor degenen die niet op de hoogte zijn, nfqueue (of NetfilterQueue) is een speciale tool voor leken die niet in staat zijn om kernelmodules te ontwikkelen, waarmee via netfilter (nftables/iptables) netwerkpakketten naar de gebruikersruimte kunnen worden omgeleid en daar met primitieve hulpmiddelen kunnen worden behandeld: modificeren (optioneel) en teruggeven aan de kernel of afwijzen.
Voor sommige programmeertalen zijn er bindings voor het werken met nfqueue, maar voor bash kon ik er geen vinden (grappig, niet verrassend), dus moest ik python gebruiken: ipipou maakt gebruik van .
Als de prestaties niet kritiek zijn, kan je met dit ding relatief snel en eenvoudig je eigen logica voor het omgaan met pakketten op een vrij laag niveau opzetten, bijvoorbeeld experimentele gegevensoverdrachtprotocollen maken of lokale en externe services trollen met ongewone gedragingen.
Samen met nfqueue werken ruwe sockets (raw sockets), bijvoorbeeld wanneer de tunnel al is ingesteld en FOU luistert op de juiste poort, kan je op de gebruikelijke manier geen pakket vanuit diezelfde poort verzenden — die is bezet, maar je kunt een willekeurig gegenereerd pakket rechtstreeks in de netwerkinterface pompen met behulp van een rauwe socket, hoewel je meer moeite moet doen om zo'n pakket te genereren. Zo worden in ipipou de pakketten met authenticatie gemaakt.
Omdat ipipou alleen de eerste pakketten van de verbinding verwerkt (en de pakketten die voor het tot stand komen van de verbinding in de wachtrij zijn geraakt), heeft de prestaties bijna geen gevolgen.
Zodra de ipipou-server een pakket ontvangt dat is geauthenticeerd, wordt de tunnel aangemaakt en worden alle volgende pakketten in de verbinding direct door de kernel verwerkt, voorbij nfqueue. Als de verbinding verlopen is, wordt het eerste pakket van de volgende verbinding naar de nfqueue-wachtrij gestuurd, afhankelijk van de instellingen. Als het geen authentiekeringspakket is, maar van het laatst opgeslagen IP en poort van de client, kan het ofwel verder worden doorgestuurd of worden afgewezen. Als een geauthenticeerd pakket van nieuwe IP en poort aankomt, wordt de tunnel opnieuw geconfigureerd om deze te gebruiken.
Een gewoon IPIP-over-FOU heeft nog een probleem bij het werken met NAT: er kunnen geen twee IPIP-tunnels die in UDP zijn ingekapseld met dezelfde IP worden aangemaakt, omdat de FOU- en IPIP-modules vrij gescheiden zijn. Dit betekent dat een paar clients achter één publiek IP niet tegelijk op dezelfde server kunnen inloggen op deze manier. In de toekomst, , zal dit op kernniveau worden opgelost, maar dat is niet zeker. Totdat de NAT-problemen zich voordoen, kan NAT worden gebruikt — als blijkt dat een paar IP-adressen al bezet zijn door een andere tunnel, zal ipipou NAT uitvoeren van het publieke naar een alternatief privé-IP, voilà! — tunnels kunnen worden aangemaakt zolang de poorten niet op zijn.
Aangezien niet alle pakketten in de verbinding zijn ondertekend, is deze eenvoudige bescherming kwetsbaar voor MITM, dus als er een kwaadaardige actor tussen de client en server zit die traffic kan afluisteren en beheren, kan hij authentiseringspakketten omleiden naar een ander adres en een tunnel creëren met een onbetrouwbaar host.
Als iemand ideeën heeft over hoe dit te verhelpen terwijl het grootste deel van het verkeer in de kernel blijft, aarzel dan niet — deel je gedachten.
Overigens heeft het inkapselen in UDP zich zeer goed bewezen. In vergelijking met inkapseling boven IP is het veel stabieler en vaak sneller, ondanks de extra overhead van de UDP-header. Dit komt omdat de meeste hosts op het internet zich behoorlijk goed gedragen met alleen de drie meest populaire protocollen: TCP, UDP, ICMP. Een aanzienlijk aantal kan zelfs alles eromheen afwijzen of langzamer verwerken, omdat ze alleen voor deze drie zijn geoptimaliseerd.
Bijvoorbeeld, daarom werd QUICK, dat de basis vormt voor HTTP/3, precies bovenop UDP ontwikkeld, en niet bovenop IP.
Nou, genoeg gepraat, laten we kijken hoe dit werkt in de 'echte wereld'.
Battle
Om de echte wereld te emuleren, wordt iperf3gebruikt. Qua nabijheid aan de werkelijkheid is dit ongeveer vergelijkbaar met de emulatie van de echte wereld in Minecraft, maar voorlopig voldoet het.
De competitie omvat:
- de referentie hoofdkanaal
- de held van dit artikel ipipou
- OpenVPN met authenticatie, maar zonder encryptie
- OpenVPN in 'alles inbegrepen' modus
- WireGuard zonder PresharedKey, met MTU=1440 (omdat alleen IPv4)
Technische gegevens voor geek
Metrics worden verzameld met deze commando's
op de client:
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"
# Waar "-b 12M" de bandbreedte van het hoofdkanaal is, gedeeld door het aantal streams "-P", om het aantal overbodige pakketten te minimaliseren en de prestaties niet te verstoren.
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 (gelanceerd tegelijk 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
nummer 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
client
/etc/ipipou/client.conf:
client
nummer 0
fou-local @eth0
fou-remote SERVER_IP:10000
tunl-ip 172.28.0.1
# public key van auth-key-b64: eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-key-b64 RuBZkT23na2Q4QH1xfmZCfRgSgPt5s362UPAFbecTso=
auth-secret topsecret
keepalive 27
verb 3
systemctl start ipipou@client
openvpn (zonder encryptie, met authenticatie)
server
openvpn --genkey --secret ovpn.key # Daarna moet je ovpn.key naar de client sturen
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
client
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, zoals het hoort)
Ingesteld met behulp van
wireguard
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
client
/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
Resultaten
Een rauwe lelijke tabel
De CPU-belasting van de server is niet erg indicatief, omdat er veel andere services draaien die soms resources verbruiken:
proto bandwidth[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps verbinding van een microcomputer (4 cores) naar VPS (1 core) via de Atlantische Oceaan
# puur
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 (alleen authenticatie, geen encryptie)
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 (volledige encryptie, authenticatie, enz.)
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
## ongeveer-1Gbps verbinding tussen VPS in Europa en de VS (1 core)
# puur
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 (alleen authenticatie, geen encryptie)
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
verbinding van 20 Mbps


verbinding van 1 optimistische Gbps


In alle gevallen is ipipou behoorlijk dicht bij de basisverbinding, en dat is geweldig!
De ongecodeerde openvpn-tunnel gedroeg zich nogal vreemd in beide gevallen.
Als iemand het gaat testen, zou het interessant zijn om feedback te horen.
Moge IPv6 en NetPrickle met ons zijn!
Bron: habr.com
