ipipou: mehr als nur ein unverschlüsselter Tunnel

Was sagen wir zum Gott von IPv6?

ipipou: mehr als nur ein unverschlüsselter Tunnel
Das ist richtig, das Gleiche werden wir heute dem Gott der Verschlüsselung sagen.

Hier sprechen wir von einem unverschlüsselten IPv4-Tunnel, aber nicht von einem „warmen Lampen“-Tunnel, sondern von einem modernen „LED“-Tunnel. Und hier blinken auch Raw-Sockets, und es wird mit Paketen im Userspace gearbeitet.

Es gibt N Tunnelprotokolle für jeden Geschmack und jede Farbe:

  • stilvoll, modisch, jugendlich WireGuard
  • multifunktional, wie Schweizer Messer, OpenVPN und SSH
  • alt und nicht böse GRE
  • das einfachste, schnellste und völlig unverschlüsselte IPIP
  • aktiv weiterentwickeln GENEVE
  • viele andere.

Da ich aber Programmierer bin, werde ich N nur um einen Bruchteil erhöhen und die Entwicklung echter Protokolle den Kommersant-Entwicklern überlassen.

Bei einem Ungeborenen ProjektWas ich jetzt mache, ist, Hosts hinter NAT von außen zu erreichen. Als ich dafür Protokolle mit Erwachsenenkryptographie verwendete, wurde ich das Gefühl nicht los, dass es so war, als würde man Spatzen aus einer Kanone schießen. Weil Der Tunnel wird größtenteils nur dazu verwendet, Löcher in NAT-e zu stechen, interner Datenverkehr wird normalerweise ebenfalls verschlüsselt, aber er ertrinkt immer noch in HTTPS.

Bei der Recherche zu verschiedenen Tunnelprotokollen wurde mein innerer Perfektionist aufgrund des minimalen Overheads immer wieder auf IPIP aufmerksam. Aber es hat für meine Aufgaben eineinhalb wesentliche Nachteile:

  • es erfordert öffentliche IPs auf beiden Seiten,
  • und keine Authentifizierung für Sie.

Deshalb wurde der Perfektionist in die dunkle Ecke des Schädels zurückgedrängt, oder wo immer er dort sitzt.

Und dann eines Tages, während ich Artikel darüber las nativ unterstützte Tunnel unter Linux bin ich auf FOU (Foo-over-UDP) gestoßen, d.h. Was auch immer, verpackt in UDP. Bisher werden nur IPIP und GUE (Generic UDP Encapsulation) unterstützt.

„Hier ist die Wunderwaffe! Mir reicht ein einfaches IPIP.“ - Ich dachte.

Tatsächlich stellte sich heraus, dass die Kugel nicht ganz aus Silber war. Die Kapselung in UDP löst das erste Problem – Sie können von außen über eine vorab eingerichtete Verbindung eine Verbindung zu Clients hinter NAT herstellen, aber hier erblüht der halbe nächste Nachteil von IPIP in einem neuen Licht – jeder aus einem privaten Netzwerk kann sich hinter dem Sichtbaren verstecken öffentliche IP und Client-Port (bei reinem IPIP besteht dieses Problem nicht).

Um dieses eineinhalbfache Problem zu lösen, wurde das Dienstprogramm geboren ipipou. Es implementiert einen hausgemachten Mechanismus zur Authentifizierung eines Remote-Hosts, ohne den Betrieb der Kernel-FOU zu unterbrechen, die Pakete im Kernel-Space schnell und effizient verarbeitet.

Wir brauchen Ihr Skript nicht!

Ok, wenn Sie den öffentlichen Port und die IP des Clients kennen (z. B. alle dahinter gehen nirgendwo hin, NAT versucht, Ports 1-in-1 abzubilden), können Sie mit dem einen IPIP-über-FOU-Tunnel erstellen Befolgen von Befehlen, ohne Skripte.

auf dem 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

auf dem Client:

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

wo

  • ipipou* – Name der lokalen Tunnelnetzwerkschnittstelle
  • 203.0.113.1 — öffentlicher IP-Server
  • 198.51.100.2 — öffentliche IP des Clients
  • 192.168.0.2 — Client-IP, zugewiesen der Schnittstelle eth0
  • 10001 — lokaler Client-Port für FOU
  • 20001 — öffentlicher Client-Port für FOU
  • 10000 — öffentlicher Server-Port für FOU
  • encap-csum – Option zum Hinzufügen einer UDP-Prüfsumme zu gekapselten UDP-Paketen; kann ersetzt werden durch noencap-csumGanz zu schweigen davon, dass die Integrität bereits durch die äußere Kapselungsschicht kontrolliert wird (während sich das Paket im Tunnel befindet).
  • eth0 – lokale Schnittstelle, an die der IPIP-Tunnel gebunden wird
  • 172.28.0.1 — IP der Client-Tunnelschnittstelle (privat)
  • 172.28.0.0 — IP-Tunnelserverschnittstelle (privat)

