Containerens pakke når aldrig tun0, og ekstra capabilities på containeren ændrer ikke det. To ruteopslag står mellem gæsten og tunnelen: ét inde i gæstens netværksnavnerum, ét på værten efter at rammen lander på broen, og det andet spørger en anden tabel med andre matchregler. Mit referenceopsæt er en Linux-vært, der kører en OpenVPN klient, som har 10.8.0.10 på tun0, en Docker-bro på 172.18.0.0/16 og en privat adresse, som kun tunnelsiden kan nå. Målet forbliver snævert: det gæste-subnet forlader gennem tunnelen, mens værten beholder sin normale uplink.

At køre VPN-klienten inde i containeren er et andet design, ikke en genvej. Containeren ejer så sin egen tunnel og sine legitimationsoplysninger, og arbejdet flytter ind i en Dockerfile i stedet for at blive i iptables.

Videresendelse vælger en rute efter destination, derefter omskriver NAT kilden

Med net.ipv4.ip_forward=1 sat sendes en videresendt pakke kun efter destination i hovedtabellen. Kilden spiller ingen rolle i den beslutning. Først efter at ruten er valgt, kører kernen POSTROUTING, og det er der, Dockers egen regel ligger: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. Alt, der forlader broen til en anden grænseflade, bliver oversat, og den adresse, det bliver til, afhænger af den grænseflade, ruten sendte det til.

Den regel forklarer de fleste af de symptomer, jeg bliver spurgt om. Hvis hovedtabellen ikke har en rute til den private destination, forlader pakken uplinken med værtens fysiske adresse. Din udbyder dropper destinationen, eller det fjerne websted dropper en RFC 1918-kilde, det aldrig forventede, og containeren løber tør for tid, mens tunnelen forbliver oppe og sund. En optagelse på tun0 viser intet, og det er tegnet. Hvis ruten faktisk peger på tun0, omskriver MASQUERADE kilden til 10.8.0.10 og den samme container virker, uden at du rører noget.

Bed om det opslag, der navngiver gæstens kilde. Uden from og iif svarer værten for sin egen socket og rapporterer en vej, som den videresendte pakke aldrig vil tage:

ip route get 192.168.11.222 from 172.18.0.5 iif br-appnet

Hvis det svar navngiver uplinken, er tunnelen ikke med i billedet, og ingen indstilling på tunnelsiden hjælper.

Flyt gæsten over på tunnelens egen adresse

Den mindste ændring, der virker, er en eksplicit kildeomskrivning på vej ud. Indsæt den i stedet for at tilføje den: Docker sidder allerede i spidsen af POSTROUTING, så en regel tilføjet med -A evalueres bag Dockers og udløses aldrig.

iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10

To betingelser afgør, om det holder. Adressen skal være en, klienten faktisk har, så fastgør den med en post pr. klient, der indeholder ifconfig-push 10.8.0.10 255.255.255.0; en regel, der navngiver en adresse, der aldrig blev tildelt, giver lydløse drops, ikke en fejl. Og den fjerne side skal acceptere den kilde og route svar tilbage, derfor navngiver en allowlist der tunneladressen i stedet for gæste-subnettet. Når klientadressen er dynamisk, -j MASQUERADE med -o tun0 følger hvad grænsefladen end har, på bekostning af fjernpolitik, som du ikke kan skrive mod en fast værdi.

Filtrering er den anden halvdel. Docker sætter FORWARD politikken til drop og rydder de kæder, den ejer, ved hver daemon-genstart, så læg lokal politik 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 ACCEPT

Returreglen er vigtig: et svar, der ankommer på tun0 for en forbindelse åbnet af broen, accepteres ikke bare fordi retningen ud blev.

Hold værten direkte med en kilderegel

Når hele broen skal bruge tunnelen, men intet andet på værten skal, spring NAT over og giv den kilde sin egen tabel. Gæsteadresser forbliver så synlige hele vejen igennem.

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 vpnbox

Regler konsulteres før hovedtabellen, og from matcher kilden. Tunnelens egne ydre pakker bærer værtens fysiske kilde, så de matcher aldrig from 172.18.0.0/16 og tunnelen kan ikke route sig selv tilbage i sig selv. Den selvekskludering er det, der gør opsættet stabilt.

Svarvejen har nu brug for hjælp. Den fjerne ende ser en uoversat 172.18.0.x og har ingen grund til at vide, hvor den bor. På serveren skal en fil pr. klient have iroute 172.18.0.0 255.255.0.0, så serverens interne tabel giver det præfiks til denne klient, og en pushet route 172.18.0.0 255.255.0.0, så andre klienter ved, at den findes.

To kontroller, før du stoler på det. Kø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 værten er slet ikke direkte, fordi de halve standardruter vinder uden at erstatte default. Regler tilføjet med ip rule add forsvinder også ved genstart, så bær dem i et up-script.

Pakningsstørrelse er den fejl, du møder som den anden

En brogrænseflade på 1500 bytes giver tunnelen mere, end den kan bære, når de ydre UDP- og IP-headere kommer til. Små udvekslinger går igennem, så DNS opløses og TLS-håndtrykket fuldføres, derefter hænger en større upload uden nogen fejl. Afsenderen bliver ved med at gentage den samme segmentstørrelse, fordi ICMP-meddelelsen om fragmentering, som ville fortælle den at krympe, aldrig ankommer.

Ret det ved grænsefladen, ved MSS, eller begge. Clamping kræver ingen medvirken fra gæsten:

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-pmtu

Clampingen omskriver kun TCP. UDP, inklusive QUIC, kræver i stedet en lavere gæstegrænseflade, med --opt com.docker.network.driver.mtu=1400 ved netværksoprettelse eller den matchende compose-nøgle.

DNS og de kontroller, der er værd at gentage

Navneopløsning følger samme vej. Dockers indbyggede resolver på 127.0.0.11 videresender til værtens konfigurerede servere, så en container arver, hvad værten bruger. Når den private zone kun besvares af en resolver, der kan nås gennem tunnelen, peg gæsten på den adresse, og test med getent hosts inde i containeren i stedet for på værten.

  • Kør den fejlende anmodning inde i gæsten; en test på værtsiden bruger en anden kildeadresse og skjuler problemet.
  • Læs tællerne på de NAT- og filterregler, du tilføjede. En regel uden match er en routingfejl, ikke en politikfejl.
  • Bekræft, at tunneladressen i din SNAT-regel er en, klienten faktisk har.
  • Sænk gæstens MTU eller clamp MSS, før du bebrejder serveren for en pause.

Gå ned ad listen, og stop ved den første fejl. Det lag er det, der skal rettes; alt, der tilføjes længere nede, skjuler kun den grænse, der brækker.

Relaterede artikler