Jeg brukte mesteparten av en formiddag denne våren på å jage en container som ikke klarte å slå opp et eneste internt navn, mens nettleseren ved siden av åpnet wikien uten klager. Tunnelen var oppe, nettleseren beviste det, og containeren lå i et annet nettverksnavnerom med en annen rutetabell og en annen resolver. Det som følger, er rekkefølgen jeg sjekker nå: rutetabeller først, oppslag neste, videresending tredje og vertsplattformen til slutt. Stopp ved det første trinnet som allerede feiler, fordi regler lenger nede i stakken skjuler grensen i stedet for å flytte den.

Sammenlign de tre rutetabellene før du endrer en regel

En container på Dockers standardbro har én standardrute mot docker0-gatewayen og aner ikke at tun0 finnes. Verten har tun0, rutene VPN-en pushet, og muligens en policyregel som sender probetrafikk ut gjennom et annet grensesnitt enn du forventer. WSL legger til en tredje tabell bak sin egen virtuelle adapter. Skriv ut alle tre før du redigerer noen av dem.

ContextCommandWhat a wrong answer looks like
Hostip route get 10.8.0.20Leaves a physical interface instead of tun0
Containerip routeOnly 172.17.0.0/16 via 172.17.0.1
Container DNScat /etc/resolv.confA public resolver, or an upstream the tunnel never pushed
WSLip addr; ip routeOnly the WSL subnet, no Windows VPN adapter

Hvis containerens eneste rute er broen og tun0 aldri dukker opp i navnerommet dens, hjelper ingenting nedenfor: ingen navneserverendring lager en rute, og ingen rute lager en tunnelidentitet peer-en vil godta. Rekkefølgen er viktig fordi en DNS-feil og en rutefeil ser identiske ut innenfra et skall.

Pek resolveren mot tunnelen i stedet for en offentlig

På verten, resolvectl status lister DNS-servere per lenke, og readlink -f /etc/resolv.conf forteller deg om filen er systemd-stubben, en NetworkManager-fil eller noe en leverandørklient har skrevet om. Inne i en container på et brukerdefinert nettverk, /etc/resolv.conf navngir 127.0.0.11, Dockers innebygde resolver, som videresender til den upstreamen daemonen ble konfigurert med. En container på standardbroen fikk derimot en kopi av vertens fil da den ble opprettet, og beholder den.

Den kopien er grunnen til at en tunnel som kommer opp etter at containeren starter, lar containeren spørre den gamle upstreamen. Enten sender du VPN-resolveren eksplisitt med docker run --dns 10.8.0.53, eller setter "dns": ["10.8.0.53"] i /etc/docker/daemon.json og starter daemonen på nytt, eller gjenoppretter containeren når tunnelen er oppe. Gjenoppretting er vanligvis billigere enn å feilsøke en resolver som var riktig for en time siden.

Å bytte til en offentlig resolver er reparasjonen som bevarer symptomet. Den løser internett og feiler i hver privat sone, og den kan stille omgå filtreringen tunnelen finnes for å håndheve. Hvis du prøver å skrive inne i /etc/resolv.conf på en kjørende container, forvent Device or resource busy: Docker bind-mountet den filen, så endringen krever først en avmontering. Å sette --dns ved opprettelsen er sekvensen som overlever en omstart.

Videresend og oversett når containere låner tunnelen

Med standardbroen er verten en ruter mellom docker0 og tun0, og den gjør som standard ingen av delene. Slå på videresending og oversett kildeadressen slik at peer-en ser en tunneladresse på verten i stedet for en den ikke har returrute til (erstatt tun0 med det klienten din kaller grensesnittet):

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 ACCEPT

FORWARD-reglene betyr noe på verter der videresendingspolicyen er DROP. Docker setter inn egne kjeder, så test med en pakke i stedet for å lese iptables -S: fra containeren, ip route get 10.8.0.20 skal navngi broens gateway, og nc -vz 10.8.0.20 443 skal koble til eller timeout ved tunnelen i stedet for ved resolveren.

Uten MASQUERADE trenger peer-en en returrute til 172.17.0.0/16, som de fleste fjern tilgang-VPN-er ikke pusher. Det er også her en policybasert klient ødelegger planen: hvis den bare fanger trafikk fra bestemte UID-er eller cgroups, ser videresendingsreglene dine aldri pakken inne i tunnelen, og rettelsen hører hjemme i klientens ruteringspolicy og ikke i iptables. Regler som legges til manuelt, forsvinner ved omstart, så legg dem i en systemd-enhet eller en netfilter-persistent-tabell og test på nytt etter en omstart, siden en tunnel som kommer sent opp kan skylle dem bort.