Solange die UDP-Verbindung aktiv ist, ist der Tunnel funktionsfähig, aber wenn er kaputt geht, haben Sie Glück – wenn der IP-Port des Clients gleich bleibt, bleibt er bestehen, wenn er sich ändert, wird er kaputt gehen.

Der einfachste Weg, alles wieder rückgängig zu machen, besteht darin, die Kernel-Module zu entladen: modprobe -r fou ipip

Auch wenn keine Authentifizierung erforderlich ist, sind die öffentliche IP-Adresse und der öffentliche Port des Clients nicht immer bekannt und oft unvorhersehbar oder variabel (je nach NAT-Typ). Wenn Sie es weglassen encap-dport Auf der Serverseite funktioniert der Tunnel nicht, er ist nicht intelligent genug, um den Remote-Verbindungsport zu nutzen. In diesem Fall kann auch ipipou helfen, oder WireGuard und andere wie es können Ihnen helfen.

Wie funktioniert es?

Der Client (der sich normalerweise hinter NAT befindet) öffnet einen Tunnel (wie im obigen Beispiel) und sendet ein Authentifizierungspaket an den Server, damit dieser den Tunnel auf seiner Seite konfiguriert. Abhängig von den Einstellungen kann es sich um ein leeres Paket handeln (nur damit der Server den öffentlichen IP-Verbindungsport sehen kann) oder um Daten, anhand derer der Server den Client identifizieren kann. Bei den Daten kann es sich um eine einfache Passphrase im Klartext handeln (da fällt mir eine Analogie zu HTTP Basic Auth ein) oder um speziell gestaltete Daten, die mit einem privaten Schlüssel signiert sind (ähnlich wie bei HTTP Digest Auth, nur stärker, siehe Funktion). client_auth im Code).

Auf dem Server (der Seite mit der öffentlichen IP) erstellt ipipou beim Start einen nfqueue-Warteschlangenhandler und konfiguriert Netfilter so, dass die erforderlichen Pakete dorthin gesendet werden, wo sie sein sollten: Pakete, die die Verbindung zur nfqueue-Warteschlange initialisieren, und [fast] Der Rest geht direkt an den Hörer FOU.

Für diejenigen, die es nicht wissen: nfqueue (oder NetfilterQueue) ist etwas Besonderes für Amateure, die nicht wissen, wie man Kernel-Module entwickelt, mit denen Sie mithilfe von Netfilter (nftables/iptables) Netzwerkpakete in den Benutzerbereich umleiten und dort verarbeiten können primitiv bedeutet zur Hand: modifizieren (optional) und an den Kernel zurückgeben oder verwerfen.

Für einige Programmiersprachen gibt es Bindungen für die Arbeit mit nfqueue, für Bash gab es keine (heh, nicht überraschend), ich musste Python verwenden: ipipou nutzt NetfilterQueue.

Wenn die Leistung nicht entscheidend ist, können Sie mit diesem Ding relativ schnell und einfach Ihre eigene Logik für die Arbeit mit Paketen auf relativ niedrigem Niveau zusammenstellen, zum Beispiel experimentelle Datenübertragungsprotokolle erstellen oder lokale und Remote-Dienste mit nicht standardmäßigem Verhalten trollen.

Raw-Sockets arbeiten Hand in Hand mit nfqueue. Wenn der Tunnel beispielsweise bereits konfiguriert ist und FOU den gewünschten Port abhört, können Sie nicht auf die übliche Weise ein Paket von demselben Port senden – er ist ausgelastet, aber Sie können ein zufällig generiertes Paket über einen Raw-Socket direkt an die Netzwerkschnittstelle senden, obwohl das Generieren eines solchen Pakets etwas mehr Aufwand erfordert. So entstehen in ipipou Pakete mit Authentifizierung.

Da ipipou nur die ersten Pakete der Verbindung verarbeitet (und diejenigen, die vor dem Verbindungsaufbau in die Warteschlange gelangen konnten), leidet die Leistung nahezu nicht.

Sobald der ipipou-Server ein authentifiziertes Paket empfängt, wird ein Tunnel erstellt und alle nachfolgenden Pakete in der Verbindung werden bereits vom Kernel unter Umgehung von nfqueue verarbeitet. Wenn die Verbindung fehlschlägt, wird das erste Paket des nächsten an die nfqueue-Warteschlange gesendet. Wenn es sich nicht um ein Paket mit Authentifizierung handelt, sondern von der zuletzt gespeicherten IP und dem Client-Port, kann es entweder weitergeleitet werden eingeschaltet oder verworfen. Wenn ein authentifiziertes Paket von einer neuen IP und einem neuen Port kommt, wird der Tunnel neu konfiguriert, um diese zu verwenden.

