ipipou: mer än bara en okrypterad tunnel

Vad säger vi till IPv6-guden?

ipipou: mer än bara en okrypterad tunnel
Det stämmer, vi kommer att säga detsamma till krypteringsguden idag.

Här kommer vi att prata om en okrypterad IPv4-tunnel, men inte om en "varm lampa", utan om en modern "LED". Och här blinkar även råa sockets och det pågår arbete med paket i användarutrymmet.

Det finns N tunnlingsprotokoll för alla smaker och färger:

  • snygg, fashionabel, ungdom WireGuard
  • multifunktionella, som schweiziska knivar, OpenVPN och SSH
  • gammal och inte ond GRE
  • den enklaste, snabbaste, helt okrypterade IPIP
  • aktivt utvecklas GENEVE
  • många andra.

Men jag är en programmerare, så jag kommer bara att öka N med en bråkdel och överlåta utvecklingen av riktiga protokoll till Kommersant-utvecklare.

I en ofödd projektetDet jag gör nu är att nå värdar bakom NAT utifrån. Genom att använda protokoll med vuxenkryptering för detta kunde jag inte skaka känslan av att det var som att skjuta sparvar ur en kanon. Därför att tunneln används för det mesta bara för att sticka hål i NAT-e, intern trafik är vanligtvis också krypterad, men de drunknar fortfarande i HTTPS.

Medan jag undersökte olika tunnlingsprotokoll drogs min inre perfektionists uppmärksamhet till IPIP om och om igen på grund av dess minimala overhead. Men det har en och en halv betydande nackdel för mina uppgifter:

  • det kräver offentliga IP-adresser på båda sidor,
  • och ingen autentisering för dig.

Därför drevs perfektionisten tillbaka in i det mörka hörnet av skallen, eller var han nu sitter där.

Och så en dag, medan jag läste artiklar om inbyggt stödda tunnlar i Linux stötte jag på FOU (Foo-over-UDP), d.v.s. vad som helst, insvept i UDP. Än så länge stöds endast IPIP och GUE (Generisk UDP Encapsulation).

"Här är silverkulan! En enkel IPIP räcker för mig.” - Jag trodde.

Kulan visade sig faktiskt inte vara helt silverfärgad. Inkapsling i UDP löser det första problemet - du kan ansluta till klienter bakom NAT utifrån med hjälp av en företablerad anslutning, men här blommar hälften av nästa nackdel med IPIP i ett nytt ljus - vem som helst från ett privat nätverk kan gömma sig bakom det synliga offentlig IP och klientport (i ren IPIP existerar inte detta problem).

För att lösa detta ett och ett halvt problem föddes verktyget ipipou. Den implementerar en hemmagjord mekanism för att autentisera en fjärrvärd, utan att störa driften av kärnans FOU, som snabbt och effektivt kommer att bearbeta paket i kärnutrymmet.

Vi behöver inte ditt manus!

Ok, om du känner till klientens publika port och IP (till exempel alla bakom den går ingenstans, NAT försöker kartlägga portar 1-i-1), kan du skapa en IPIP-over-FOU-tunnel med följande kommandon, utan några skript.

på servern:

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

där

  • ipipou* — Namnet på det lokala tunnelnätverkets gränssnitt
  • 203.0.113.1 — offentlig IP-server
  • 198.51.100.2 — Kundens offentliga IP
  • 192.168.0.2 — klient-IP tilldelad till gränssnittet eth0
  • 10001 — lokal klientport för FOU
  • 20001 — Offentlig kundhamn för FOU
  • 10000 — offentlig serverport för FOU
  • encap-csum — möjlighet att lägga till en UDP-kontrollsumma till inkapslade UDP-paket; kan ersättas av noencap-csum, för att inte tala om, integriteten kontrolleras redan av det yttre inkapslingsskiktet (medan paketet är inne i tunneln)
  • eth0 — Lokalt gränssnitt till vilket ipip-tunneln kommer att anslutas
  • 172.28.0.1 — IP för klienttunnelgränssnittet (privat)
  • 172.28.0.0 — IP-tunnelservergränssnitt (privat)

Så länge UDP-anslutningen är vid liv kommer tunneln att fungera, men om den går sönder har du tur - om klientens IP:-port förblir densamma - kommer den att leva, om de ändras - kommer den att gå sönder.

Det enklaste sättet att vända tillbaka allt är att ladda ur kärnmodulerna: modprobe -r fou ipip

Även om autentisering inte krävs är klientens publika IP och port inte alltid kända och är ofta oförutsägbara eller varierande (beroende på NAT-typ). Om du utelämnar encap-dport på serversidan kommer tunneln inte att fungera, det är inte smart nog att ta fjärranslutningsporten. I det här fallet kan ipipou också hjälpa dig, eller så kan WireGuard och liknande hjälpa dig.

Hur fungerar det?

Klienten (som vanligtvis ligger bakom NAT) öppnar en tunnel (som i exemplet ovan) och skickar ett autentiseringspaket till servern så att den konfigurerar tunneln på sin sida. Beroende på inställningarna kan detta vara ett tomt paket (bara så att servern kan se den offentliga IP:anslutningsporten), eller med data som servern kan identifiera klienten med. Data kan vara en enkel lösenordsfras i klartext (liknelsen med HTTP Basic Auth kommer att tänka på) eller specialdesignad data signerad med en privat nyckel (liknar HTTP Digest Auth bara starkare, se funktion client_auth i koden).

På servern (sidan med den offentliga IP-adressen), när ipipou startar, skapar den en nfqueue-köhanterare och konfigurerar nätfilter så att de nödvändiga paketen skickas dit de ska vara: paket som initierar anslutningen till nfqueue-kön och [nästan] resten går direkt till lyssnaren FOU.

