Slik ruter du bare Docker-containertrafikk gjennom OpenVPN
La verten bli på sin vanlige uplink og legg ett containernett i tunnelen: NAT-rekkefølge, kilderegler, OpenVPN iroute og MSS-kontroller.
Pakken fra containeren når aldri tun0 , og ekstra egenskaper på containeren endrer ikke det. Mellom gjesten og tunnelen ligger to ruteoppslag: ett inne i gjestens nettverksnavnerom, ett på verten etter at rammen lander på broen, og det andre spør en annen tabell med andre matchregler. Oppsettet mitt er en Linux-vert som kjører en OpenVPN klient som holder 10.8.0.10 på tun0 , en Docker-bro på 172.18.0.0/16 , og en privat adresse som bare tunnelsiden kan nå. Målet forblir smalt: det gjestenettet går ut gjennom tunnelen mens verten beholder sin vanlige uplink.
Å kjøre VPN-klienten inne i containeren er et annet design , ikke en snarvei. Da eier containeren sin egen tunnel og sine egne legitimasjoner, og arbeidet flytter inn i en Dockerfile i stedet for å bli i iptables.
Videresending velger rute etter destinasjon, så skriver NAT om kilden
Med net.ipv4.ip_forward=1 satt blir en videresendt pakke rutet bare etter destinasjon, i hovedtabellen. Kilden spiller ingen rolle i den beslutningen. Først etter at ruten er valgt, kjører kjernen POSTROUTING , og det er der Dockers egen regel sitter: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE . Alt som forlater broen til et annet grensesnitt, blir oversatt, og adressen det blir til avhenger av grensesnittet ruten sendte det til.
Den regelen forklarer de fleste symptomene jeg blir spurt om. Hvis hovedtabellen ikke har en rute til den private destinasjonen, forlater pakken uplinken med vertens fysiske adresse. Leverandøren din dropper destinasjonen, eller det eksterne nettstedet dropper en RFC 1918-kilde det aldri forventet, og containeren får tidsavbrudd mens tunnelen står oppe og er sunn. En opptak på tun0 viser ingenting, og det er tegnet. Hvis ruten faktisk peker på tun0 , skriver MASQUERADE om kilden til 10.8.0.10 , og samme container virker uten at du rører noe.
Be om oppslaget som navngir gjestens kilde. Uten from og iif svarer verten for sin egen socket og rapporterer en vei den videresendte pakken aldri vil ta:
ip route get 192.168.11.222 from 172.18.0.5 iif br-appnetHvis svaret navngir uplinken, er ikke tunnelen i bildet, og ingen innstilling på tunnelsiden hjelper.
Flytt gjesten over på tunnelens egen adresse
Den minste endringen som virker, er en eksplisitt kildeomskriving på vei ut. Sett den inn i stedet for å legge den til: Docker sitter allerede i spissen av POSTROUTING , så en regel lagt til med -A vurderes bak Dockers og utløses aldri.
iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10To betingelser avgjør om dette holder. Adressen må være en klienten faktisk har, så fest den med en oppføring per klient som inneholder ifconfig-push 10.8.0.10 255.255.255.0 ; en regel som navngir en adresse som aldri ble tildelt, gir stille dropp, ikke en feil. Og den eksterne siden må godta den kilden og rute svar tilbake, derfor navngir en tillatelsesliste der tunneladressen i stedet for gjestenettet. Når klientadressen er dynamisk, -j MASQUERADE med -o tun0 følger det grensesnittet har, på bekostning av ekstern politikk du ikke kan skrive mot en fast verdi.
Filtrering er den andre halvdelen. Docker setter FORWARD -policyen til drop og tømmer kjedene den eier ved hver daemon-restart, så legg lokal politikk i DOCKER-USER , som overlever:
iptables -I DOCKER-USER -i br-appnet -o tun0 -j ACCEPT
iptables -I DOCKER-USER -i tun0 -o br-appnet -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTReturregelen er viktig: et svar som ankommer på tun0 for en tilkobling åpnet av broen, godtas ikke bare fordi retningen ut ble det.
Hold verten direkte med en kilderegel
Når hele broen skal bruke tunnelen, men ingenting annet på verten skal, hopp over NAT og gi den kilden sin egen tabell. Gjesteadresser forblir da synlige hele veien.
echo "200 vpnbox" >> /etc/iproute2/rt_tables
ip rule add from 172.18.0.0/16 lookup vpnbox pref 1000
ip route add default dev tun0 table vpnboxRegler konsulteres før hovedtabellen, og from matcher kilden. Tunnelens egne ytre pakker bærer vertens fysiske kilde, så de matcher aldri from 172.18.0.0/16 , og tunnelen kan ikke rute seg selv tilbake i seg selv. Den selvekskluderingen er det som gjør oppsettet stabilt.
Returveien trenger nå hjelp. Den eksterne siden ser en uoversatt 172.18.0.x og har ingen grunn til å vite hvor den bor. På serveren trenger en fil per klient iroute 172.18.0.0 255.255.0.0 , slik at serverens interne tabell gir det prefikset til denne klienten, og en pushet route 172.18.0.0 255.255.0.0 , slik at andre klienter vet at den finnes.
To kontroller før du stoler på det. Kjør ip route show default : med redirect-gateway def1 holder tabellen 0.0.0.0/1 og 128.0.0.0/1 via tun0 , og verten er slett ikke direkte, fordi de halve standardrutene vinner uten å erstatte default . Regler lagt til med ip rule add forsvinner også ved omstart, så bær dem i et up-skript.
Pakkestørrelse er den andre feilen du møter
Et brogrensesnitt på 1500 byte gir tunnelen mer enn den kan bære når de ytre UDP- og IP-hodene kommer til. Små utvekslinger går gjennom, så DNS løses og TLS-håndtrykket fullføres, deretter henger en større opplasting uten noen feil noe sted. Avsenderen fortsetter å gjenta samme segmentstørrelse fordi ICMP-meldingen om fragmenteringsbehov, som ville bedt den krympe, aldri kommer.
Fiks det på grensesnittet, på MSS, eller begge. Klemming krever ingen medvirkning fra gjesten:
ip link set dev tun0 mtu 1400
iptables -t mangle -A FORWARD -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuKlemmingen skriver bare om TCP. UDP, inkludert QUIC, trenger i stedet et lavere gjestegrensesnitt, med --opt com.docker.network.driver.mtu=1400 ved nettverksoppretting eller den matchende compose-nøkkelen.
DNS og kontrollene som er verdt å gjenta
Navneoppslag følger samme vei. Dockers innebygde resolver på 127.0.0.11 videresender til vertens konfigurerte servere, så en container arver det verten bruker. Når den private sonen bare besvares av en resolver som er tilgjengelig gjennom tunnelen, pek gjesten på den adressen, og test med getent hosts inne i containeren i stedet for på verten.
- Kjør den feilende forespørselen inne i gjesten; en test på vertssiden bruker en annen kildeadresse og skjuler problemet.
- Les tellerne på NAT- og filterreglene du la til. En regel uten treff er en rutefeil, ikke en politikkfeil.
- Bekreft at tunneladressen i SNAT-regelen din er en klienten faktisk har.
- Senk gjestens MTU eller klem MSS før du klandrer serveren for en stans.
Gå nedover listen og stopp ved første feil. Det laget er det som må rettes; alt som legges til lenger ned, skjuler bare grensen som ryker.