Das übliche IPIP-über-FOU hat bei der Arbeit mit NAT ein weiteres Problem: Es ist unmöglich, zwei in UDP gekapselte IPIP-Tunnel mit derselben IP zu erstellen, da die FOU- und IPIP-Module ziemlich isoliert voneinander sind. Diese. Ein Clientpaar hinter derselben öffentlichen IP-Adresse kann auf diese Weise nicht gleichzeitig eine Verbindung zum selben Server herstellen. In der Zukunft, vielleicht, es wird auf Kernel-Ebene gelöst, aber das ist nicht sicher. In der Zwischenzeit können NAT-Probleme durch NAT gelöst werden – wenn es passiert, dass ein IP-Adressenpaar bereits von einem anderen Tunnel belegt ist, führt ipipou NAT von einer öffentlichen zu einer alternativen privaten IP durch, voila! - Sie können Tunnel erstellen, bis die Ports erschöpft sind.

Weil Da nicht alle Pakete in der Verbindung signiert sind, ist dieser einfache Schutz anfällig für MITM. Wenn also ein Bösewicht auf dem Weg zwischen dem Client und dem Server lauert, der den Datenverkehr abhören und manipulieren kann, kann er authentifizierte Pakete umleiten über eine andere Adresse und erstellen Sie einen Tunnel von einem nicht vertrauenswürdigen Host aus.

Wenn jemand Ideen hat, wie man dieses Problem beheben und gleichzeitig den Großteil des Datenverkehrs im Kern belassen kann, kann man sich gerne melden.

Die Kapselung in UDP hat sich übrigens sehr gut bewährt. Im Vergleich zur Kapselung über IP ist sie trotz des zusätzlichen Overheads des UDP-Headers deutlich stabiler und oft schneller. Dies liegt daran, dass die meisten Hosts im Internet nur mit den drei gängigsten Protokollen gut funktionieren: TCP, UDP, ICMP. Der materielle Teil kann alles andere komplett verwerfen oder langsamer verarbeiten, da er nur für diese drei optimiert ist.

Aus diesem Grund wurde beispielsweise QUICK, auf dem HTTP/3 basiert, auf UDP und nicht auf IP erstellt.

Nun, genug der Worte, es ist Zeit zu sehen, wie es in der „realen Welt“ funktioniert.

Schlacht

Wird verwendet, um die reale Welt nachzuahmen iperf3. Vom Grad der Realitätsnähe her entspricht dies in etwa der Nachbildung der realen Welt in Minecraft, reicht aber vorerst aus.

Teilnehmer am Wettbewerb:

  • Referenz-Hauptkanal
  • Der Held dieses Artikels ist Iipipou
  • OpenVPN mit Authentifizierung, aber ohne Verschlüsselung
  • OpenVPN im All-Inclusive-Modus
  • WireGuard ohne PresharedKey, mit MTU=1440 (da nur IPv4)

Technische Daten für Geeks
Metriken werden mit den folgenden Befehlen erfasst:

auf dem Client:

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-Latenz

ping -c 10 SERVER_IP | tail -1

auf dem Server (läuft gleichzeitig mit dem Client):

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

Client
/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 (keine Verschlüsselung, mit Authentifizierung)
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

Client

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 (mit Verschlüsselung, Authentifizierung, über UDP, alles wie erwartet)
Konfiguriert mit 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

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

Ergebnisse

Feuchtes hässliches Schild
Die CPU-Auslastung des Servers ist nicht sehr aussagekräftig, weil... Dort laufen noch viele andere Dienste, die manchmal Ressourcen verschlingen:

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 Mbit/s-Kanal

ipipou: mehr als nur ein unverschlüsselter Tunnel

ipipou: mehr als nur ein unverschlüsselter Tunnel

Kanal pro 1 optimistisches Gbit/s

ipipou: mehr als nur ein unverschlüsselter Tunnel

ipipou: mehr als nur ein unverschlüsselter Tunnel

In allen Fällen kommt ipipou in der Leistung dem Basiskanal ziemlich nahe, was großartig ist!

Der unverschlüsselte OpenVPN-Tunnel verhielt sich in beiden Fällen recht seltsam.

Wenn jemand es testen möchte, wird es interessant sein, Feedback zu hören.

Mögen IPv6 und NetPrickle mit uns sein!

Source: habr.com

Kommentar hinzufügen