Legg tunnelen inne i containeren når isolasjon er målet

Hvis én container må gå ut gjennom en bestemt node, kjør klienten i den containeren. Den trenger --cap-add NET_ADMIN og --device /dev/net/tun, og da eier den sin egen tun0, ruter og brannmur. Hvis andre containere passerer gjennom den, trenger den også videresending og en MASQUERADE-regel inne i containeren, og de peer-ene trenger en eksplisitt rute eller proxy som peker på den; Dockers standardbro legger ikke til den ruten for deg.

Vertsnettverk, docker run --network host, er den billige versjonen: containeren blir med i vertens navnerom og ser tunnelrutene direkte. Det dropper også nettverksisolering, inviterer portkollisjoner og er i praksis et Linux-alternativ. En publisert port er ikke en erstatning. -p 8080:80 gjør en tjeneste tilgjengelig fra verten; det gjør ikke interne 10.x-adresser tilgjengelige fra innsiden av containeren, og å blande de to er måten et ruteringsproblem blir en eksponert databaseport.

Skil en navnefeil fra et overlappende subnett

Dockers standardbro er 172.17.0.0/16, og mange bedrifts-VPN-puljer ligger innenfor 172.16.0.0/12. Når tunnelen eier det området, kan trafikk ment for en container bli rutet inn i tunnelen i stedet. Navn feiler, feilen ser ut som DNS, og problemet er adressene. Sjekk destinasjonen, ikke resolveren: ip route get 172.17.0.5 på verten, sammenlign så broprefikset med VPN-puljen og med docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.

Reparasjonen er å flytte et av områdene: enten en annen standardadressepulje for Docker i daemon.json eller en annen pulje på konsentratoren. Å legge til en navneserver til gjør ingenting her, og det gjør ssh -b heller ikke: å binde klienten til et grensesnitt endrer kildeadressen, ikke en destinasjonsrute som peker inn i tunnelen. Rask arbeidsdeling: et navn som løses på verten men ikke i containeren, er et resolverproblem, mens en direkte IP-forbindelse som henger i containeren men virker på verten, er et rute- eller videresendingskjedeproblem.

WSL og Docker Desktop følger ikke Linux-reglene

I sin standard NAT-modus sitter WSL bak sin egen virtuelle adapter og mottar ikke Windows-VPN-ens interne ruter eller DNS-suffikser, og det er derfor nettleseren virker og ping inne i distribusjonen ikke. Speilnettverk, tilgjengelig i nyere Windows 11-bygg, deler i stedet vertens grensesnitt, men det må aktiveres i .wslconfig og distribusjonen startes på nytt med wsl --shutdown. Bekreft den aktive modusen med wslinfo --networking-mode før du bruker en rettelse, fordi NAT-svaret er feil for speilmodus og omvendt.

Docker Desktop legger til enda et lag. Linux-containere på Windows sitter bak en NAT-implementasjon som historisk kalles vpnkit, og veien deres skiller seg fra en nativ Linux-vert. Windows-containere kobles til via en virtuell Hyper-V-svitsj, og en bedriftsklient kan blokkere trafikk fra den svitsjen, så Linux-containere på samme maskin kan nå nettverket mens Windows-containere ikke har noe. En ekstern svitsj bundet til den fysiske adapteren er den vanlige løsningen, og den endrer containerens nettverksidentitet på måter policyen kanskje ikke tillater.

Før du skriver enda en regel, gå gjennom de fire lagene i rekkefølge og stopp der ett av dem er feil:

  • Inne i containeren: når ip route det private prefikset, og navngir /etc/resolv.conf en upstream som løser det?
  • På verten: er videresending på, er NAT bundet til tunnelgrensesnittet, og godtar videresendingskjeden bro til tunnel?
  • På peer-en: kjenner den en returvei, eller regner du med at NAT skjuler containerens subnett?
  • På plattformen: er WSL i NAT- eller speilmodus, og er containeren Linux eller Windows?

Kjør hver test på nytt i en fersk container eller et ferskt skall. En langvarig prosess med bufret svar eller en etablert forbindelse vil fortsette å feile etter at konfigurasjonen er riktig, og den feilen ser nøyaktig ut som en rettelse som ikke virket.

Relaterte artikler