Der VPN-Client verbindet sich, der Browser funktioniert, und der eine Befehl, der Ihnen wirklich wichtig ist, geht weiterhin über Ihre Heimverbindung hinaus. Oder das Gegenteil passiert: Der Tunnel übernimmt die Standardroute, und der lokale Dateiserver verschwindet vom Desktop. Ich bin darauf bei einem Build-Agenten gestoßen, der einen internen Artefakt-Spiegel erreichen musste, während der Rest des Hosts im Büro-LAN blieb, und der Client bot genau zwei Möglichkeiten: der gesamte Verkehr oder keiner. Die Anforderung ist in beide Richtungen dieselbe, dieses Programm und nicht der ganze Rechner, und die meisten Clients haben kein Kontrollkästchen dafür, weil die Routentabelle nie gewusst hat, was ein Programm ist.

Warum die Routentabelle Ihr Programm nicht sehen kann

Wenn ein Prozess ein Paket sendet, schlägt der Stack die Zieladresse in der Routentabelle nach. Weder der Name der ausführbaren Datei noch der Port gehen in diese Suche ein. Domainnamen scheitern aus demselben Grund: Ein Tunnel transportiert Verkehr zu einer Adresse, und wenn ein Name zu einer aufgelöst ist, ist das Routing der einzige Hebel, den der Kernel noch hat. Deshalb bedeutet „VPN nur für diese Seite“ am Ende einen Proxy, und „VPN nur für dieses Programm“ bedeutet einen aus einer kleinen Gruppe von Mechanismen.

MechanismWhat selectsCoversOn tunnel drop
Socket bindsource addressa client you controlconnections fail
UID rule (Linux)owning userevery process of one accountconnections fail
Firewall profile (Windows)adapter profileone programfails closed
Network namespacenamespace membershipeverything started insideconnections fail
SOCKS hopapp proxy settingone app or one browser profileconnections fail

Nichts davon ist eine Route pro Prozess. Jedes verlagert die Entscheidung auf eine Ebene, die das Programm, den Benutzer oder die eigene Konfiguration der Anwendung sieht.

Ein einzelnes Ziel ist immer noch eine Route, keine Programmregel. Unter macOS besteht das übliche Rezept darin, „Gesamten Datenverkehr über VPN senden“ abzuwählen und eine Host-Route hinzuzufügen: sudo route add -host internal-tuner.example -interface ppp0, wieder entfernt mit sudo route delete -host internal-tuner.example -interface ppp0. Lesen Sie zuerst den Schnittstellennamen aus ifconfig, denn ppp0 ist nur ein Beispiel. Platzhalter funktionieren in einer Host-Route nicht, daher braucht ein Dienst, der mit mehreren Namen spricht, einen Eintrag pro Name, und die Regel deckt jeden Prozess auf dem Rechner ab, nicht den, den Sie im Sinn hatten.

Binden Sie den Socket an die Tunneladresse

Wenn Sie den Client kontrollieren, ist Binden der billigste Selektor. Ein an die lokale Adresse des Tunnels gebundener Socket verlässt diese Schnittstelle, solange dort eine Route zum Ziel existiert, und alles andere auf dem Rechner behält seinen normalen Weg. Die Feinheit ist, dass das Binden die Quelladresse wählt, während die Routentabelle weiterhin den Ausgang wählt. Binden allein verschiebt keinen Verkehr, der keine Route in den Tunnel hat, und genau das ist der Fehler hinter den meisten Meldungen „ich habe es gebunden, und es geht immer noch den falschen Weg“.

import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))

Höherwertige Werkzeuge bieten dieselbe Idee: curl --interface 10.8.0.2, CURLOPT_INTERFACE in PHP und das Argument source_address in Pythons HTTP-Clients. Die Adresse muss zum Tunnel gehören, und ein Tippfehler hier scheitert als gewöhnlicher Verbindungsfehler, nicht als Routing-Beschwerde. Das Binden an einen Gerätenamen statt an eine Adresse ist ein Linux-Recht und überträgt sich nicht auf Windows oder macOS, deshalb übergeben portable Skripte die Adresse.

Linux: den Eigentümer abgleichen, nicht den Programmnamen

Der Kernel kann den Eigentümer eines Sockets abgleichen, also wird ein eigenes Konto zum Selektor. Legen Sie den Tunnel in eine eigene Routentabelle und schicken Sie nur den Verkehr dieses Kontos dorthin:

ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100

Das funktioniert nur, wenn der Tunnel nicht bereits die Standardroute ist. Starten Sie den Client mit --route-nopull oder filtern Sie die geschobene Umleitung mit --pull-filter ignore "redirect-gateway" auf OpenVPN 2.4 und neuer, und fügen Sie den Tabelleneintrag dann selbst hinzu. Sonst existieren beide Standardrouten, und die mit der niedrigeren Priorität gewinnt. Die bewusste Variante davon ist, die Standardroute auf das Tunnel-Gateway zu zeigen oder das aus einem Trennskript zu tun: Es hält den Verkehr vom Internet fern, wenn der Tunnel ausfällt, aber das ist ein Kill Switch, kein Selektor, und er bleibt, bis ihn jemand entfernt.

