Docker-container oåtkomlig via VPN? Jämför subnäten
Docker delar ut ett subnät som VPN:en också gör anspråk på. Jämför de två adressplanerna och välj en åtgärd som håller efter en återanslutning.
Containrar svarar på kontorets LAN och slutar sedan svara i samma stund som företagets VPN ansluter. Ingenting i containern har ändrats: ingen omstart, ingen bilduppdatering, ingen konfigurationsändring. Det som ändrats är värdens routingtabell, där en rutt som tunneln lade till nu täcker adressen som Docker delade ut. Innan du ändrar ett subnät eller begär ett VPN-undantag, lägg båda adressplanerna sida vid sida, eftersom åtgärderna kostar mycket olika mycket tid.
Läs rutten innan du ändrar konfigurationen
Börja med nätverket som Docker faktiskt byggde. IPAM-blocket är adressplanen, och subnätet är den rad som spelar roll:
docker network inspect project_default --format '{{json .IPAM.Config}}'
# [{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]Compose tilldelar som standard ett /16 till varje nytt projekt, så två tjänster kan rymmas inom ett intervall på 65,536 adresser. Det är den bredden som gör överlappningar vanliga: ett slumpmässigt valt /16 är stort nog att svälja en sajt du når via tunneln, och ingen märker det förrän tunneln kommer upp.
Jämför sedan ruttvalet för en containeradress, en gång med tunneln nere och en gång med den uppe:
ip route get 172.18.0.7
# 172.18.0.7 dev br-3f2a9c src 172.18.0.1Två svarstyper leder till olika ställen. Om utskriften namnger tun0, wg0 eller en annan tunnelenhet är ett VPN-prefix minst lika specifikt som bronrutten och paketet når aldrig fram till containern. Om den fortfarande namnger bron kan värden nå containern, och ditt fel är filtrering, DNS eller en returväg som bara fallerar i en riktning.
Varför en /16-kollision är lätt att träffa
Docker hämtar bronätverk från de privata poolerna, och standardpoolen kommer från 172.16.0.0/12-blocket i /16-bitar. Företagsnätverk använder ofta samma privata utrymme, och en fjärråtkomst-VPN skjuter de prefixen till varje klient som ansluter. När båda sidor tror sig äga 172.18.0.0/16, håller en routingtabell två motstridiga anspråk.
Prefixlängden avgör vinnaren, inte ordningen rutterna står i. En tunnel som skjuter 10.0.0.0/8 slår inte en bronrutt på 172.18.0.0/16, eftersom /8 är mindre specifikt. En tunnel som skjuter 172.18.0.0/16, eller 172.18.5.0/24 inuti den, gör det. Därför fungerar en laptop på kontorsnätet och fallerar på hotellets Wi-Fi med samma VPN: klienten får olika prefix.
Samma tabell styr den andra riktningen. En container som behöver en intern värd på 10.8.4.20 skickar paketet till sin brongateway, och värden routar det sedan in i tunneln. Den vägen fungerar oftast eftersom värden utför NAT, men den fallerar när företagssidan bara accepterar laptopens VPN-adress, eller när omvänd vägfiltrering släpper det asymmetriska svaret.
Jämför de tre åtgärderna
Det finns bara tre vägar ut, och de skiljer sig i vem som måste godkänna dem och vad som går sönder medan du tillämpar en.
| Repair | Who owns it | What it costs |
|---|---|---|
| Renumber the container network | You | Recreates the network, restarts services, invalidates literal addresses in configs and scripts |
| Exclude your range from the tunnel | Network team | Changes what every laptop sees, needs a change request and a client profile update |
| Reach the service through a published port | You and the client | One port only, no direct container-to-container routing across the tunnel |
Omnumrering är den enda åtgärd du kan slutföra utan ett ärende, och därför slutar de flesta texter där. Det är billigt på ett ungt projekt och dyrt när ett migreringsverktyg eller en sparad anslutningssträng pekar på en litterär adress. Räkna de referenserna innan du redigerar YAML, inte efter.
Ett undantag från tunneln är det renaste svaret när överlappningen ligger i ett intervall som ditt företag verkligen äger. Det är också det långsammaste, och det är värdelöst för en konsult som inte kan ändra klientprofilen. En publicerad port eller en liten omvänd proxy undviker diskussionen: klienten talar med en adress som tunneln redan routar korrekt, och bara den porten behöver vara nåbar.
Fäst ett intervall med avsikt
För ett enskilt projekt, deklarera subnätet i compose-filen så att projektet slutar hämta från den delade poolen:
networks:
default:
ipam:
config:
- subnet: 10.201.7.0/24Ta det intervallet från ett register som ditt team underhåller, inte från ett exempel du hittade när du sökte. Kontrollera det mot prefixen VPN:en skjuter, hemmanäten folk arbetar från och de andra containernätverken på samma värd. Planera sedan återskapandet, eftersom ett befintligt nätverk behåller sitt gamla subnät: docker compose down, sedan up -d. docker network rm vägrar medan slutpunkter är anslutna, och varje ipv4_address-post i filen måste ligga inom det nya intervallet.
För nätverk som Docker skapar själv, ställ in en daemonpool i /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.201.0.0/16", "size": 24 },
{ "base": "10.202.0.0/16", "size": 24 }
]
}Poolen gäller nätverk som skapas efter att daemonen startat om, och broar som redan finns behåller sina adresser. Det är bekvämt och lätt att glömma, så behandla ett gammalt projekt som fortfarande överlappande tills någon återskapar dess nätverk. Dockers nätverksdokumentation för motorn beskriver tilldelningsreglerna om du behöver försvara poolen i en granskning.
Linux-bron och Docker Desktop fallerar olika
På en Linux-värd sitter containrarna på broar i värdens namnutrymme. En tabell avgör allt, så jämförelsen ovan är hela historien: läs bronrutten, läs tunnelrutten och se vilken som vinner för en verklig containeradress.
Docker Desktop placerar containrar bakom en VM med egen NAT. Värden har ingen rutt alls till containeradressen; den når tjänsten via en publicerad port, vanligen bunden till 127.0.0.1. En VPN-klient kan därför bryta tjänsten helt utan subnätöverlappning: antingen genom att installera en rutt för VM:ens interna intervall eller genom att blockera loopback-trafik medan tunneln är ansluten. Om ip route get på värden rapporterar att det inte finns någon rutt till containeradressen är det förväntat och inte felet.
Den praktiska konsekvensen är var du tittar först. På Linux, börja med routingtabellen. På Desktop, börja med den publicerade porten och VPN-klientens brandväggsregler, och kontrollera sedan om VM:ens interna subnät kolliderar med något som tunneln skjuter.
Verifiera med tunneln uppe
Anslut VPN:en två gånger innan du kallar det löst, eftersom en profil som skjuter rutter vid anslutning och drar tillbaka dem vid frånkoppling kan se korrekt ut efter första försöket. Kör sedan de kontroller som klienten faktiskt kör:
ip route getfrån värden, med containerens verkliga adress.- En begäran från en annan container, för att bevisa att själva tjänsten är frisk.
curl -sv http://127.0.0.1:8080/från klienten med tunneln uppe, sedantraceroute -ntill containeradressen från en maskin på LAN:et.sysctl net.ipv4.conf.all.rp_filterom begäran når värden men svaret försvinner.
Skriv till slut in det valda intervallet i samma register som du använde för att välja det, med datum och projektet som äger det. Nästa överlappning kommer från ett projekt som ingen dokumenterade, och det enda försvaret är att intervallet reserverades med avsikt i stället för att dras slumpmässigt.