ipipou: více než jen nešifrovaný tunel

Co říkáme bohu IPv6?

ipipou: více než jen nešifrovaný tunel
Správně, a bohu šifrování dnes řekneme totéž.

Bude tu řeč o nešifrovaném IPv4 tunelu, ale ne o "teplém lampovém", nýbrž o moderním "LED". A také se tu mihotají syrové sockety, a probíhá práce s pakety v uživatelském prostoru.

Existuje N protokolů tunelování pro každého dle chuti a barvy:

  • stylový, módní, mladistvý WireGuard
  • multifunkční, jako švýcarské nože, OpenVPN a SSH
  • stará a ne zlá GRE
  • maximálně jednoduchý, rychlý, zcela nešifrovaný IPIP
  • aktivně se rozvíjející GENEVE
  • mnoho dalších.

Ale já jsem programátor, proto zvýším N jen o kousek, a vývoj skutečných protokolů nechám Ď-developerům.

V jednom ještě nenarozeném projektu, kterým se teď zabývám, potřebuji získat přístup k hostitelům za NATem zvenčí. Používajíc pro to protokoly se seriózní kryptografií, stále mě však neopouští pocit, že je to jako střílet z děla na vrabce. Jelikož se tunel používá většinou pouze k prorážení díry v NATu, vnitřní provoz je obvykle také zašifrovaný, všichni hrají na https.

Při zkoumání různých protokolů tunelování opakovaně přitahoval pozornost mého vnitřního perfekcionisty IPIP kvůli jeho minimálním nákladům. Ale má jeden a půl podstatných nedostatků pro mé úkoly:

  • vyžaduje veřejné IP na obou stranách,
  • a žádnou autentifikaci.

Proto se perfekcionista zase zavřel zpět do temného kouta své hlavy, nebo kde to sedí.

A tak jsem jednou při čtení článků o nativně podporovaných tunelech v Linuxu narazil na FOU (Foo-over-UDP), tj. něco, co se zabalí do UDP. Zatím jsou z toho, co se podporuje, pouze IPIP a GUE (generická UDP enkapsulace).

"Tady je stříbrná kulka! Mně stačí jednoduchý IPIP." – myslel jsem si.

Ve skutečnosti se kulka ukázala být nedokonale stříbrnou. Enkapsulace do UDP vyřešila první problém – ke klientům za NATem se dá připojit zvenčí použitím předem nastaveného spojení, ale tady se půlka dalšího nedostatku IPIP ukazuje v novém světle – za viditelnými veřejnými IP a portem klienta se může skrývat kdokoli z privátní sítě (v čistém IPIP tento problém není).

Pro řešení tohoto polo-problemu vznikla utilita ipipouV ní byl implementován domácí mechanismus autentizace vzdáleného hostitele, přičemž nedochází k narušení funkce jádra FOU, které bude rychle a efektivně zpracovávat pakety v prostorovém jádru.

Tvůj skript není potřeba!

Dobře, pokud jsou ti známy veřejný port a IP adresa klienta (například za ním všechny, kam popadá nechodí, NAT se snaží mapovat porty 1:1), můžeš vytvořit tunel IPIP-over-FOU následujícími příkazy, bez jakýchkoli skriptů.

na serveru:

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

na klientu:

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

# Možnosti local, peer, peer_port, dev nemusí starší jádra podporovat, lze je opomenout.
# peer a peer_port se používají k okamžitému vytvoření spojení při vytvoření FOU listeneru.
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

kde

  • ipipou* — název místního tunelového síťového rozhraní
  • 203.0.113.1 — veřejná IP adresa serveru
  • 198.51.100.2 — veřejná IP adresa klienta
  • 192.168.0.2 — IP adresa klienta, přidělená rozhraní eth0
  • 10001 — místní port klienta pro FOU
  • 20001 — veřejný port klienta pro FOU
  • 10000 — veřejný port serveru pro FOU
  • encap-csum — volba pro přidání kontrolního součtu UDP do enkapsulovaných UDP paketů; lze nahradit noencap-csum, aby se nepočítalo, integrita je tak kontrolována externí vrstvou enkapsulace (pokud packet zůstává uvnitř tunelu)
  • eth0 — místní rozhraní, k němuž bude připojen tunel IPIP
  • 172.28.0.1 — IP adresa tunelového rozhraní klienta (privátní)
  • 172.28.0.0 — IP adresa tunelového rozhraní serveru (privátní)

