ipipou: mer enn bare en ukryptert tunnel

Hva sier vi til IPv6-guden?

ipipou: mer enn bare en ukryptert tunnel
Det er riktig, vi vil si det samme til krypteringsguden i dag.

Her vil vi snakke om en ukryptert IPv4-tunnel, men ikke om en "varm lampe", men om en moderne "LED". Og det er også råsockets som blinker her, og det jobbes med pakker i brukerrommet.

Det er N tunnelprotokoller for enhver smak og farge:

  • stilig, fasjonabel, ungdom WireGuard
  • multifunksjonelle, som sveitsiske kniver, OpenVPN og SSH
  • gammel og ikke ond GRE
  • den mest enkle, raske, fullstendig ukrypterte IPIP
  • aktivt utvikler seg GENEVE
  • mange andre.

Men jeg er en programmerer, så jeg vil øke N bare med en brøkdel, og overlate utviklingen av ekte protokoller til Kommersant-utviklere.

I en ufødt prosjektDet jeg gjør nå er å nå verter bak NAT fra utsiden. Ved å bruke protokoller med voksenkryptografi for dette, kunne jeg ikke rokke ved følelsen av at det var som å skyte spurver ut av en kanon. Fordi tunnelen brukes stort sett kun til å stikke hull i NAT-e, intern trafikk er vanligvis også kryptert, men de drukner fortsatt i HTTPS.

Mens jeg undersøkte forskjellige tunnelprotokoller, ble min indre perfeksjonists oppmerksomhet trukket mot IPIP om og om igjen på grunn av dens minimale overhead. Men det har en og en halv betydelige ulemper for oppgavene mine:

  • det krever offentlige IP-er på begge sider,
  • og ingen autentisering for deg.

Derfor ble perfeksjonisten drevet tilbake i det mørke hjørnet av hodeskallen, eller hvor han nå enn sitter der.

Og så en dag, mens du leser artikler om naturlig støttede tunneler i Linux kom jeg over FOU (Foo-over-UDP), dvs. uansett, pakket inn i UDP. Så langt er det bare IPIP og GUE (Generisk UDP Encapsulation) som støttes.

«Her er sølvkulen! En enkel IPIP er nok for meg." - Jeg tenkte.

Faktisk viste det seg at kulen ikke var helt sølvfarget. Innkapsling i UDP løser det første problemet - du kan koble til klienter bak NAT fra utsiden ved hjelp av en forhåndsetablert tilkobling, men her blomstrer halvparten av den neste ulempen med IPIP i et nytt lys - hvem som helst fra et privat nettverk kan gjemme seg bak det synlige offentlig IP og klientport (i ren IPIP eksisterer ikke dette problemet).

For å løse dette halvannet problemet ble verktøyet født ipipou. Den implementerer en hjemmelaget mekanisme for autentisering av en ekstern vert, uten å forstyrre driften av kjerne-FOU, som raskt og effektivt vil behandle pakker i kjerneplass.

Vi trenger ikke manuset ditt!

Ok, hvis du kjenner den offentlige porten og IP-en til klienten (for eksempel, alle bak den går ingen steder, NAT prøver å kartlegge porter 1-i-1), kan du opprette en IPIP-over-FOU-tunnel med følgende kommandoer, uten noen skript.

på 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

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

der

  • ipipou* — navnet på det lokale tunnelnettverksgrensesnittet
  • 203.0.113.1 — offentlig IP-server
  • 198.51.100.2 — offentlig IP til klienten
  • 192.168.0.2 — klient IP tilordnet grensesnitt eth0
  • 10001 — lokal klientport for FOU
  • 20001 — offentlig klientport for FOU
  • 10000 — offentlig serverport for FOU
  • encap-csum — mulighet for å legge til en UDP-sjekksum til innkapslede UDP-pakker; kan erstattes av noencap-csum, for ikke å nevne, integritet er allerede kontrollert av det ytre innkapslingslaget (mens pakken er inne i tunnelen)
  • eth0 – lokalt grensesnitt som ipip-tunnelen vil bli bundet til
  • 172.28.0.1 — IP for klienttunnelgrensesnittet (privat)
  • 172.28.0.0 — IP-tunnelservergrensesnitt (privat)

