Ret Docker- og WSL-trafik, der ignorerer værtens VPN
Containere og WSL holder deres egne ruter, resolver og videresendelsesvej, så en fungerende værtstunnel efterlader dem med forældet DNS eller ingen udgang. Ret hvert lag.
Jeg brugte det meste af en formiddag i foråret på at jage en container, der ikke kunne opløse et eneste internt navn, mens browseren ved siden af åbnede wiki'en uden klager. Tunnelen var oppe, browseren beviste det, og containeren sad i et andet netværksnavnerum med en anden rutetabel og en anden resolver. Det følgende er den rækkefølge, jeg tjekker nu: rutetabeller først, opløsning derefter, videresendelse som tredje og værtens platform til sidst. Stop ved det første trin, der allerede fejler, fordi regler længere nede i stakken skjuler grænsen i stedet for at flytte den.
Sammenlign de tre rutetabeller, før du ændrer en regel
En container på Dockers standardbro har én standardrute, der peger på docker0-gatewayen, og aner ikke, at tun0 findes. Værten har tun0, de ruter VPN'en skubbede, og muligvis en policyregel, der sender probetrafik ud gennem en anden grænseflade, end du forventer. WSL tilføjer en tredje tabel bag sin egen virtuelle adapter. Udskriv alle tre, før du redigerer nogen af dem.
| 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 |
Hvis containerens eneste rute er broen, og tun0 aldrig dukker op i dens navnerum, hjælper intet nedenfor: ingen navneserverændring skaber en rute, og ingen rute skaber en tunnelidentitet, som peer'en accepterer. Rækkefølgen er vigtig, fordi en DNS-fejl og en rutefejl ser identiske ud indefra en shell.
Peg resolvers på tunnelen i stedet for en offentlig
På værten, resolvectl status viser DNS-servere pr. link, og readlink -f /etc/resolv.conf fortæller dig, om filen er systemd-stubben, en NetworkManager-fil eller noget, en leverandørklient har skrevet om. Inde i en container på et brugerdefineret netværk, /etc/resolv.conf navngiver 127.0.0.11, Dockers indbyggede resolver, som videresender til den upstream, som daemonen blev konfigureret med. En container på standardbroen fik derimod en kopi af værtens fil, da den blev oprettet, og beholder den.
Den kopi er grunden til, at en tunnel, der kommer op efter containeren starter, efterlader containeren med at spørge den gamle upstream. Enten send VPN-resolveren eksplicit med docker run --dns 10.8.0.53, eller sæt "dns": ["10.8.0.53"] i /etc/docker/daemon.json og genstart daemonen, eller genskab containeren, når tunnelen er oppe. Det er normalt billigere at genskabe end at fejlfinde en resolver, der var korrekt for en time siden.
At skifte til en offentlig resolver er den reparation, der bevarer symptomet. Den opløser internettet og fejler i hver privat zone, og den kan stille omgå den filtrering, som tunnelen findes for at håndhæve. Hvis du prøver at skrive inde i /etc/resolv.conf på en kørende container, forvent Device or resource busy: Docker bind-mounter den fil, så redigeringen kræver først en afmontering. At sætte --dns ved oprettelsen er den sekvens, der overlever en genstart.
Videresend og oversæt, når containere låner tunnelen
Med standardbroen er værten en router mellem docker0 og tun0, og den gør som standard ingen af delene. Slå videresendelse til, og oversæt kildeadressen, så peer'en ser en tunneladresse på værten i stedet for en, den ikke har nogen returrute til (erstat tun0 med det, din klient kalder grænsefladen):
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 ACCEPTFORWARD-reglerne betyder noget på værter, hvis videresendelsespolitik er DROP. Docker indsætter sine egne kæder, så test med en pakke i stedet for at læse iptables -S: fra containeren, ip route get 10.8.0.20 skal navngive broens gateway, og nc -vz 10.8.0.20 443 skal forbinde eller timeout ved tunnelen i stedet for ved resolveren.
Uden MASQUERADE har peer'en brug for en returrute til 172.17.0.0/16, som de fleste fjernadgangs-VPN'er ikke skubber. Det er også her, en policybaseret klient ødelægger planen: hvis den kun fanger trafik fra bestemte UID'er eller cgroups, ser dine videresendelsesregler aldrig pakken inde i tunnelen, og rettelsen hører i klientens rutepolitik og ikke i iptables. Regler, der tilføjes manuelt, forsvinder ved genstart, så læg dem i en systemd-unit eller en netfilter-persistent-tabel og test igen efter en genstart, da en tunnel, der kommer sent op, kan skylle dem væk.
Læg tunnelen inde i containeren, når isolering er målet
Hvis én container skal ud gennem en bestemt node, kør klienten i den container. Den har brug for --cap-add NET_ADMIN og --device /dev/net/tun, og så ejer den sin egen tun0, ruter og firewall. Hvis andre containere passerer gennem den, har den også brug for videresendelse og en MASQUERADE-regel inde i containeren, og de peers har brug for en eksplicit rute eller proxy, der peger på den; Dockers standardbro tilføjer ikke den rute for dig.
Værtsnetværk, docker run --network host, er den billige version: containeren tilslutter sig værtens navnerum og ser tunnelruterne direkte. Det dropper også netværksisolering, inviterer portkollisioner og er i praksis en Linux-mulighed. En publiceret port er ikke en erstatning. -p 8080:80 gør en tjeneste tilgængelig fra værten; det gør ikke interne 10.x-adresser tilgængelige indefra containeren, og at blande de to er måden, hvorpå et ruteproblem bliver en eksponeret databaseport.
Skeln mellem en navnefejl og et overlappende subnet
Dockers standardbro er 172.17.0.0/16, og mange virksomheds-VPN-puljer ligger inden for 172.16.0.0/12. Når tunnelen ejer det interval, kan trafik beregnet til en container i stedet sendes ind i tunnelen. Navne fejler, fejlen ser ud som DNS, og problemet er adresserne. Tjek destinationen, ikke resolvers: ip route get 172.17.0.5 på værten, sammenlign derefter bropræfikset med VPN-puljen og med docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.
Rettelsen er at flytte et af intervallerne: enten en anden standardadressepulje til Docker i daemon.json eller en anden pulje på koncentratoren. At tilføje endnu en navneserver gør intet her, og det gør ssh -b heller ikke: at binde klienten til en grænseflade ændrer kildeadressen, ikke en destinationsrute, der peger ind i tunnelen. Hurtig arbejdsdeling: et navn, der opløses på værten men ikke i containeren, er et resolverproblem, mens en direkte IP-forbindelse, der hænger i containeren men virker på værten, er et rute- eller videresendelseskædeproblem.
WSL og Docker Desktop følger ikke Linux-reglerne
I sin standard NAT-tilstand sidder WSL bag sin egen virtuelle adapter og modtager ikke Windows-VPN'ens interne ruter eller DNS-suffikser, hvilket er grunden til, at browseren virker, og ping inde i distributionen ikke gør. Spejlet netværk, tilgængeligt i nyere Windows 11-builds, deler i stedet værtens grænseflader, men det skal aktiveres i .wslconfig og distributionen genstartes med wsl --shutdown. Bekræft den aktive tilstand med wslinfo --networking-mode før du anvender en rettelse, fordi NAT-svaret er forkert for spejlet tilstand og omvendt.
Docker Desktop tilføjer endnu et lag. Linux-containere på Windows sidder bag en NAT-implementering, der historisk hedder vpnkit, og deres vej adskiller sig fra en native Linux-vært. Windows-containere tilsluttes via en Hyper-V virtuel switch, og en virksomhedsklient kan blokere trafik fra den switch, så Linux-containere på samme maskine kan nå netværket, mens Windows-containere ikke har noget. En ekstern switch bundet til den fysiske adapter er den sædvanlige løsning, og den ændrer containerens netværksidentitet på måder, som politik måske ikke tillader.
Før du skriver endnu en regel, gå de fire lag igennem i rækkefølge og stop, hvor et af dem er forkert:
- Inde i containeren: når
ip routedet private præfiks, og navngiver/etc/resolv.confen upstream, der opløser det? - På værten: er videresendelse slået til, er NAT bundet til tunnelgrænsefladen, og accepterer videresendelseskæden bro til tunnel?
- På peer'en: kender den en returvej, eller regner du med, at NAT skjuler containerens subnet?
- På platformen: er WSL i NAT- eller spejltilstand, og er containeren Linux eller Windows?
Kør hver test igen i en ny container eller shell. En langvarig proces med et cachet svar eller en etableret forbindelse bliver ved med at fejle, efter konfigurationen er korrekt, og den fejl ser præcis ud som en rettelse, der ikke virkede.