Dokud je UDP spojení aktivní, tunel bude funkční, a jakmile se přeruší, bude se to vyvíjet tak, jak to bude, — pokud zůstanou IP: port klienta stejné — bude trvat, změní-li se — dojde k přerušení.

Nejjednodušší je vzít vše zpět vyložením modulů jádra: modprobe -r fou ipip

I když autentizace není vyžadována, veřejné IP a port klienta nejsou vždy známy a často jsou nepředvídatelné nebo proměnné (v závislosti na typu NAT). Pokud vynecháš encap-dport na straně serveru, tunel nebude fungovat, není tak chytrý, aby převezmout vzdálený port spojení. V tomto případě ti může také pomoci ipipou nebo WireGuard a podobné nástroje.

Jak to funguje?

Klient (co obvykle za NAT-em) vytvoří tunel (jak je uvedeno v předchozím příkladu) a odešle balíček s autentizací na server, aby nastavil tunel na své straně. V závislosti na nastavení to může být prázdný balíček (jen aby server viděl veřejné IP: port připojení), nebo s daty, podle kterých server dokáže identifikovat klienta. Data mohou být jednoduchou heslovou frází v prostém textu (v hlavě mě napadá analogie s HTTP Basic Auth) nebo podepsaná soukromým klíčem speciálně zformátovaná data (podobně jako HTTP Digest Auth, ale silnější, viz funkce client_auth v kódu).

Na serveru (straně s veřejným IP) při spuštění ipipou vytvoří zpracovatel fronty nfqueue a nastaví netfilter tak, aby potřebné balíčky byly směrovány tam, kam mají: balíčky iniciující připojení do fronty nfqueue, a [téměř] všechny ostatní přímo do listeneru FOU.

Kdo neví, nfqueue (nebo NetfilterQueue) — to je speciální věc pro amatéry, kteří neumí vyvíjet jádrové moduly, která pomocí netfilter (nftables/iptables) umožňuje přesměrovávat síťové balíčky do uživatelského prostoru a tam je zpracovávat primitivními dostupnými prostředky: modifikovat (volitelně) a vrátit zpět jádru, nebo je zahodit.

Pro některé programovací jazyky existují vazby pro práci s nfqueue, ale pro bash se nic nenašlo (heh, nepřekvapuje), musel jsem použít python: ipipou používá NetfilterQueue.

Pokud není výkon kritický, pomocí této věci lze relativně rychle a jednoduše vyrobit vlastní logiku práce s balíčky na dost nízké úrovni, například vytvářet experimentální protokoly přenosu dat nebo trollit místní a vzdálené služby neobvyklým chováním.

Ruku v ruce s nfqueue pracují syrové sokety (raw sockets), například když je tunel již nastaven a FOU poslouchá na požadovaném portu, obvyklým způsobem nelze odeslat balíček z tohoto portu — je obsazen, ale můžete vzít a vygenerovaný balíček poslat přímo do síťového rozhraní pomocí syrového socketu, i když nad generováním takového balíčku bude třeba trochu více pracovat. Tak se vytvářejí v ipipou balíčky s autentizací.

Jelikož ipipou zpracovává pouze první pakety ze spojení (a také ty, které se dostaly do fronty před navázáním spojení), výkon téměř netrpí.

Jakmile ipipou server obdrží paket, který prošel autentizací, tunel se vytvoří a všechny následující pakety ve spojení jsou již zpracovány jádrem, obcházejíc nfqueue. Pokud spojení vyprší, první paket následujícího spojení bude směrován do fronty nfqueue, v závislosti na nastavení, pokud to není autentizační paket, ale má poslední zapamatovanou IP a port klienta, může být buď propuštěn dále, nebo odmítnut. Pokud autentizovaný paket přichází z nových IP a portu, tunel se přeprogramuje na jejich používání.

Obvyklé IPIP-over-FOU má další problém při práci s NAT — nelze vytvořit dva IPIP tunely encapsulované v UDP se stejnými IP, neboť moduly FOU a IPIP jsou dostatečně izolovány. Tedy pár klientů za jednou veřejnou IP se nebude moci současně připojit k jednomu serveru tímto způsobem. V budoucnu, možné, to vyřeší na úrovni jádra, ale to není jisté. Zatím lze problémy s NAT vyřešit pomocí NAT — pokud se stane, že pár IP adres již obsadil jiný tunel, ipipou provede NAT z veřejného na alternativní soukromou IP, voilà! — tunely lze vytvářet, dokud nezůstanou porty.