Så lenge UDP-forbindelsen er i live, vil tunnelen fungere, men hvis den går i stykker, vil du være heldig - hvis klientens IP:-port forblir den samme - vil den leve, hvis de endres - vil den gå i stykker.

Den enkleste måten å snu alt tilbake på er å laste ut kjernemodulene: modprobe -r fou ipip

Selv om autentisering ikke er nødvendig, er ikke klientens offentlige IP og port alltid kjent og er ofte uforutsigbare eller variable (avhengig av NAT-type). Hvis du utelater encap-dport på serversiden vil ikke tunnelen fungere, det er ikke smart nok å ta fjerntilkoblingsporten. I dette tilfellet kan ipipou også hjelpe, eller WireGuard og andre som den kan hjelpe deg.

Hvordan virker det?

Klienten (som vanligvis er bak NAT) åpner en tunnel (som i eksempelet ovenfor), og sender en autentiseringspakke til serveren slik at den konfigurerer tunnelen på siden. Avhengig av innstillingene kan dette være en tom pakke (bare slik at serveren kan se den offentlige IP: tilkoblingsporten), eller med data som serveren kan identifisere klienten med. Dataene kan være en enkel passordfrase i klartekst (analogien med HTTP Basic Auth kommer til tankene) eller spesialdesignede data signert med en privat nøkkel (ligner HTTP Digest Auth bare sterkere, se funksjon client_auth i koden).

På serveren (siden med den offentlige IP-en), når ipipou starter, oppretter den en nfqueue-køhåndtering og konfigurerer nettfilter slik at de nødvendige pakkene sendes dit de skal være: pakker som initialiserer tilkoblingen til nfqueue-køen, og [nesten] resten går rett til lytter-FOU.

For de som ikke vet, er nfqueue (eller NetfilterQueue) en spesiell ting for amatører som ikke vet hvordan de skal utvikle kjernemoduler, som ved å bruke netfilter (nftables/iptables) lar deg omdirigere nettverkspakker til brukerplass og behandle dem der ved å bruke primitive midler for hånden: modifiser (valgfritt ) og gi den tilbake til kjernen, eller kast den.

For noen programmeringsspråk er det bindinger for å jobbe med nfqueue, for bash var det ingen (heh, ikke overraskende), jeg måtte bruke python: ipipou bruker Nettfilterkø.

Hvis ytelsen ikke er kritisk, kan du ved å bruke denne tingen relativt raskt og enkelt lage din egen logikk for å jobbe med pakker på et ganske lavt nivå, for eksempel lage eksperimentelle dataoverføringsprotokoller, eller trolle lokale og eksterne tjenester med ikke-standard oppførsel.

Raw sockets fungerer hånd i hånd med nfqueue, for eksempel når tunnelen allerede er konfigurert og FOU lytter på ønsket port, vil du ikke kunne sende en pakke fra samme port på vanlig måte - den er opptatt, men du kan ta og sende en tilfeldig generert pakke direkte til nettverksgrensesnittet ved å bruke en rå socket, selv om å generere en slik pakke vil kreve litt mer fiksing. Dette er hvordan pakker med autentisering opprettes i ipipou.

Siden ipipou kun behandler de første pakkene fra tilkoblingen (og de som klarte å lekke inn i køen før tilkoblingen ble opprettet), lider nesten ikke ytelsen.

