ipipou: mere end blot en ukrypteret tunnel

Hvad siger vi til IPv6's Gud?

ipipou: mere end blot en ukrypteret tunnel
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:

  • stilfuld, moderigtig, ungdommelig WireGuard
  • multifunktionel, som schweiziske knive, OpenVPN og SSH
  • gammel og ikke ond GRE
  • den mest enkle, hurtige, fuldstændig ukrypterede IPIP
  • aktivt udviklende GENEVE
  • mange andre.

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", чтобы лишние пакеты не плодить и не портить производительность.

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 latens

ping -c 10 SERVER_IP | tail -1

på serveren (kører samtidig med klienten):

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"

Tunnel konfiguration

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

kunde
/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 (ingen kryptering, med godkendelse)
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

kunde

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 (med kryptering, autentificering, via UDP, alt som forventet)
Konfigureret vha openvpn-administrere

trådbeskyttelse
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

kunde
/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

Fund

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

ipipou: mere end blot en ukrypteret tunnel

ipipou: mere end blot en ukrypteret tunnel

kanal pr. 1 optimistisk Gbps

ipipou: mere end blot en ukrypteret tunnel

ipipou: mere end blot en ukrypteret tunnel

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.

Må IPv6 og NetPrickle være med os!

Kilde: www.habr.com

Tilføj en kommentar