Jelikož nejsou všechny pakety ve spojení podepsány, taková jednoduchá ochrana je zranitelná vůči MITM, takže pokud se na cestě mezi klientem a serverem skrývá zloduch, který může naslouchat provozu a ovládat ho, může přesměrovat pakety s autentizací na jinou adresu a vytvořit tunel z nedůvěryhodného hostitele.

Pokud má někdo nápady, jak to opravit, zatímco většina provozu zůstává v jádře, neváhejte — ozvěte se.

Mimochodem, encapsulace v UDP se osvědčila velmi dobře. Ve srovnání s encapsulací nad IP je mnohem stabilnější a často rychlejší, ačkoliv má dodatečné náklady na hlavičku UDP. To je způsobeno tím, že na internetu většina hostitelů funguje rozumně jen s třemi nejpopulárnějšími protokoly: TCP, UDP, ICMP. Pozoruhodná část může dokonce ignorovat všechno ostatní, nebo to zpracovávat pomaleji, protože je optimalizována pouze pro tyto tři.

Například, proto byl QUICK, na jehož základě byl vytvořen HTTP/3, vytvořen právě nad UDP, nikoli nad IP.

Ale dost slov, pojďme se podívat, jak to funguje v „reálném světě“.

Bitva

Pro emulaci reálného světa se používá iperf3. Co se týče přiblížení k realitě, je to zhruba jako emulace reálného světa v Minecraftu, ale prozatím postačí.

V soutěži se účastní:

  • standardní hlavní kanál
  • hrdina tohoto článku ipipou
  • OpenVPN s autentizací, ale bez šifrování
  • OpenVPN v režimu „všechno zahrnuto“
  • WireGuard bez PresharedKey, s MTU=1440 (protože IPv4-only)

Technické údaje pro geeky
Metriky se snímají těmito příkazy

na klientu:

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"
# Kde "-b 12M" je šířka pásma hlavního kanálu, dělená počtem vláken "-P", aby se nevytvářely přebytečné pakety a nezhoršovala se výkonnost.

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 latence

ping -c 10 SERVER_IP | tail -1

na serveru (spouští se současně s klientem):

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"

Konfigurace tunelů

ipipou
server
/etc/ipipou/server.conf:

server
číslo 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

se dostal do backendu.
/etc/ipipou/client.conf:

client
číslo 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 (bez šifrování, s autentizací)
server

openvpn --genkey --secret ovpn.key  # Poté je třeba předat ovpn.key klientovi
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

se dostal do backendu.

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 (se šifrováním, autentizací, přes UDP, všechno tak, jak má být)
Nastavené pomocí openvpn-manage

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

se dostal do backendu.
/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

Výsledky

Syrová, ošklivá tabulka
Zatížení CPU serveru není příliš ukazatel, protože tam běží mnoho dalších služeb, které občas spotřebovávají zdroje:

proto šířka pásma[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps kanál z mikropočítače (4 jádra) do VPS (1 jádro) přes Atlantik
# čistý
UDP 20.4      99.80 93.34
TCP 19.2      99.67 96.68
ICMP latence 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 latence min/avg/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (pouze ověřování, žádné šifrování)
UDP 19.3      99.89 72.90
TCP 16.1      95.95 88.46
ICMP latence min/avg/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (plné šifrování, ověřování atd.)
UDP 19.6      99.75 72.35
TCP 17.0      94.47 87.99
ICMP latence 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 latence min/avg/max/mdev = 217.925/223.601/230.696/3.266 ms

## přibližně 1Gbps kanál mezi VPS v Evropě a USA (1 jádro)
# čistý
UDP 729      73.40 39.93
TCP 363      96.95 90.40
ICMP latence 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 latence min/avg/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (pouze ověřování, žádné šifrování)
UDP 193      17.51  1.62
TCP  12      95.45 92.80
ICMP latence 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 latence min/avg/max/mdev = 107.616/107.788/108.038/0.128 ms

kanál na 20 Mbps

ipipou: více než jen nešifrovaný tunel

ipipou: více než jen nešifrovaný tunel

kanál na 1 optimistický Gbps

ipipou: více než jen nešifrovaný tunel

ipipou: více než jen nešifrovaný tunel

Ve všech případech je ipipou poměrně blízko základnímu kanálu a to je skvělé!

Nešifrovaný tunel openvpn se choval docela podivně v obou případech.

Pokud se někdo chystá testovat, budou zajímavé vaše názory.

Kéž by s námi bylo IPv6 a NetPrickle!

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster