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

Co říkáme Bohu IPv6?

ipipou: víc než jen nešifrovaný tunel
To je pravda, totéž dnes řekneme bohu šifrování.

Zde budeme hovořit o nešifrovaném IPv4 tunelu, nikoli však o „teplém lampovém“, ale o moderním „LED“. A také zde blikají raw sockety a pracuje se s pakety v uživatelském prostoru.

Existuje N tunelovacích protokolů pro každý vkus a barvu:

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

Jsem ale programátor, takže N zvýším jen o zlomek a vývoj skutečných protokolů nechám na vývojářích Kommersantu.

V jednom nenarozeném projektTo, co teď dělám, je oslovit hostitele za NAT zvenčí. Když jsem k tomu použil protokoly s dospělou kryptografií, nemohl jsem se zbavit pocitu, že to bylo jako střílet vrabce z děla. Protože tunel slouží z větší části jen k vrtání děr v NAT-e, interní provoz je většinou také šifrovaný, ale stejně se topí v HTTPS.

Při zkoumání různých tunelovacích protokolů byla pozornost mého vnitřního perfekcionisty znovu a znovu přitahována k IPIP kvůli jeho minimální režii. Ale pro mé úkoly to má jednu a půl významné nevýhody:

  • vyžaduje veřejné IP adresy na obou stranách,
  • a žádné ověření pro vás.

Proto byl perfekcionista zahnán zpět do temného koutu lebky, nebo kamkoli tam sedí.

A pak jednoho dne, při čtení článků o nativně podporované tunely v Linuxu jsem narazil na FOU (Foo-over-UDP), tzn. cokoliv, zabalené v UDP. Zatím jsou podporovány pouze IPIP a GUE (Generic UDP Encapsulation).

„Tady je stříbrná kulka! Stačí mi jednoduchý IPIP.“ - Myslel jsem.

Ve skutečnosti se ukázalo, že kulka nebyla úplně stříbrná. Zapouzdření v UDP řeší první problém - ke klientům za NAT se můžete připojit zvenčí pomocí předem vytvořeného připojení, ale zde se polovina další nevýhody IPIP rozkvétá v novém světle - kdokoli z privátní sítě se může schovat za viditelné veřejná IP a klientský port (v čistém IPIP tento problém neexistuje).

K vyřešení tohoto jeden a půl problému se zrodil nástroj ipipou. Implementuje podomácku vyrobený mechanismus pro autentizaci vzdáleného hostitele, aniž by narušil provoz jádra FOU, který bude rychle a efektivně zpracovávat pakety v prostoru jádra.

Nepotřebujeme váš skript!

Dobře, pokud znáte veřejný port a IP klienta (například všichni za ním nikam nejdou, NAT se snaží mapovat porty 1-v-1), můžete vytvořit tunel IPIP-over-FOU pomocí následující 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 klientovi:

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

kde

  • ipipou* — název síťového rozhraní lokálního tunelu
  • 203.0.113.1 — veřejný IP server
  • 198.51.100.2 — veřejná IP klienta
  • 192.168.0.2 — IP klienta přiřazená rozhraní eth0
  • 10001 — místní klientský port pro FOU
  • 20001 — veřejný klientský port pro FOU
  • 10000 — veřejný port serveru pro FOU
  • encap-csum — možnost přidat kontrolní součet UDP k zapouzdřeným paketům UDP; lze nahradit noencap-csumnemluvě o tom, že integrita je již řízena vnější zapouzdřovací vrstvou (zatímco paket je uvnitř tunelu)
  • eth0 — lokální rozhraní, na které bude ipip tunel vázán
  • 172.28.0.1 — IP rozhraní klientského tunelu (soukromé)
  • 172.28.0.0 — Rozhraní serveru IP tunelu (soukromé)

Dokud bude připojení UDP živé, bude tunel fungovat, ale pokud se rozbije, budete mít štěstí - pokud IP: port klienta zůstane stejný - bude žít, pokud se změní - prolomí se.

Nejjednodušší způsob, jak vše vrátit zpět, je uvolnit moduly jádra: modprobe -r fou ipip

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

Jak to funguje?

Klient (který je obvykle za NAT) otevře tunel (jako ve výše uvedeném příkladu) a odešle autentizační paket na server, aby nakonfiguroval tunel na své straně. V závislosti na nastavení to může být prázdný paket (jen proto, aby server viděl veřejnou IP: spojovací port), nebo s daty, podle kterých může server identifikovat klienta. Data mohou být jednoduchá přístupová fráze v čistém textu (napadá mě analogie s HTTP Basic Auth) nebo speciálně navržená data podepsaná soukromým klíčem (podobně jako HTTP Digest Auth jen silnější, viz funkce client_auth v kódu).

Na serveru (na straně s veřejnou IP), když se ipipou spustí, vytvoří obsluhu fronty nfqueue a nakonfiguruje netfilter tak, aby se potřebné pakety posílaly tam, kde mají být: pakety inicializující připojení k frontě nfqueue a [téměř] vše ostatní jde přímo k posluchači FOU.

Pro neznalé je nfqueue (nebo NetfilterQueue) speciální věc pro amatéry, kteří nevědí, jak vyvíjet moduly jádra, které pomocí netfilter (nftables/iptables) umožňují přesměrovat síťové pakety do uživatelského prostoru a zpracovat je tam pomocí primitivní prostředky po ruce: upravit (volitelné) a vrátit to jádru nebo je zahodit.