Så snart ipipou-serveren mottar en autentisert pakke, opprettes en tunnel og alle påfølgende pakker i forbindelsen er allerede behandlet av kjernen som omgår nfqueue. Hvis tilkoblingen mislykkes, vil den første pakken av den neste bli sendt til nfqueue-køen, avhengig av innstillingene, hvis det ikke er en pakke med autentisering, men fra den sist huskede IP-en og klientporten, kan den enten sendes på eller forkastet. Hvis en autentisert pakke kommer fra en ny IP og port, blir tunnelen rekonfigurert til å bruke dem.

Den vanlige IPIP-over-FOU har ett problem til når du arbeider med NAT - det er umulig å lage to IPIP-tunneler innkapslet i UDP med samme IP, fordi FOU- og IPIP-modulene er ganske isolert fra hverandre. De. et par klienter bak samme offentlige IP vil ikke kunne koble til samme server samtidig på denne måten. I fremtiden, kanskje, vil det løses på kjernenivå, men dette er ikke sikkert. I mellomtiden kan NAT-problemer løses av NAT - hvis det skjer at et par IP-adresser allerede er okkupert av en annen tunnel, vil ipipou gjøre NAT fra offentlig til en alternativ privat IP, voila! - du kan lage tunneler til portene går tom.

Fordi Ikke alle pakker i forbindelsen er signert, da er denne enkle beskyttelsen sårbar for MITM, så hvis det er en skurk som lurer på banen mellom klienten og serveren som kan lytte til trafikken og manipulere den, kan han omdirigere autentiserte pakker gjennom en annen adresse og lag en tunnel fra en uklarert vert.

Hvis noen har ideer om hvordan du kan fikse dette mens du lar hoveddelen av trafikken ligge i kjernen, ikke nøl med å si ifra.

Innkapsling i UDP har forresten vist seg veldig godt. Sammenlignet med innkapsling over IP, er den mye mer stabil og ofte raskere til tross for den ekstra overheaden til UDP-headeren. Dette skyldes det faktum at de fleste verter på Internett fungerer bra bare med de tre mest populære protokollene: TCP, UDP, ICMP. Den håndgripelige delen kan forkaste alt annet, eller behandle det saktere, fordi det er optimalisert kun for disse tre.

For eksempel er dette grunnen til at QUICK, som HTTP/3 er basert på, ble opprettet på toppen av UDP, og ikke på toppen av IP.

Vel, nok ord, det er på tide å se hvordan det fungerer i den "virkelige verden".

Slag

Brukes til å etterligne den virkelige verden iperf3. Når det gjelder graden av nærhet til virkeligheten, er dette omtrent det samme som å emulere den virkelige verden i Minecraft, men foreløpig vil det gjøre det.

Deltakere i konkurransen:

  • referanse hovedkanal
  • helten i denne artikkelen er ipipou
  • OpenVPN med autentisering men ingen kryptering
  • OpenVPN i all-inclusive-modus
  • WireGuard uten PresharedKey, med MTU=1440 (bare siden IPv4)

Tekniske data for nerder
Beregninger tas 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 (kjø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"

Tunnelkonfigurasjon

ipipou
serveren
/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 autentisering)
serveren

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, autentisering, via UDP, alt som forventet)
Konfigurert ved hjelp av openvpn-administrere

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

Funn

Fuktig stygt skilt
Server CPU-belastning er ikke veldig veiledende, fordi... Det er mange andre tjenester som kjører der, noen ganger spiser de opp ressurser:

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: mer enn bare en ukryptert tunnel

ipipou: mer enn bare en ukryptert tunnel

kanal per 1 optimistisk Gbps

ipipou: mer enn bare en ukryptert tunnel

ipipou: mer enn bare en ukryptert tunnel

I alle tilfeller er ipipou ganske nær basekanalen i ytelse, noe som er flott!

Den ukrypterte openvpn-tunnelen oppførte seg ganske merkelig i begge tilfeller.

Hvis noen skal teste den, blir det interessant å høre tilbakemeldinger.

Må IPv6 og NetPrickle være med oss!

Kilde: www.habr.com

Legg til en kommentar