Det er rigtigt, vi vil sige det samme til krypteringsguden i dag.
Her vil vi tale om en ukrypteret IPv4-tunnel, men ikke om en "varm lampe", men om en moderne "LED". Og her blinker også raw sockets, og der arbejdes med pakker i brugerrummet.
Der er N tunnelprotokoller for enhver smag og farve:
Men jeg er programmør, så jeg vil kun øge N med en brøkdel og overlade udviklingen af rigtige protokoller til Kommersant-udviklere.
I en endnu ikke født projektDet, jeg gør nu, er at nå værterne bag NAT udefra. Ved at bruge protokoller med voksenkryptografi til dette, kunne jeg ikke ryste følelsen af, at det var som at skyde gråspurve ud af en kanon. Fordi tunnelen bruges for det meste kun til at stikke huller i NAT-e, intern trafik er normalt også krypteret, men de drukner stadig i HTTPS.
Mens jeg undersøgte forskellige tunneling-protokoller, blev min indre perfektionists opmærksomhed henledt til IPIP igen og igen på grund af dens minimale overhead. Men det har halvanden væsentlige ulemper for mine opgaver:
det kræver offentlige IP'er på begge sider,
og ingen godkendelse til dig.
Derfor blev perfektionisten drevet tilbage i det mørke hjørne af kraniet, eller hvor han nu sidder der.
Og så en dag, mens du læste artikler om naturligt understøttede tunneler i Linux stødte jeg på FOU (Foo-over-UDP), dvs. uanset hvad, pakket ind i UDP. Indtil videre er kun IPIP og GUE (Generisk UDP Encapsulation) understøttet.
"Her er sølvkuglen! En simpel IPIP er nok for mig." - Jeg troede.
Faktisk viste kuglen sig ikke at være helt sølvfarvet. Indkapsling i UDP løser det første problem - du kan oprette forbindelse til klienter bag NAT udefra ved hjælp af en forudetableret forbindelse, men her blomstrer halvdelen af den næste ulempe ved IPIP i et nyt lys - enhver fra et privat netværk kan gemme sig bag det synlige offentlig IP og klientport (i ren IPIP eksisterer dette problem ikke).
For at løse dette halvandet problem blev værktøjet født ipipou. Den implementerer en hjemmelavet mekanisme til autentificering af en fjernvært uden at forstyrre driften af kerne-FOU'en, som hurtigt og effektivt vil behandle pakker i kernerummet.
Vi har ikke brug for dit manuskript!
Ok, hvis du kender klientens offentlige port og IP (f.eks. går alle bagved ingen steder, NAT forsøger at kortlægge porte 1-i-1), kan du oprette en IPIP-over-FOU-tunnel med følgende kommandoer uden scripts.
på serveren:
# Подгрузить модуль ядра 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
på klienten:
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
где
ipipou* — navnet på den lokale tunnelnetværksgrænseflade
203.0.113.1 — offentlig IP-server
198.51.100.2 — klientens offentlige IP
192.168.0.2 — klient IP tildelt interface eth0
10001 — lokal klientport for FOU
20001 — offentlig klienthavn for FOU
10000 — offentlig serverport for FOU
encap-csum — mulighed for at tilføje en UDP-kontrolsum til indkapslede UDP-pakker; kan erstattes af noencap-csum, for ikke at nævne, integritet er allerede kontrolleret af det ydre indkapslingslag (mens pakken er inde i tunnelen)
eth0 — lokal grænseflade, som ipip-tunnelen vil blive bundet til
172.28.0.1 — IP for klienttunnelgrænsefladen (privat)
172.28.0.0 — IP-tunnelservergrænseflade (privat)
Så længe UDP-forbindelsen er i live, vil tunnelen være i funktionsdygtig stand, men hvis den går i stykker, vil du være heldig - hvis klientens IP:-port forbliver den samme - den vil leve, hvis de ændrer sig - vil den gå i stykker.
Den nemmeste måde at vende alt tilbage på er at aflæse kernemodulerne: modprobe -r fou ipip
Selvom godkendelse ikke er påkrævet, er klientens offentlige IP og port ikke altid kendt og er ofte uforudsigelige eller variable (afhængigt af NAT-typen). Hvis du undlader encap-dport på serversiden vil tunnelen ikke fungere, det er ikke smart nok at tage fjernforbindelsesporten. I dette tilfælde kan ipipou også hjælpe, eller WireGuard og lignende kan hjælpe dig.
Hvordan fungerer det?
Klienten (som normalt er bag NAT) åbner en tunnel (som i eksemplet ovenfor), og sender en godkendelsespakke til serveren, så den konfigurerer tunnelen på sin side. Afhængigt af indstillingerne kan dette være en tom pakke (bare så serveren kan se den offentlige IP: forbindelsesport), eller med data, som serveren kan identificere klienten med. Dataene kan være en simpel adgangssætning i klar tekst (analogien med HTTP Basic Auth kommer til at tænke på) eller specialdesignede data signeret med en privat nøgle (ligner HTTP Digest Auth kun stærkere, se funktion client_auth i koden).
På serveren (siden med den offentlige IP), når ipipou starter, opretter den en nfqueue køhåndtering og konfigurerer netfilter, så de nødvendige pakker sendes, hvor de skal være: pakker initialiserer forbindelsen til nfqueue køen, og [næsten] resten går direkte til lytteren FOU.
For dem der ikke ved det, er nfqueue (eller NetfilterQueue) en speciel ting for amatører, der ikke ved, hvordan man udvikler kernemoduler, som ved hjælp af netfilter (nftables/iptables) giver dig mulighed for at omdirigere netværkspakker til brugerplads og behandle dem der ved hjælp af primitive midler ved hånden: modificere (valgfrit ) og giv det tilbage til kernen, eller kasser det.
For nogle programmeringssprog er der bindinger til at arbejde med nfqueue, for bash var der ingen (heh, ikke overraskende), jeg var nødt til at bruge python: ipipou bruger Netfilterkø.
Hvis ydeevnen ikke er kritisk, kan du ved hjælp af denne ting relativt hurtigt og nemt lave din egen logik til at arbejde med pakker på et ret lavt niveau, for eksempel oprette eksperimentelle dataoverførselsprotokoller eller trolde lokale og fjerntjenester med ikke-standard adfærd.
Raw sockets arbejder hånd i hånd med nfqueue, når f.eks. tunnelen allerede er konfigureret og FOU lytter på den ønskede port, vil du ikke kunne sende en pakke fra den samme port på den sædvanlige måde - den er optaget, men du kan tage og sende en tilfældigt genereret pakke direkte til netværksgrænsefladen ved hjælp af en rå socket, selvom generering af en sådan pakke vil kræve lidt mere fifling. Sådan oprettes pakker med godkendelse i ipipou.
Da ipipou kun behandler de første pakker fra forbindelsen (og dem, der nåede at lække ind i køen, før forbindelsen blev etableret), lider ydeevnen næsten ikke.
Så snart ipipou-serveren modtager en autentificeret pakke, oprettes en tunnel, og alle efterfølgende pakker i forbindelsen er allerede behandlet af kernen, der omgår nfqueue. Hvis forbindelsen mislykkes, vil den første pakke af den næste blive sendt til nfqueue-køen, afhængigt af indstillingerne, hvis det ikke er en pakke med godkendelse, men fra den sidst huskede IP og klientport, kan den enten videregives på eller kasseret. Hvis en autentificeret pakke kommer fra en ny IP og port, omkonfigureres tunnelen til at bruge dem.
Den sædvanlige IPIP-over-FOU har endnu et problem, når man arbejder med NAT - det er umuligt at skabe to IPIP-tunneler indkapslet i UDP med samme IP, fordi FOU- og IPIP-modulerne er ret isolerede fra hinanden. De der. et par klienter bag den samme offentlige IP vil ikke være i stand til samtidigt at oprette forbindelse til den samme server på denne måde. I fremtiden, måske, vil det blive løst på kerneniveau, men det er ikke sikkert. I mellemtiden kan NAT-problemer løses af NAT - hvis det sker, at et par IP-adresser allerede er optaget af en anden tunnel, vil ipipou lave NAT fra offentlig til en alternativ privat IP, voila! - du kan lave tunneler, indtil havnene løber tør.
Fordi Ikke alle pakker i forbindelsen er signeret, så er denne simple beskyttelse sårbar over for MITM, så hvis der er en skurk, der lurer på stien mellem klienten og serveren, som kan lytte til trafikken og manipulere den, kan han omdirigere autentificerede pakker gennem en anden adresse og opret en tunnel fra en vært, der ikke er tillid til.
Hvis nogen har ideer til, hvordan man løser dette, mens størstedelen af trafikken efterlades i kernen, så tøv ikke med at sige fra.
Indkapsling i UDP har i øvrigt vist sig meget godt. Sammenlignet med indkapsling over IP er den meget mere stabil og ofte hurtigere på trods af den ekstra overhead af UDP-headeren. Dette skyldes det faktum, at de fleste værter på internettet kun fungerer godt med de tre mest populære protokoller: TCP, UDP, ICMP. Den håndgribelige del kan helt kassere alt andet, eller behandle det langsommere, fordi det kun er optimeret til disse tre.
For eksempel er det derfor, QUICK, som HTTP/3 er baseret på, blev oprettet oven på UDP og ikke oven på IP.
Nå, nok ord, det er tid til at se, hvordan det fungerer i den "virkelige verden".
Kamp
Bruges til at efterligne den virkelige verden iperf3. Med hensyn til graden af nærhed til virkeligheden er dette omtrent det samme som at efterligne den virkelige verden i Minecraft, men indtil videre duer det.
Deltagere i konkurrencen:
reference hovedkanal
helten i denne artikel er ipipou
OpenVPN med godkendelse men ingen kryptering
OpenVPN i altomfattende tilstand
WireGuard uden PresharedKey, med MTU=1440 (kun siden IPv4)
Tekniske data for nørder Metrics tages med følgende kommandoer:
på klienten:
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", чтобы лишние пакеты не плодить и не портить производительность.
Fugtigt grimt skilt
Server CPU-belastning er ikke særlig vejledende, fordi... Der er mange andre tjenester, der kører der, nogle gange spiser de ressourcer:
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 kanal
kanal pr. 1 optimistisk Gbps
I alle tilfælde er ipipou ret tæt på basiskanalen i ydeevne, hvilket er fantastisk!
Den ukrypterede openvpn-tunnel opførte sig ret mærkeligt i begge tilfælde.
Hvis nogen skal teste det, vil det være interessant at høre feedback.