Pro některé programovací jazyky existují vazby pro práci s nfqueue, pro bash žádná nebyla (heh, není se čemu divit), musel jsem použít python: ipipou používá NetfilterQueue.

Pokud výkon není kritický, pomocí této věci si můžete poměrně rychle a snadno vymyslet vlastní logiku pro práci s pakety na poměrně nízké úrovni, například vytvořit experimentální protokoly přenosu dat nebo trollovat místní a vzdálené služby s nestandardním chováním.

Raw sockety fungují ruku v ruce s nfqueue, například když je tunel již nakonfigurován a FOU naslouchá na požadovaném portu, paket ze stejného portu nebudete moci odeslat běžným způsobem - je zaneprázdněn, ale můžete vzít a odeslat náhodně vygenerovaný paket přímo do síťového rozhraní pomocí raw socketu, i když generování takového paketu bude vyžadovat trochu více práce. Takto se v ipipou vytvářejí pakety s autentizací.

Vzhledem k tomu, že ipipou zpracovává pouze první pakety z připojení (a ty, kterým se podařilo uniknout do fronty před navázáním připojení), výkon téměř neutrpí.

Jakmile server ipipou přijme ověřený paket, vytvoří se tunel a všechny následující pakety ve spojení jsou již zpracovávány jádrem, které obchází nfqueue. Pokud se spojení nezdaří, tak do fronty nfqueue bude podle nastavení odeslán první paket dalšího, pokud se nejedná o paket s autentizací, ale z poslední zapamatované IP a klientského portu, lze jej buď předat na nebo vyřazeny. Pokud ověřený paket pochází z nové IP adresy a portu, tunel se překonfiguruje tak, aby je používal.

Obvyklý IPIP-over-FOU má při práci s NAT ještě jeden problém - nelze vytvořit dva IPIP tunely zapouzdřené v UDP se stejnou IP, protože moduly FOU a IPIP jsou od sebe dost izolované. Tito. dvojice klientů za stejnou veřejnou IP se nebude moci tímto způsobem současně připojit ke stejnému serveru. Napříště, možná, bude to řešeno na úrovni jádra, ale není to jisté. Mezitím lze problémy s NATem vyřešit NATem - pokud se stane, že pár IP adres je již obsazen jiným tunelem, ipipou udělá NAT z veřejné na alternativní privátní IP, voila! - můžete vytvářet tunely, dokud nedojdou porty.

Protože Ne všechny pakety ve spojení jsou podepsané, pak je tato jednoduchá ochrana zranitelná vůči MITM, takže pokud na cestě mezi klientem a serverem číhá padouch, který může naslouchat provozu a manipulovat s ním, může přesměrovat ověřené pakety přes jinou adresu a vytvořte tunel z nedůvěryhodného hostitele.

Pokud má někdo nápady, jak to opravit a přitom ponechat většinu provozu v jádru, neváhejte se ozvat.

Mimochodem, zapouzdření v UDP se velmi osvědčilo. Ve srovnání se zapouzdřením přes IP je mnohem stabilnější a často rychlejší navzdory dodatečné režii hlavičky UDP. To je způsobeno skutečností, že většina hostitelů na internetu funguje dobře pouze se třemi nejoblíbenějšími protokoly: TCP, UDP, ICMP. Hmotná část může vše ostatní úplně zahodit, případně zpracovat pomaleji, protože je optimalizována pouze pro tyto tři.

To je například důvod, proč byl QUICK, na kterém je založen HTTP/3, vytvořen nad UDP, a ne nad IP.

No, dost slov, je čas vidět, jak to funguje v „reálném světě“.

Bitva

Používá se k napodobení skutečného světa iperf3. Pokud jde o míru blízkosti reality, je to přibližně stejné jako emulace skutečného světa v Minecraftu, ale zatím to bude stačit.

Účastníci soutěže:

  • referenční hlavní kanál
  • hrdinou tohoto článku je ipipou
  • OpenVPN s ověřováním, ale bez šifrování
  • OpenVPN v režimu all-inclusive
  • WireGuard bez PresharedKey, s MTU=1440 (od pouze IPv4)

Technická data pro geeky
Metriky se přebírají pomocí následujících příkazů:

na klientovi:

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 latence

ping -c 10 SERVER_IP | tail -1

na serveru (běží 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 tunelu

ipipou
serveru
/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

zákazník
/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 (bez šifrování, s ověřováním)
serveru

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

zákazník

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še podle očekávání)
Nakonfigurováno pomocí openvpn-manage

drátěný strážce
serveru
/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

zákazník
/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

Vlhké ošklivé znamení
Zatížení CPU serveru není příliš orientační, protože... Tam běží mnoho dalších služeb, někdy spotřebovávají zdroje:

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 kanál

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

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

kanál na 1 optimistický Gbps

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

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

Ve všech případech je ipipou výkonem docela blízko základnímu kanálu, což je skvělé!

Nezašifrovaný openvpn tunel se v obou případech choval dost zvláštně.

Pokud to někdo bude testovat, bude zajímavé slyšet zpětnou vazbu.

Ať jsou IPv6 a NetPrickle s námi!

Zdroj: www.habr.com

Přidat komentář