Ich habe zu Hause einen Wireguard-Server laufen, um von außen auf mein Heimnetzwerk zugreifen zu können. Wireguard verwendet UDP. Leider kann ich nicht einfach einen IPv4-UDP-Port in meiner Fritzbox öffnen, wie es früher funktionierte, weil mein Provider mich hinter CG-NAT gestellt hat, ich muss IPv6 verwenden, aber IPv6 ist an meinem Arbeitsplatz noch nicht nutzbar.
Zu Hause habe ich also IPv6 und bei der Arbeit habe ich IPv4 und keine Möglichkeit, mein Heimnetzwerk zu erreichen.
Ich habe auch einen VPS mit einer öffentlichen IPv4, zu dem ich Reverse-SSH-Tunnel für den TCP-Verkehr einrichten könnte. Leider unterstützen die SSH-Tunnel nur TCP und man sollte sowieso keine VPN-Verbindungen über TCP herstellen, weshalb Wireguard nur UDP verwendet.
VPN-Server VPN-Client
(UDP) | | (IPv4 UDP)
| | | not possible |
-----------|--------| <-------------------
| |
NAT CG-NAT
VPN-Server VPN-Client
(UDP) (IPv4 UDP)
| |
| |
inside agent outside agent
tunnel tunnel
|| | | ||
|| punch| punch| ||
| --------->|-------->|-------------------- |
-----------|<--------|<--------------------
| |
| |
NAT CG-NAT
Der externe Agent läuft auf einem VPS mit öffentlicher IPv4
Der interne Agent sendet UDP-Datagramme an die öffentliche IP und den öffentlichen Port des externen Agenten. Dadurch werden Löcher in beide NATs geschlagen. Der externe Agent empfängt diese Datagramme und lernt von ihrer Quelladresse und ihrem Port, wie er Datagramme an den internen Agenten zurücksendet .
Der VPN-Client kann UDP an den externen Agenten senden und diese Datagramme werden an den internen Agenten und von dort an den VPN-Server weitergeleitet. Der VPN-Server kann dem internen Agenten antworten, diese werden an den externen Agenten weitergeleitet und von dort an der VPN-Client.
Der externe Agent erscheint dem Client als VPN-Server und der interne Agent dem Server als VPN-Client.
Mehrere Client-Verbindungen sind möglich, da für jeden neuen Client, der eine Verbindung nach außen herstellt, ein Tunnel und ein neuer Socket auf dem Inside-Agenten verwendet werden, sodass es für den Server so aussieht, als würden mehrere Clients auf dem Host des Inside-Agenten ausgeführt.
Klonen oder entpacken Sie den Code auf beiden Computern und erstellen Sie ihn. Sie benötigen mindestens make und gcc. Geben Sie das Quellverzeichnis ein und verwenden Sie den Befehl
$ make
Nach erfolgreicher Erstellung befindet sich der binäre udp-tunnel
im selben Ordner. Jetzt können Sie es entweder direkt von einem Terminal aus starten (natürlich mit den richtigen Optionen), um ein paar schnelle Tests durchzuführen, oder Sie können es mithilfe des Makefiles installieren.
Der kompilierte binäre udp-tunnel
kann entweder als interner Agent oder als externer Agent fungieren, je nachdem, welche Optionen Sie in der Befehlszeile übergeben.
Nehmen wir als Beispiel an, dass der VPN-Server auf UDP 1234 lauscht, auf localhost läuft (derselbe wie der interne Agent) und die externe Maschine Jump.example.com ist und wir möchten, dass dieser auf UDP-Port 9999 lauscht.
Mit dem Inside-Host beginnen wir
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999
Auf dem externen Host beginnen wir mit
$ ./udp-tunnel -l 9999
Das Makefile enthält drei Installationsziele: install
, um nur die Binärdatei zu installieren, install-outside
und install-inside
, um auch die systemd-Dienstdateien zu installieren. Für die beiden letztgenannten müssen Variablen übergeben werden, damit sie ordnungsgemäß funktionieren.
Um den externen Agenten auf dem Jump-Host zu installieren (vorausgesetzt, Sie möchten Port 9999), führen Sie diesen Befehl aus:
$ sudo make install-outside listen=9999
Dadurch wird die Binärdatei in /usr/local/bin/
installiert und eine Systemd-Dienstdatei in /etc/systemd/system/
installiert, die den erforderlichen Befehl enthält, um sie im externen Agentenmodus mit Port 9999 zu starten.
Um den Inside-Agenten auf der Inside-Maschine zu installieren, verwenden Sie den folgenden Befehl (angenommen, Ihr VPN-Server ist localhost:1234 und Ihr Jump-Host ist jump.example.com):
$ sudo make install-inside service=localhost:1234 outside=jump.example.com:9999
Dadurch wird die Binärdatei erneut in /usr/local/bin/
und eine Systemd-Unit-Datei in /etc/systemd/system/
installiert.
An dieser Stelle möchten Sie vielleicht einen kurzen Blick in die Systemd-Unit-Dateien werfen, um zu sehen, wie die Binärdatei verwendet wird, und um zu überprüfen, ob die Optionen korrekt sind. Die Optionen sollten im Schnelltest wie oben beschrieben aussehen.
Nachdem die Systemd-Dateien installiert und auf Richtigkeit überprüft wurden, sind sie noch nicht für den Autostart aktiviert. Sie müssen sie aktivieren und starten. Auf der Inside-Maschine:
$ sudo systemctl enable udp-tunnel-inside.service
$ sudo systemctl start udp-tunnel-inside.service
und auf der Außenmaschine
$ sudo systemctl enable udp-tunnel-outside.service
$ sudo systemctl start udp-tunnel-outside.service
Es gibt keine Verschlüsselung. Pakete werden so weitergeleitet, wie sie sind. Es wird davon ausgegangen, dass der Dienst, den Sie tunneln, seine Daten selbst schützen oder verschlüsseln kann. Normalerweise ist dies bei VPN-Verbindungen der Fall.
Darüber hinaus möchte ein Angreifer möglicherweise die Keepalive-Pakete des internen Agenten fälschen, um den externen Agenten zu verwirren und den Tunnel auf seinen eigenen Computer umzuleiten, was zu einer Dienstunterbrechung führt. Um diesen sehr einfachen Angriff zu verhindern, können die Keepalive-Datagramme mit einem Hash-basierten Nachrichtenauthentifizierungscode authentifiziert werden. Sie können ein vorab freigegebenes Passwort mit der Option -k an beiden Tunnelenden verwenden, um diese Funktion zu aktivieren.
Auf dem Inside-Host würden Sie es so verwenden
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999 -k mysecretpassword
Auf dem externen Host würden Sie es beginnen
$ ./udp-tunnel -l 9999 -k mysecretpassword
Nachdem Sie die oben beschriebenen Installationsschritte erfolgreich ausgeführt haben, möchten Sie möglicherweise Ihre Systemd-Dateien auf beiden Enden manuell bearbeiten und eine Option -k hinzufügen, dann neu laden und auf beiden Enden neu starten.
Die Keepalive-Nachricht enthält dann einen SHA-256 über dieses Passwort und über eine streng ansteigende Nonce, die nur genau einmal verwendet werden kann, um einfache Replay-Angriffe zu verhindern.
Beide Agenten verwalten eine Liste von Verbindungen. Jede Verbindung speichert Socket-Adressen und Socket-Handles, die dieser bestimmten Client-Verbindung zugeordnet sind.
Beim Start initiiert der interne Agent einen ausgehenden Tunnel zum externen Agenten, markiert ihn als unbenutzt und sendet darüber Keepalive-Pakete. Der externe Agent sieht diese Pakete und fügt diesen Tunnel mit seiner Quelladresse zu seiner eigenen Verbindungsliste hinzu. Die Keepalive-Pakete sind mit einer Nonce und einem Authentifizierungscode signiert, sodass sie nicht gefälscht oder abgespielt werden können.
Wenn ein Client eine Verbindung herstellt, sendet der externe Agent die Clientdaten über diesen ungenutzten Ersatztunnel. Der interne Agent sieht dies, markiert den Tunnel als aktiv, erstellt einen Socket für die Kommunikation mit dem Dienst und leitet Daten in beide Richtungen weiter. Es wird dann auch sofort ein weiterer neuer ausgehender Ersatztunnel erstellt, um für den nächsten eingehenden Client bereit zu sein.
Wenn der neue Ersatztunnel beim externen Agenten ankommt, fügt er ihn seiner eigenen Verbindungsliste hinzu, um den nächsten verbindenden Client sofort bedienen zu können.
Zu diesem Zeitpunkt haben beide Agenten zwei Tunnel in ihrer Liste, einer ist aktiv und einer ist frei und wartet auf die nächste Client-Verbindung.
Inaktivitäts-Timeouts stellen sicher, dass tote Tunnel (solche, die in der Vergangenheit als aktiv markiert wurden, aber seit einiger Zeit keine Client-Daten mehr haben) gelöscht und ihre Sockets geschlossen werden. Der interne Agent erkennt, dass über einen längeren Zeitraum keine weitergeleiteten Daten vorhanden sind, entfernt sie aus seiner eigenen Liste und schließt seine Sockets. Einige Zeit später erkennt der externe Agent, dass für diese Verbindung keine Keepalives mehr eingehen, und entfernt sie aus seiner eigenen Liste auch.
Dieser Code ist noch sehr experimentell, also bauen Sie darauf noch kein Multi-Millionen-Dollar-Geschäft auf, zumindest noch nicht. Für mich erfüllt es seinen Zweck vollkommen, aber es kann für Sie zum Absturz, Brennen und Explodieren Ihres Servers führen. Sie wurden gewarnt.