Herstel Docker- en WSL-verkeer dat het host-VPN negeert
Containers en WSL houden hun eigen routes, resolver en doorstuurpad, dus een werkende host-tunnel laat ze achter met verouderde DNS of zonder uitgang. Herstel elke laag.
Ik heb dit voorjaar het grootste deel van een ochtend besteed aan het najagen van een container die geen enkele interne naam kon resolven, terwijl de browser ernaast de wiki zonder klachten opende. De tunnel stond, de browser bewees het, en de container zat in een andere netwerknaamruimte met een andere routetabel en een andere resolver. Wat volgt is de volgorde die ik nu controleer: eerst routetabellen, dan resolutie, dan doorsturen en tot slot het hostplatform. Stop bij de eerste stap die al faalt, want regels verderop in de stack verbergen de grens in plaats van hem te verplaatsen.
Vergelijk de drie routetabellen voordat je een regel wijzigt
Een container op de standaardbrug van Docker heeft één standaardroute naar de docker0-gateway en weet niet dat tun0 bestaat. De host heeft tun0, de routes die de VPN heeft geduwd en mogelijk een beleidsregel die testverkeer via een andere interface naar buiten stuurt dan je verwacht. WSL voegt een derde tabel toe achter zijn eigen virtuele adapter. Druk alle drie af voordat je er een wijzigt.
| 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 |
Als de enige route van de container de brug is en tun0 nooit in zijn naamruimte verschijnt, helpt niets hieronder: geen enkele nameserverwijziging maakt een route, en geen enkele route maakt de tunnelidentiteit die de peer accepteert. De volgorde is belangrijk omdat een DNS-storing en een routestoring er van binnenuit een shell identiek uitzien.
Wijs de resolver naar de tunnel in plaats van naar een publieke
Op de host, resolvectl status somt DNS-servers per link op, en readlink -f /etc/resolv.conf vertelt je of het bestand de systemd-stub is, een NetworkManager-bestand of iets dat een leveranciersclient heeft herschreven. Binnen een container op een door de gebruiker gedefinieerd netwerk, /etc/resolv.conf noemt 127.0.0.11, de ingebouwde resolver van Docker, die doorstuurt naar de upstream waarmee de daemon was geconfigureerd. Een container op de standaardbrug kreeg daarentegen bij het aanmaken een kopie van het hostbestand en houdt die.
Die kopie is de reden dat een tunnel die na de start van de container opkomt, de container de oude upstream laat bevragen. Geef de VPN-resolver expliciet mee met docker run --dns 10.8.0.53, of stel "dns": ["10.8.0.53"] in /etc/docker/daemon.json in en herstart de daemon, of maak de container opnieuw aan zodra de tunnel staat. Opnieuw aanmaken is meestal goedkoper dan het debuggen van een resolver die een uur geleden nog correct was.
Een publieke resolver invullen is de reparatie die het symptoom houdt. Hij resolvet het internet en faalt in elke privézone, en hij kan de filtering die de tunnel hoort af te dwingen stilzwijgend omzeilen. Als je probeert te schrijven in /etc/resolv.conf van een draaiende container, verwacht dan Device or resource busy: Docker bind-mount dat bestand, dus de bewerking vereist eerst een unmount. --dns bij het aanmaken instellen is de volgorde die een herstart overleeft.
Stuur door en vertaal wanneer containers de tunnel lenen
Met de standaardbrug is de host een router tussen docker0 en tun0, en standaard doet hij geen van beide taken. Zet doorsturen aan en vertaal het bronadres zodat de peer een tunneladres van de host ziet in plaats van een adres waarnaar hij geen terugroute heeft (vervang tun0 door de naam die je client aan de interface geeft):
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 ACCEPTDe FORWARD-regels zijn belangrijk op hosts waarvan het doorstuurbeleid DROP is. Docker voegt zijn eigen chains toe, dus test met een pakket in plaats van te lezen iptables -S: vanaf de container, ip route get 10.8.0.20 zou de brug-gateway moeten noemen, en nc -vz 10.8.0.20 443 zou moeten verbinden of time-outen op de tunnel in plaats van op de resolver.
Zonder MASQUERADE heeft de peer een terugroute naar 172.17.0.0/16 nodig, die de meeste remote-access-VPN's niet duwen. Hier breekt ook een op beleid gebaseerde client het plan: als hij alleen verkeer van bepaalde UID's of cgroups onderschept, zien je doorstuurregels het pakket nooit binnen de tunnel, en de correctie hoort in het routeringsbeleid van de client en niet in iptables. Regels die met de hand zijn toegevoegd, verdwijnen bij een herstart, dus zet ze in een systemd-unit of een netfilter-persistent-tabel en test opnieuw na een herstart, want een tunnel die laat opkomt kan ze wegspoelen.
Zet de tunnel in de container wanneer isolatie het doel is
Als één container via een specifieke node naar buiten moet, draai de client in die container. Die heeft --cap-add NET_ADMIN en --device /dev/net/tun nodig, en dan bezit hij zijn eigen tun0, routes en firewall. Als andere containers erdoorheen gaan, heeft hij ook doorsturen en een MASQUERADE-regel in de container nodig, en die peers hebben een expliciete route of proxy nodig die naar hem wijst; de standaardbrug van Docker voegt die route niet voor je toe.
Host-networking, docker run --network host, is de goedkope versie: de container sluit zich aan bij de naamruimte van de host en ziet de tunnelroutes rechtstreeks. Het laat ook netwerkisolatie vallen, nodigt poortconflicten uit en is in de praktijk een Linux-optie. Een gepubliceerde poort is geen vervanging. -p 8080:80 maakt een dienst bereikbaar vanaf de host; het maakt interne 10.x-adressen niet bereikbaar van binnen de container, en die twee verwarren is hoe een routeringsprobleem een blootgestelde databasepoort wordt.
Onderscheid een naamfout van een overlappend subnet
De standaardbrug van Docker is 172.17.0.0/16, en veel bedrijfs-VPN-pools liggen binnen 172.16.0.0/12. Zodra de tunnel dat bereik bezit, kan verkeer dat voor een container bedoeld is in plaats daarvan de tunnel in worden gerouteerd. Namen falen, de fout lijkt op DNS, en het probleem zit in de adressen. Controleer de bestemming, niet de resolver: ip route get 172.17.0.5 op de host, vergelijk dan het brugprefix met de VPN-pool en met docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.
De reparatie is een van de bereiken verplaatsen: ofwel een andere standaardadrespool voor Docker in daemon.json ofwel een andere pool op de concentrator. Een extra nameserver toevoegen doet hier niets, en ssh -b ook niet: de client aan een interface binden verandert het bronadres, niet een bestemmingsroute die de tunnel in wijst. Snelle taakverdeling: een naam die op de host wel wordt geresolvet maar in de container niet, is een resolverprobleem, terwijl een directe IP-verbinding die in de container blijft hangen maar op de host werkt, een route- of doorstuurkettingsprobleem is.
WSL en Docker Desktop volgen de Linux-regels niet
In zijn standaard NAT-modus zit WSL achter zijn eigen virtuele adapter en ontvangt hij de interne routes of DNS-suffixen van het Windows-VPN niet, waardoor de browser werkt en ping binnen de distributie niet. Gespiegeld netwerken, beschikbaar in recente Windows 11-builds, deelt in plaats daarvan de interfaces van de host, maar het moet worden ingeschakeld in .wslconfig en de distributie moet opnieuw worden gestart met wsl --shutdown. Bevestig de actieve modus met wslinfo --networking-mode voordat je een correctie toepast, want het NAT-antwoord is fout voor de gespiegelde modus en andersom.
Docker Desktop voegt nog een laag toe. Linux-containers op Windows zitten achter een NAT-implementatie die historisch vpnkit heet, en hun pad verschilt van dat van een native Linux-host. Windows-containers verbinden via een virtuele Hyper-V-switch, en een bedrijfsclient kan verkeer van die switch blokkeren, zodat Linux-containers op dezelfde machine het netwerk wel bereiken terwijl Windows-containers niets hebben. Een externe switch die aan de fysieke adapter is gebonden, is de gebruikelijke oplossing, en die verandert de netwerkidentiteit van de container op manieren die het beleid misschien niet toestaat.
Voordat je nog een regel schrijft, loop de vier lagen in volgorde door en stop waar een van hen onjuist is:
- In de container: bereikt
ip routehet privéprefix, en noemt/etc/resolv.confeen upstream die het resolvet? - Op de host: staat doorsturen aan, is NAT gebonden aan de tunnelinterface, en accepteert de doorstuurketen brug naar tunnel?
- Op de peer: kent hij een terugweg, of vertrouw je op NAT om het containersubnet te verbergen?
- Op het platform: staat WSL in NAT- of gespiegelde modus, en is de container Linux of Windows?
Voer elke test opnieuw uit in een verse container of shell. Een langdraaiend proces met een gecacht antwoord of een gevestigde verbinding blijft falen nadat de configuratie correct is, en die fout ziet er precies uit als een reparatie die niet werkte.