Die stärkere Variante ist ein Netzwerk-Namespace. Erstellen Sie ihn, führen Sie den Client darin aus und starten Sie die Anwendung mit ip netns exec. Nichts außerhalb des Namespace sieht die Tunnelschnittstelle, also kann die Anwendung nicht versehentlich auf die Host-Route zurückfallen. Der Preis ist real: Der Namespace braucht ein veth-Paar, um das physische Netz zu erreichen, und eine eigene Resolver-Datei, weil der Mount-Namespace die Auflösungseinstellungen des Hosts nicht teilt.

Zwei Fehlermodi erklären die meisten Lecks. Die Namensauflösung findet weiterhin im Resolver des Hosts statt, also verlässt die Abfrage den Tunnel, auch wenn die TCP-Verbindung hineingeht. Und IPv6 braucht eine eigene ip -6 rule, weil eine v4-Regel sie nicht abdeckt.

Windows und macOS: Firewall eingrenzen oder einen Proxy übergeben

Auch Windows hat keinen Routenselektor pro Anwendung, aber seine Firewall-Regeln lassen sich auf ein Profil eingrenzen, und ein VPN-Adapter kann als „öffentlich“ eingestuft werden, während das Heim-LAN „privat“ bleibt. Erstellen Sie ausgehende Blockregeln für das Programm in den Profilen „Privat“ und „Domäne“, lassen Sie „Öffentlich“ offen, und die Anwendung erreicht das Netzwerk nur über den Tunnel. Legen Sie zuerst die Netzwerkkategorie der Verbindung fest und bestätigen Sie dann, dass die Regel im Überwachungsbereich der Firewall gegriffen hat. Es ist eine Blockade, keine Route: Sie schlägt geschlossen fehl, wenn der Tunnel ausfällt, und kann eine Anwendung nicht stoppen, die die Firewall ignoriert.

macOS folgt derselben Logik mit anderen Werkzeugen. Ein über SSH erreichter SOCKS-Listener ist die am wenigsten invasive Option, denn ssh -D 1080 user@host erzeugt einen lokalen Port, und nur Anwendungen, die darauf konfiguriert sind, nehmen diesen Weg. Eine pf-Regel kann ein Programm nach Benutzer oder Gruppe blockieren, aber sie kann den Tunnel noch immer nicht für dieses Programm allein wählen. Tunnelblick kann überhaupt nicht nach Domäne filtern, weil Routen mit Adressen arbeiten; OpenConnect-Nutzer greifen zu vpn-slice, wenn der Server eine zu breite Präfixliste schiebt.

Zwei Umgehungen decken ab, was die Bordwerkzeuge übersehen. Browser-Erweiterungen, die den Proxy pro Domäne wechseln, sind die praktische Antwort auf „nur diese Seite“, weil sie auf der Anwendungsebene wirken, wo Namen noch existieren; sie decken einen Browser ab und sonst nichts. Die grobe Variante ist, den Client des Anbieters in einer virtuellen Maschine laufen zu lassen, damit der Gast die Routenänderung besitzt, während der Host seinen Weg behält, zum Preis von zwei zu pflegenden Netzwerk-Stacks.

Ein SOCKS-Hop für Werkzeuge ohne Bindungsoption

Die meisten Anwendungen lassen sich auf einen HTTP- oder SOCKS-Proxy zeigen, auch wenn sie nichts über Schnittstellen wissen. Ein SSH-Client liefert einen ohne Änderung auf der Serverseite:

ssh -N -D 127.0.0.1:9999 [email protected]

Alles, was für diesen Port konfiguriert ist, verlässt am Bastion, während der Rest des Desktops seine normale Verbindung behält. Eine lokale 3proxy-Instanz, gebunden an die Tunneladresse, tut dasselbe für einen Port ohne SSH, und stunnel deckt einen einzelnen TLS-verpackten Port ab. Die Grenzen sind praktisch: Ein Proxy trägt nur Protokolle, die wissen, wie sie ihn benutzen, UDP braucht eine SOCKS5-UDP-Assoziation, die die „SOCKS5“-Einstellung vieler Clients nicht aktiviert, und DNS muss vom Proxy aufgelöst werden (socks5h in curl), sonst leakt der Name zum lokalen Resolver.

Halten Sie den Listener auf Loopback, es sei denn, eine andere Maschine braucht ihn.

Prüfen Sie den Weg mit einem Mitschnitt, nicht mit einer Statusmeldung

Statusfenster von Anwendungen beschreiben die Einstellung, nicht das Paket. Zeichnen Sie auf beiden Schnittstellen auf und vergleichen Sie:

tcpdump -i any -n port 443
ss -tnp | grep 443

Erscheint die Verbindung auf der physischen Schnittstelle ohne passenden Eintrag am Tunnel, ist der Selektor nicht in Kraft, was der Client auch meldet. Prüfen Sie IPv6 und den Resolver getrennt und prüfen Sie auf einen zweiten Weg wie einen Hilfsprozess oder einen geerbten System-Proxy.

Wählen Sie dann die Grenze, die zur Anforderung passt: Ein Proxy reicht für eine Seite, eine UID-Regel reicht für ein Konto, ein Firewall-Profil reicht, wenn die Verbindung geschlossen fehlschlagen muss, und ein Namespace ist die einzige Option, die den Tunnel vor allem außerhalb verbirgt.

Ähnliche Artikel