Docker- und WSL-Verkehr reparieren, der das Host-VPN ignoriert
Container und WSL behalten eigene Routen, Resolver und Weiterleitungspfade, daher bleiben sie bei einem funktionierenden Host-Tunnel mit veraltetem DNS oder ohne Ausgang zurück. Reparieren Sie jede Schicht.
Ich habe in diesem Frühjahr den größten Teil eines Vormittags damit verbracht, einen Container zu jagen, der keinen einzigen internen Namen auflösen konnte, während der Browser daneben das Wiki ohne Beschwerde öffnete. Der Tunnel war oben, der Browser bewies es, und der Container saß in einem anderen Netzwerk-Namespace mit einer anderen Routentabelle und einem anderen Resolver. Was folgt, ist die Reihenfolge, die ich jetzt prüfe: zuerst Routentabellen, zweitens Namensauflösung, drittens Weiterleitung und zuletzt die Host-Plattform. Hören Sie beim ersten Schritt auf, der bereits fehlschlägt, denn Regeln weiter unten im Stack verbergen die Grenze, statt sie zu verschieben.
Vergleichen Sie die drei Routentabellen, bevor Sie eine Regel ändern
Ein Container auf Dockers Standard-Bridge hat eine Standardroute zum docker0-Gateway und keine Ahnung, dass tun0 existiert. Der Host hat tun0, die vom VPN geschobenen Routen und möglicherweise eine Policy-Regel, die Sondierungsverkehr über eine andere Schnittstelle schickt, als Sie erwarten. WSL fügt hinter seinem eigenen virtuellen Adapter eine dritte Tabelle hinzu. Geben Sie alle drei aus, bevor Sie eine davon bearbeiten.
| Context | Command | What a wrong answer looks like |
|---|---|---|
| Host | ip route get 10.8.0.20 | Leaves a physical interface instead of tun0 |
| Container | ip route | Only 172.17.0.0/16 via 172.17.0.1 |
| Container DNS | cat /etc/resolv.conf | A public resolver, or an upstream the tunnel never pushed |
| WSL | ip addr; ip route | Only the WSL subnet, no Windows VPN adapter |
Wenn die einzige Route des Containers die Bridge ist und tun0 nie in seinem Namespace auftaucht, hilft nichts weiter unten: Keine Nameserver-Änderung erzeugt eine Route, und keine Route erzeugt eine Tunnelidentität, die der Peer akzeptiert. Die Reihenfolge zählt, weil ein DNS-Fehler und ein Routenfehler aus einer Shell heraus identisch aussehen.
Richten Sie den Resolver auf den Tunnel statt auf einen öffentlichen
Auf dem Host, resolvectl status listet DNS-Server pro Link auf, und readlink -f /etc/resolv.conf sagt Ihnen, ob die Datei der systemd-Stub, eine NetworkManager-Datei oder etwas ist, das ein Anbieter-Client umgeschrieben hat. In einem Container in einem benutzerdefinierten Netzwerk, /etc/resolv.conf nennt 127.0.0.11, Dockers eingebetteten Resolver, der an den Upstream weiterleitet, mit dem der Daemon konfiguriert wurde. Ein Container auf der Standard-Bridge erhielt stattdessen bei seiner Erstellung eine Kopie der Host-Datei und behält sie.
Diese Kopie ist der Grund, warum ein Tunnel, der nach dem Start des Containers hochkommt, den Container den alten Upstream abfragen lässt. Übergeben Sie den VPN-Resolver entweder explizit mit docker run --dns 10.8.0.53, oder setzen Sie "dns": ["10.8.0.53"] in /etc/docker/daemon.json und starten Sie den Daemon neu, oder erstellen Sie den Container neu, sobald der Tunnel steht. Neu erstellen ist meist billiger, als einen Resolver zu debuggen, der vor einer Stunde noch richtig war.
Der Wechsel zu einem öffentlichen Resolver ist die Reparatur, die das Symptom erhält. Er löst das Internet auf und scheitert in jeder privaten Zone, und er kann die Filterung stillschweigend umgehen, für die der Tunnel existiert. Wenn Sie in /etc/resolv.conf eines laufenden Containers schreiben wollen, rechnen Sie mit Device or resource busy: Docker bind-mountet diese Datei, daher braucht die Änderung zuerst ein Unmount. --dns bei der Erstellung zu setzen ist die Reihenfolge, die einen Neustart überlebt.
Leiten Sie weiter und übersetzen Sie, wenn Container den Tunnel ausleihen
Mit der Standard-Bridge ist der Host ein Router zwischen docker0 und tun0, und standardmäßig erledigt er keine der beiden Aufgaben. Schalten Sie die Weiterleitung ein und übersetzen Sie die Quelladresse, damit der Peer eine Tunneladresse des Hosts sieht statt einer, zu der er keine Rückroute hat (ersetzen Sie tun0 durch den Namen, den Ihr Client der Schnittstelle gibt):
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A FORWARD -i docker0 -o tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -o docker0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTDie FORWARD-Regeln sind auf Hosts wichtig, deren Weiterleitungsrichtlinie DROP ist. Docker fügt eigene Ketten ein, testen Sie also mit einem Paket statt durch Lesen von iptables -S: Vom Container aus sollte ip route get 10.8.0.20 das Bridge-Gateway nennen, und nc -vz 10.8.0.20 443 sollte am Tunnel verbinden oder in ein Timeout laufen statt am Resolver.
Ohne MASQUERADE braucht der Peer eine Rückroute nach 172.17.0.0/16, die die meisten Remote-Access-VPNs nicht schieben. Hier bricht auch ein richtlinienbasierter Client den Plan: Wenn er nur Verkehr von bestimmten UIDs oder cgroups abfängt, sehen Ihre Weiterleitungsregeln das Paket innerhalb des Tunnels nie, und die Korrektur gehört in die Routing-Richtlinie des Clients und nicht in iptables. Von Hand hinzugefügte Regeln verschwinden beim Neustart, legen Sie sie also in eine systemd-Unit oder eine netfilter-persistent-Tabelle und testen Sie nach einem Neustart erneut, da ein spät hochkommender Tunnel sie wegspülen kann.
Legen Sie den Tunnel in den Container, wenn Isolation das Ziel ist
Wenn ein Container über einen bestimmten Knoten hinausgehen muss, führen Sie den Client in diesem Container aus. Er braucht --cap-add NET_ADMIN und --device /dev/net/tun, und dann besitzt er sein eigenes tun0, seine Routen und seine Firewall. Wenn andere Container durch ihn hindurchgehen, braucht er auch Weiterleitung und eine MASQUERADE-Regel im Container, und diese Peers brauchen eine explizite Route oder einen Proxy, der auf ihn zeigt; Dockers Standard-Bridge fügt diese Route nicht für Sie hinzu.
Host-Netzwerk, docker run --network host, ist die billige Variante: Der Container tritt dem Host-Namespace bei und sieht die Tunnelrouten direkt. Es verwirft auch die Netzwerkisolation, lädt Portkollisionen ein und ist in der Praxis eine Linux-Option. Ein veröffentlichter Port ist kein Ersatz. -p 8080:80 macht einen Dienst vom Host aus erreichbar; es macht interne 10.x-Adressen nicht von innerhalb des Containers erreichbar, und die beiden zu verwechseln ist der Weg, auf dem aus einem Routingproblem ein offener Datenbankport wird.
Unterscheiden Sie einen Namensfehler von einem überlappenden Subnetz
Dockers Standard-Bridge ist 172.17.0.0/16, und viele Unternehmens-VPN-Pools liegen innerhalb von 172.16.0.0/12. Sobald der Tunnel diesen Bereich besitzt, kann für einen Container bestimmter Verkehr stattdessen in den Tunnel geleitet werden. Namen scheitern, der Fehler sieht wie DNS aus, und die Adressen sind das Problem. Prüfen Sie das Ziel, nicht den Resolver: ip route get 172.17.0.5 auf dem Host, vergleichen Sie dann das Bridge-Präfix mit dem VPN-Pool und mit docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.
Die Reparatur besteht darin, einen der Bereiche zu verschieben: entweder einen anderen Standardadresspool für Docker in daemon.json oder einen anderen Pool am Konzentrator. Einen weiteren Nameserver hinzuzufügen bringt hier nichts, und ssh -b auch nicht: Den Client an eine Schnittstelle zu binden ändert die Quelladresse, nicht eine Zielroute, die in den Tunnel zeigt. Schnelle Arbeitsteilung: Ein Name, der auf dem Host, aber nicht im Container aufgelöst wird, ist ein Resolverproblem, während eine direkte IP-Verbindung, die im Container hängt, aber auf dem Host funktioniert, ein Routen- oder Weiterleitungskettenproblem ist.
WSL und Docker Desktop folgen nicht den Linux-Regeln
Im standardmäßigen NAT-Modus sitzt WSL hinter seinem eigenen virtuellen Adapter und erhält weder die internen Routen noch die DNS-Suffixe des Windows-VPN, weshalb der Browser funktioniert und ping innerhalb der Distribution nicht. Gespiegeltes Netzwerk, verfügbar in neueren Windows 11-Builds, teilt stattdessen die Host-Schnittstellen, muss aber in .wslconfig aktiviert und die Distribution mit wsl --shutdown neu gestartet werden. Bestätigen Sie den aktiven Modus mit wslinfo --networking-mode bevor Sie eine Korrektur anwenden, denn die NAT-Antwort ist für den gespiegelten Modus falsch und umgekehrt.
Docker Desktop fügt eine weitere Schicht hinzu. Linux-Container unter Windows sitzen hinter einer NAT-Implementierung, die historisch vpnkit heißt, und ihr Pfad unterscheidet sich von einem nativen Linux-Host. Windows-Container werden über einen virtuellen Hyper-V-Switch angebunden, und ein Unternehmensclient kann Verkehr von diesem Switch blockieren, sodass Linux-Container auf derselben Maschine das Netzwerk erreichen können, während Windows-Container keines haben. Ein externer Switch, der an den physischen Adapter gebunden ist, ist der übliche Ausweg, und er ändert die Netzwerkidentität des Containers auf Arten, die die Richtlinie möglicherweise nicht erlaubt.
Bevor Sie eine weitere Regel schreiben, gehen Sie die vier Schichten der Reihe nach durch und halten Sie dort an, wo eine von ihnen falsch ist:
- Im Container: Erreicht
ip routedas private Präfix, und nennt/etc/resolv.confeinen Upstream, der es auflöst? - Auf dem Host: Ist die Weiterleitung an, ist NAT an die Tunnelschnittstelle gebunden, und akzeptiert die FORWARD-Kette Bridge zu Tunnel?
- Beim Peer: Kennt er einen Rückweg, oder verlassen Sie sich darauf, dass NAT das Container-Subnetz verbirgt?
- Auf der Plattform: Ist WSL im NAT- oder im gespiegelten Modus, und ist der Container Linux oder Windows?
Führen Sie jeden Test in einem frischen Container oder einer frischen Shell erneut aus. Ein langlaufender Prozess mit zwischengespeicherter Antwort oder etablierter Verbindung schlägt weiterhin fehl, nachdem die Konfiguration korrekt ist, und dieser Fehler sieht genau wie eine Reparatur aus, die nicht funktioniert hat.