För de som inte känner till är nfqueue (eller NetfilterQueue) en speciell sak för amatörer som inte vet hur man utvecklar kärnmoduler, som med hjälp av netfilter (nftables/iptables) låter dig omdirigera nätverkspaket till användarutrymme och bearbeta dem där med primitiva medel till hands: modifiera (valfritt ) och ge det tillbaka till kärnan, eller kassera det.

För vissa programmeringsspråk finns det bindningar för att arbeta med nfqueue, för bash fanns det inga (heh, inte förvånande), jag var tvungen att använda python: ipipou använder Nätfilterkö.

Om prestandan inte är kritisk kan du med hjälp av denna sak relativt snabbt och enkelt skapa din egen logik för att arbeta med paket på en ganska låg nivå, till exempel skapa experimentella dataöverföringsprotokoll, eller trolla lokala och fjärrtjänster med icke-standardiserat beteende.

Raw sockets fungerar hand i hand med nfqueue, till exempel när tunneln redan är konfigurerad och FOU lyssnar på önskad port, kommer du inte att kunna skicka ett paket från samma port på vanligt sätt - det är upptaget, men du kan ta och skicka ett slumpmässigt genererat paket direkt till nätverksgränssnittet med hjälp av en rå-socket, även om generering av ett sådant paket kräver lite mer mixtrande. Så här skapas paket med autentisering i ipipou.

Eftersom ipipou endast bearbetar de första paketen från anslutningen (och de som lyckades läcka in i kön innan anslutningen upprättades), blir prestandan nästan inte lidande.

Så fort ipipou-servern tar emot ett autentiserat paket skapas en tunnel och alla efterföljande paket i anslutningen behandlas redan av kärnan som förbigår nfqueue. Om anslutningen misslyckas, kommer det första paketet i nästa att skickas till nfqueue-kön, beroende på inställningarna, om det inte är ett paket med autentisering, men från den senast ihågkomna IP-adressen och klientporten, kan det antingen skickas på eller kasseras. Om ett autentiserat paket kommer från en ny IP och port, konfigureras tunneln om för att använda dem.

Den vanliga IPIP-över-FOU har ett problem till när man arbetar med NAT - det är omöjligt att skapa två IPIP-tunnlar inkapslade i UDP med samma IP, eftersom FOU- och IPIP-modulerna är ganska isolerade från varandra. De där. ett par klienter bakom samma publika IP kommer inte att kunna ansluta till samma server samtidigt på detta sätt. I framtiden, kanske, kommer det att lösas på kärnnivå, men detta är inte säkert. Under tiden kan NAT-problem lösas av NAT - om det händer att ett par IP-adresser redan är upptagna av en annan tunnel kommer ipipou att göra NAT från offentlig till en alternativ privat IP, voila! - du kan skapa tunnlar tills hamnarna tar slut.

Därför att Inte alla paket i anslutningen är signerade, då är detta enkla skydd sårbart för MITM, så om det finns en skurk som lurar på vägen mellan klienten och servern som kan lyssna på trafiken och manipulera den, kan han omdirigera autentiserade paket genom en annan adress och skapa en tunnel från en opålitlig värd.

Om någon har idéer om hur man fixar detta samtidigt som man lämnar huvuddelen av trafiken i kärnan, tveka inte att säga till.

Förresten, inkapsling i UDP har visat sig mycket väl. Jämfört med inkapsling över IP är den mycket stabilare och ofta snabbare trots den extra overheaden för UDP-huvudet. Detta beror på det faktum att de flesta värdar på Internet fungerar bra endast med de tre mest populära protokollen: TCP, UDP, ICMP. Den påtagliga delen kan helt kassera allt annat, eller bearbeta det långsammare, eftersom det är optimerat endast för dessa tre.

Det är till exempel därför som QUICK, som HTTP/3 bygger på, skapades ovanpå UDP och inte ovanpå IP.

Tja, nog med ord, det är dags att se hur det fungerar i den "verkliga världen".

Slåss

Används för att efterlikna den verkliga världen iperf3. När det gäller graden av närhet till verkligheten är detta ungefär detsamma som att emulera den verkliga världen i Minecraft, men för nu kommer det att duga.

Deltagare i tävlingen:

  • referens huvudkanal
  • hjälten i denna artikel är ipipou
  • OpenVPN med autentisering men ingen kryptering
  • OpenVPN i all-inclusive-läge
  • WireGuard utan PresharedKey, med MTU=1440 (eftersom endast IPv4)

Tekniska data för nördar
Mätvärden tas med följande kommandon:

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å servern (körs samtidigt 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"

Tunnelkonfiguration

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

kund
/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)
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

kund

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, allt som förväntat)
Konfigurerad med hjälp av openvpn-hantera

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

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

Resultat

Fukt ful skylt
Serverns CPU-belastning är inte särskilt vägledande, eftersom... Det finns många andra tjänster som körs där, ibland äter de upp resurser:

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 än bara en okrypterad tunnel

ipipou: mer än bara en okrypterad tunnel

kanal per 1 optimistisk Gbps

ipipou: mer än bara en okrypterad tunnel

ipipou: mer än bara en okrypterad tunnel

I alla fall är ipipou ganska nära baskanalen i prestanda, vilket är bra!

Den okrypterade openvpn-tunneln betedde sig ganska konstigt i båda fallen.

Om någon ska testa det ska det bli intressant att höra feedback.

Må IPv6 och NetPrickle vara med oss!

Källa: will.com

Lägg en kommentar