Docker-containrars trafik kringgår värdens VPN: så kontrollerar du det
En VPN på värden täcker bara värdens nätverksnamnrymd. Använd de här kontrollerna för att avgöra om containertrafik på en brygga går in i tunneln eller rakt ut.
Din webbläsare på värden visar VPN:ens utgångsadress, containern rapporterar din hemuppkoppling och någon kallar det en läcka. Inget har läckt. En värdtunnel är en samling gränssnitt, rutter och regler som hör till värdens nätverksnamnrymd, och en container på en brygga är en annan namnrymd med sin egen standardrutt. Jag har suttit på en Compose-stack där VPN-klienten hade varit ansluten i veckor och inte ett enda containerpaket någonsin hade kommit in i den.
Kontrollera namnrymden före ikonen i aktivitetsfältet
Namnrymder är hela argumentet. När Docker startar en container på ett bryggnätverk skapar det en nätverksnamnrymd, fäster ena änden av ett veth-par inuti den och ger den namnrymden en adress och en standardrutt. Tunnelgränssnittet, vad det än heter, lever i värdens namnrymd. Trafik inne i containern kan inte se det, och ingen klientinställning ändrar det utifrån.
Två stödda grepp tar dig ur isoleringen. --network host placerar containern i värdens namnrymd, så den ärver värdens rutter och därmed tunneln. --network container:vpn delar en annan containers namnrymd, och så skickar de flesta sidecar-konstruktioner en arbetsbelastning genom en VPN-container. Båda är uttryckliga val; oftast är bara ett av dem det som personen som driftsätter menade.
Värdläget är ingen gratis uppgradering. Containern slutar ha en egen adress, portpublicering slutar gälla, och en process som håller NET_ADMIN där kan skriva om värdens routning. Att acceptera det kan vara rätt beslut. Att anta att det redan är på är hur folk spenderar en kväll med att felsöka en tunnel som aldrig var i vägen.
Följ paketet ut från bryggan
En bryggcontainer skickar sitt paket till sin standardgateway, vilket är brygggränssnittet på värden. Värden vidarebefordrar sedan paketet och översätter i standardkonfigurationen källadressen till sin egen innan paketet lämnar maskinen. Det är det översättningssteget som tyst sätter policyn ur spel.
När paketet når tunnelns ändpunkt bär det värdens adress. Varje regel, policyrutt eller beslut om delad tunnel som bygger på containerns ursprungliga adress slutar matcha, och två containrar som ska ut genom olika utgångar går inte att skilja enbart på källan. Om du behöver routning per container måste beslutet fattas före översättningen eller inuti en namnrymd du kontrollerar.
Docker underhåller själv dessa regler, via vilken brandväggsbackend som helst som daemonen konfigurerades med. Handskrivna regler utanför den stödda kroken för vidarebefordrad trafik, DOCKER-USER, kan ordnas om eller tas bort när daemonen startar om eller värden uppgraderas. Dockers dokumentation om paketfiltrering visar kedjornas ordning; den praktiska poängen är att varje regel behöver en ägare och en omladdningsväg som du faktiskt har testat.
Testa de två systemen var för sig
Bevis från den ena namnrymden säger ingenting om den andra. En förfrågan som körs inne i VPN-containern bevisar att tunneln fungerar för den containern. Det är inte bevis om värden, och samma kommando kört från en --network host-container är inte bevis om bryggan.
# host namespace
ip route get 1.1.1.1
ip -brief addr show
# a fresh container on the same bridge network
docker run --rm --network appnet alpine sh -c 'ip route; cat /etc/resolv.conf; ip -6 route'
Upprepa sedan testet inne i den riktiga applikationscontainern i stället för en liknande image. Appen kan bära en extra rutt, en proxy-miljövariabel eller en annan resolver, och var och en av dem ändrar svaret. Kontrollera namnmatchning och den andra adressfamiljen varje gång: containrar på användardefinierade bryggor löses via Dockers inbyggda resolver på 127.0.0.11, och en IPv6-standardrutt på bryggan kan föra ut trafik medan du är upptagen med att testa en tunnel som bara är IPv4.
Läs mönstret innan du rör inloggningsuppgifterna
När värden fungerar och containern inte gör det är orsaken nästan alltid strukturell, och att rotera nycklar eller installera om klienten kostar en eftermiddag. Sortera symtomet först och åtgärden namnger sig oftast själv.
| What you see | Usual cause | First check |
|---|---|---|
| Host exits through the VPN, container shows the ISP address | The container never entered the tunnel | Default route inside the container |
| Container works until the daemon restarts, then stops | Rules outside the supported hook, or a changed backend | Daemon's reported firewall backend and DOCKER-USER |
| Container loses internal hosts after the tunnel comes up | VPN prefix overlaps the Docker bridge subnet | ip route on the host, then docker network inspect |
| Small replies arrive, large ones hang | Tunnel MTU or missing MSS clamping | Link MTU on both ends of the tunnel |
| Container falls back to the ISP address when the tunnel drops | Nothing enforces the kill switch for forwarded traffic | Stop the tunnel with a request in flight |
Överlappningsraden förtjänar en egen mening. En VPN-profil som skjuter en rutt för 172.17.0.0/16, eller för ett 10.0.0.0/8 som råkar innehålla din bryggas prefix, får tunneln att göra anspråk på containersubnätet, och svar till översatta adresser går i fel riktning eller försvinner. Att numrera om Docker-nätverket är oftast renare än att samla på undantag.
Gör täckningen uttrycklig och bryt den sedan med avsikt
Två konstruktioner håller vid en granskning. Dela VPN-containerns namnrymd och varje täckt arbetsbelastning använder en tunnel med en omstartsgräns, vilket är enkelt tills en tjänst behöver en annan utgång eller sin egen DNS-väg. Eller routa containernätverket till en VPN-gateway på värden: vidarebefordran på, bryggans prefix riktat mot tunneln, returtrafik hanterad uttryckligen. Det köper kontroll per nätverk och kostar dig en regeluppsättning att underhålla genom daemon- och klientuppgraderingar.
Hur som helst är testet som betyder något felet. Stoppa tunneln med en containerförfrågan under vägen och se vart paketet tar vägen. Om det faller tillbaka på den direkta vägen har täckningen aldrig genomdrivits, eftersom en klients kill switch styr klientens egen trafik och inte vidarebefordrade paket från en brygga. Starta sedan om daemonen och värden och bekräfta resultatet en andra gång; det är ögonblicket då handskrivna regler försvinner.
Innan du kallar det klart, bekräfta fyra saker: tunnelgränssnittet ligger i samma namnrymd som trafiken, containertestet kördes inne i den riktiga containern, namnuppslagning och båda adressfamiljerna kontrollerades, och tunnelfeltestet misslyckas fortfarande stängt. Skriv sedan ned brandväggsbackend, klientversion och ägare till varje regel. Den dokumentationen är det som hindrar nästa uppgradering från att lämna en odokumenterad blandning av gamla antaganden.