Co mówimy bogu IPv6?

Prawda, a bogu szyfrowania dziś powiemy to samo.
Tutaj będzie mowa o nieszyfrowanym tunelu IPv4, ale nie o «ciepłym lampowym», a o nowoczesnym « LED-owym». A ponadto pojawiają się surowe gniazda, i trwa praca z pakietami w przestrzeni użytkownika.
Są N protokołów tunelowania na każdy gust i kolor:
- stylowe, modne, młodzieżowe
- wielofunkcyjne, jak szwajcarskie noże, OpenVPN i SSH
- stary i niegroźny GRE
- maksymalnie prosty, szybki, zupełnie nieszyfrowany IPIP
- aktywnie rozwijający się
- wiele innych.
Ale ja jestem programistą, więc zwiększę N jedynie o odrobinę, a rozwój prawdziwych protokołów zostawię Ъ-deweloperom.
W jednym jeszcze nienarodzonym , którym obecnie się zajmuję, muszę dotrzeć do hostów za NAT-em z zewnątrz. Używając do tego protokołów z dorosłą kryptografią, wciąż miałem wrażenie, że to jak strzelanie z armaty do wróbli. Ponieważ tunel używany jest głównie do przebicia dziury w NAT, wewnętrzny ruch również zazwyczaj jest szyfrowany, wciąż promują HTTPS.
Badając różne protokoły tunelowania, uwaga mojego wewnętrznego perfekcjonisty za każdym razem zwracała się ku IPIP z powodu jego minimalnych kosztów operacyjnych. Ale ma on półtora istotnego niedostatku dla moich zadań:
- wymaga publicznych IP po obu stronach,
- i żadnej autoryzacji.
Dlatego perfekcjonista wrócił w mroczny kąt czaszki, albo gdziekolwiek tam siedzi.
I oto pewnego razu czytając artykuły o w Linuksie natknąłem się na FOU (Foo-over-UDP), tzn. coś przypadkowego, opakowanego w UDP. Jak na razie z czego przypadkowego wspierane są tylko IPIP i GUE (Generic UDP Encapsulation).
«Oto ona, srebrna kula! Mnie i prostego IPIP wystarczy.» — myślałem.
W rzeczywistości kula okazała się nie do końca srebrna. Inkluzja w UDP rozwiązuje pierwszy problem — do klientów za NAT-em można podłączyć się z zewnątrz, używając wcześniej nawiązanego połączenia, ale tutaj połowa następnego niedostatku IPIP rozkwita w nowym świetle — za widocznymi publicznymi IP i portem klienta może ukrywać się kto tylko chce z prywatnej sieci (w czystym IPIP tego problemu nie ma).
Aby rozwiązać ten półtorowy problem powstała utilita Zrealizowano w niej własny mechanizm autoryzacji zdalnego hosta, jednocześnie nie zakłócając działania jądrowego FOU, który sprawnie i efektywnie przetwarza pakiety w przestrzeni jądra.
Twój skrypt nie jest potrzebny!
Ok, jeśli znasz publiczny port i IP klienta (na przykład za NAT maper, który próbuje mapować porty 1-do-1), możesz stworzyć tunel IPIP-over-FOU za pomocą poniższych poleceń, bez użycia jakichkolwiek skryptów.
na serwerze:
# Подгрузить модуль ядра 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 kliencie:
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
# Opcje local, peer, peer_port, dev mogą nie być obsługiwane przez starsze jądra, można je pominąć.
# peer i peer_port są używane do stworzenia połączenia natychmiast przy tworzeniu FOU-listernera.
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
gdzie
ipipou*— nazwa lokalnego tunelowego interfejsu sieciowego203.0.113.1— publiczny IP serwera198.51.100.2— publiczny IP klienta192.168.0.2— IP klienta przypisany do interfejsu eth010001— lokalny port klienta dla FOU20001— publiczny port klienta dla FOU10000— publiczny port serwera dla FOUencap-csum— opcja do dodania sumy kontrolnej UDP do enkapsulowanych pakietów UDP; można zastąpićnoencap-csum, aby nie obliczać, integralność będzie kontrolowana przez zewnętrzną warstwę enkapsulacji (dopóki pakiet znajduje się wewnątrz tunelu)eth0— lokalny interfejs, do którego będzie przypisany tunel ipip172.28.0.1— IP interfejsu tunelowego klienta (prywatny)172.28.0.0— IP interfejsu tunelowego serwera (prywatny)
Dopóki UDP połączenie jest aktywne, tunel będzie działał, a gdy zostanie przerwane, to jak będzie — jeśli IP: port klienta pozostaną te same — będzie funkcjonować, zmienią się — przerwie.
Najprościej jest cofnąć wszystko, wyładowując moduły jądra: modprobe -r fou ipip
Nawet jeśli autoryzacja nie jest wymagana, publiczne IP i port klienta nie zawsze są znane i często są nieprzewidywalne lub zmienne (w zależności od rodzaju NAT). Jeśli pominiesz encap-dport na stronie serwera, tunel nie będzie działać, nie jest na tyle inteligentny, aby pobierać zdalny port połączenia. W takim przypadku ipipou również może pomóc, a może WireGuard i inne podobne narzędzia będą ci pomocne.
Jak to działa?
Klient (co zazwyczaj jest za NAT-em) tworzy tunel (jak w powyższym przykładzie) i wysyła pakiet z uwierzytelnieniem do serwera, aby ten skonfigurował tunel po swojej stronie. W zależności od ustawień pakiet ten może być pusty (po prostu, aby serwer zobaczył publiczne IP: port połączenia) lub zawierać dane, na podstawie których serwer zidentyfikuje klienta. Dane mogą być zwykłym hasłem w formie otwartego tekstu (przychodzi mi na myśl analogia z HTTP Basic Auth) lub podpisanymi danymi sformatowanymi w sposób szczególny z użyciem klucza prywatnego (analogicznie do HTTP Digest Auth, tylko bardziej zaawansowane, patrz funkcja client_auth w kodzie).
Na serwerze (strona z publicznym IP) podczas uruchamiania ipipou tworzy przetwornik kolejki nfqueue i konfiguruje netfilter tak, aby potrzebne pakiety były kierowane tam, gdzie należy: pakiety inicjujące połączenie do kolejki nfqueue, a [niemal] wszystkie pozostałe bezpośrednio do listenera FOU.
Dla osób niewtajemniczonych, nfqueue (lub NetfilterQueue) to specjalna funkcja dla amatorów, którzy nie potrafią rozwijać modułów jądra, która przy użyciu netfilter (nftables/iptables) umożliwia przekierowywanie pakietów sieciowych do przestrzeni użytkownika i przetwarzanie ich tam przy pomocy prostych narzędzi: modyfikowanie (opcjonalnie) i oddawanie ich z powrotem do jądra lub odrzucanie.
Dla niektórych języków programowania istnieją bindy do pracy z nfqueue, ale nie znalazłem ich dla bash (heh, nic dziwnego), musiałem użyć Pythona: ipipou korzysta z .
Jeśli wydajność nie jest krytyczna, przy użyciu tej funkcji można relatywnie szybko i łatwo stworzyć własną logikę działania z pakietami na dość niskim poziomie, na przykład opracowując eksperymentalne protokoły przesyłania danych lub trollując lokalne i zdalne usługi nietypowym zachowaniem.
Ręka w rękę z nfqueue działają surowe gniazda (raw sockets), na przykład gdy tunel jest już skonfigurowany, a FOU nasłuchuje na odpowiednim porcie, wysłanie pakietu z tego samego portu w zwykły sposób nie będzie możliwe — zajęte, ale można wziąć i wprowadzić dowolnie wygenerowany pakiet prosto do interfejsu sieciowego, używając surowego gniazda, choć generowanie takiego pakietu będzie wymagało nieco więcej wysiłku. Tak właśnie w ipipou tworzy się pakiety z uwierzytelnieniem.
Ponieważ ipipou przetwarza tylko pierwsze pakiety z połączenia (i te, które zdążyły się w kolejce przed nawiązaniem połączenia), wydajność prawie nie ucierpi.
Gdy serwer ipipou otrzyma pakiet, który przeszedł autoryzację, tunel zostaje utworzony i wszystkie dalsze pakiety w połączeniu są przetwarzane przez jądro, omijając nfqueue. Jeśli połączenie wygaśnie, pierwszy pakiet następnego połączenia zostanie skierowany do nfqueue, w zależności od ustawień. Jeśli nie jest to pakiet z autoryzacją, ale z ostatnio zapamiętanego IP i portu klienta, może zostać albo przepuszczony dalej, albo odrzucony. Jeśli autoryzowany pakiet przychodzi z nowych IP i portu, tunel zostaje dostosowany do ich użycia.
Zwykłe IPIP-over-FOU ma jeszcze jeden problem podczas pracy z NAT — nie można utworzyć dwóch tuneli IPIP enkapsulowanych w UDP z tymi samymi IP, ponieważ moduły FOU i IPIP są dość odseparowane od siebie. Oznacza to, że para klientów za jednym publicznym IP nie będzie mogła jednocześnie połączyć się z jednym serwerem w ten sposób. W przyszłości, , zostanie to rozwiązane na poziomie jądra, ale nie jest to pewne. A póki co, problemy z NAT można rozwiązać przy użyciu NAT — jeśli zdarzy się, że para adresów IP jest już zajęta przez inny tunel, ipipou wykona NAT z publicznego na alternatywny prywatny IP, voilà! — można tworzyć tuneli, dopóki porty się nie skończą.
Ponieważ nie wszystkie pakiety w połączeniu są podpisane, taka prosta ochrona jest podatna na MITM, więc jeśli przy ścieżce między klientem a serwerem czai się złośliwiec, który może podsłuchiwać ruch i nim zarządzać, może on przekierowywać pakiety z autoryzacją przez inny adres i utworzyć tunel z niewiarygodnego hosta.
Jeśli ktoś ma pomysły, jak to naprawić, pozostawiając główną część ruchu w jądrze, nie krępować się — wyrażać swoje zdanie.
A jeśli chodzi o enkapsulację w UDP, bardzo dobrze się sprawdziła. W porównaniu do enkapsulacji nad IP, jest znacznie bardziej stabilna i często szybsza, mimo dodatkowych kosztów związanych z nagłówkiem UDP. Wynika to z faktu, że w Internecie większość hostów działa poprawnie tylko z trzema najbardziej popularnymi protokołami: TCP, UDP, ICMP. Znacząca część może w ogóle odrzucać wszystko inne lub przetwarzać wolniej, ponieważ została zoptymalizowana tylko pod te trzy.
Na przykład, dlatego QUICK, na którym oparty jest HTTP/3, został zbudowany na bazie UDP, a nie na IP.
Czas już przestać gadać, pora zobaczyć, jak to działa w "prawdziwym świecie".
Bitwa
Do symulacji prawdziwego świata używa się iperf3. Poziom zbliżenia do rzeczywistości przypomina symulację prawdziwego świata w Minecrafcie, ale na razie wystarczy.
W zawodach biorą udział:
- referencyjny główny kanał
- bohater tego artykułu ipipou
- OpenVPN z autoryzacją, ale bez szyfrowania
- OpenVPN w trybie "wszystko w jednym"
- WireGuard bez PresharedKey, z MTU=1440 (bo IPv4 tylko)
Dane techniczne dla geeków
Metryki są zbierane za pomocą takich poleceń
na kliencie:
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"
# Gdzie "-b 12M" to przepustowość głównego kanału podzielona przez liczbę strumieni "-P", aby nie generować zbędnych pakietów i nie obniżać wydajności.
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"
Opóźnienie ICMP
ping -c 10 SERVER_IP | tail -1
na serwerze (uruchamiane równocześnie z 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"
Konfiguracja tuneli
ipipou
serwer
/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
客户端
/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 szyfrowania, z autoryzacją)
serwer
openvpn --genkey --secret ovpn.key # Następnie trzeba przekazać ovpn.key klientowi
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
客户端
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 (z szyfrowaniem, autoryzacją, przez UDP, wszystko jak należy)
Skonfigurowano przy użyciu
wireguard
serwer
/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
客户端
/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
Wyniki
Surowa, brzydka tabela
Obciążenie CPU serwera nie jest zbyt miarodajne, ponieważ działa tam wiele innych usług, które czasami zużywają zasoby:
protokół pasma[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps kanał z mikrokomputera (4 rdzenie) do VPS (1 rdzeń) przez Atlantyk
# czysty
UDP 20.4 99.80 93.34
TCP 19.2 99.67 96.68
ICMP opóźnienie min/średnie/maks/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 opóźnienie min/średnie/maks/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (tylko autoryzacja, bez szyfrowania)
UDP 19.3 99.89 72.90
TCP 16.1 95.95 88.46
ICMP opóźnienie min/średnie/maks/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (pełne szyfrowanie, autoryzacja itp.)
UDP 19.6 99.75 72.35
TCP 17.0 94.47 87.99
ICMP opóźnienie min/średnie/maks/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 opóźnienie min/średnie/maks/mdev = 217.925/223.601/230.696/3.266 ms
## około-1Gbps kanał między VPS w Europie a USA (1 rdzeń)
# czysty
UDP 729 73.40 39.93
TCP 363 96.95 90.40
ICMP opóźnienie min/średnie/maks/mdev = 106.867/106.994/107.126/0.066 ms
# ipipou
UDP 714 63.10 23.53
TCP 431 95.65 64.56
ICMP opóźnienie min/średnie/maks/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (tylko autoryzacja, bez szyfrowania)
UDP 193 17.51 1.62
TCP 12 95.45 92.80
ICMP opóźnienie min/średnie/maks/mdev = 107.191/107.334/107.559/0.116 ms
# wireguard
UDP 629 22.26 2.62
TCP 198 77.40 55.98
ICMP opóźnienie min/średnie/maks/mdev = 107.616/107.788/108.038/0.128 ms
kanał 20 Mbps


kanał 1 optymistyczny Gbps


We wszystkich przypadkach ipipou jest dość bliski wartościom podstawowego kanału, co jest doskonałe!
Nieszyfrowany tunel openvpn zachował się dość dziwnie w obu przypadkach.
Jeśli ktoś zdecyduje się przetestować, chętnie usłyszymy opinie.
Niech z nami będzie IPv6 i NetPrickle!
Źródło: habr.com
