Paket kontejnera nikada ne stiže do tun0, a dodatne sposobnosti na kontejneru to neće promijeniti. Između gosta i tunela stoje dvije pretrage rute: jedna unutar mrežnog imenskog prostora gosta, jedna na hostu nakon što okvir sleti na bridge, a druga pita drugu tabelu s drugačijim pravilima podudaranja. Moja referentna postavka je Linux host s OpenVPN klijentom koji drži 10.8.0.10 na tun0, Docker bridgeom na 172.18.0.0/16 i privatnom adresom do koje samo strana tunela može doći. Cilj ostaje uzak: ta gostujuća podmreža izlazi kroz tunel dok host zadržava svoj uobičajeni uplink.

Pokretanje VPN klijenta unutar kontejnera je drugačiji dizajn, a ne prečica. Kontejner tada posjeduje svoj tunel i vjerodajnice, a posao se seli u Dockerfile umjesto da ostane u iptablesu.

Prosljeđivanje bira rutu po odredištu, zatim NAT prepisuje izvor

Kada je postavljeno net.ipv4.ip_forward=1, proslijeđeni paket se rutira samo po odredištu, u glavnoj tabeli. Izvor ne igra nikakvu ulogu u toj odluci. Tek nakon što se ruta odabere, kernel pokreće POSTROUTING, i tu stoji Dockerovo vlastito pravilo: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. Sve što napusti bridge prema drugom interfejsu biva prevedeno, a adresa koju dobije zavisi od interfejsa na koji ga je ruta poslala.

To pravilo objašnjava većinu simptoma o kojima me pitaju. Ako glavna tabela nema rutu za privatno odredište, paket izlazi kroz uplink noseći fizičku adresu hosta. Vaš provajder odbacuje odredište, ili udaljena stranica odbacuje izvor RFC 1918 koji nikada nije očekivala, i kontejneru istekne vrijeme dok tunel ostaje podignut i zdrav. Snimak na tun0 ne pokazuje ništa i to je znak. Ako ruta ipak pokazuje na tun0, MASQUERADE prepisuje izvor na 10.8.0.10 i isti kontejner radi bez toga da bilo šta dirate.

Tražite pretragu koja imenuje izvor gosta. Bez from i iif host odgovara za svoj vlastiti socket i prijavljuje putanju koju proslijeđeni paket nikada neće uzeti:

ip route get 192.168.11.222 from 172.18.0.5 iif br-appnet

Ako taj odgovor imenuje uplink, tunel nije u slici i nikakva postavka na strani tunela neće pomoći.

Prevedite gosta na vlastitu adresu tunela

Najmanja promjena koja radi je izričito prepisivanje izvora na izlazu. Umetnite ga, umjesto da ga dodajete: Docker već sjedi na čelu POSTROUTING, pa se pravilo dodano s -A procjenjuje iza Dockerovog i nikada se ne pokreće.

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

Dva uslova odlučuju da li će se to održati. Adresa mora biti ona koju klijent zaista drži, zato je prikujte zapisom po klijentu koji sadrži ifconfig-push 10.8.0.10 255.255.255.0; pravilo koje imenuje adresu koja nikada nije dodijeljena proizvodi nijemo odbacivanje, ne grešku. I dalja strana mora prihvatiti taj izvor i usmjeriti odgovore natrag, zato lista dozvola tamo imenuje adresu tunela, a ne gostujuću podmrežu. Kada je adresa klijenta dinamička, -j MASQUERADE s -o tun0 slijedi ono što interfejs drži, po cijeni udaljene politike protiv koje ne možete pisati fiksnu vrijednost.

Filtriranje je druga polovina. Docker postavlja politiku FORWARD na drop i briše lance koje posjeduje pri svakom restartu daemona, zato stavite lokalnu politiku u DOCKER-USER, koja preživljava:

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

Pravilo povratka je važno: odgovor koji stigne na tun0 za vezu otvorenu kroz bridge nije prihvaćen samo zato što je odlazni smjer bio.

Držite host direktnim pravilom izvora

Kada cijeli bridge treba koristiti tunel, a ništa drugo na hostu ne treba, preskočite NAT i dajte tom izvoru vlastitu tabelu. Adrese gostiju tada ostaju vidljive od početka do kraja.

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

Pravila se konsultuju prije glavne tabele, i from se podudara s izvorom. Vlastiti vanjski paketi tunela nose fizički izvor hosta, pa se nikada ne podudaraju s from 172.18.0.0/16 i tunel ne može usmjeriti sebe natrag u sebe. To samoizuzimanje čini postavku stabilnom.

Putanja odgovora sada treba pomoć. Dalja strana vidi neprevedenu 172.18.0.x i nema razloga znati gdje živi. Na serveru, datoteka po klijentu treba iroute 172.18.0.0 255.255.0.0 da bi serverova unutrašnja tabela predala taj prefiks ovom klijentu, i gurnutu route 172.18.0.0 255.255.0.0 da bi drugi klijenti znali da postoji.

Dvije provjere prije nego mu povjerujete. Pokrenite ip route show default: s redirect-gateway def1 tabela drži 0.0.0.0/1 i 128.0.0.0/1 preko tun0, a host uopće nije direktan, jer te polovine zadanog pobjeđuju bez zamjene default. Pravila dodana s ip rule add također nestaju pri restartu, zato ih nosite u up skripti.

Veličina paketa je drugi kvar na koji naletite

Bridge interfejs na 1500 bajtova daje tunelu više nego što može nositi kada se dodaju vanjski UDP i IP zaglavlja. Male razmjene prolaze, pa se DNS razriješi i TLS rukovanje završi, a zatim veći upload visi bez ikakve greške. Pošiljalac stalno ponavlja istu veličinu segmenta jer ICMP poruka o potrebi fragmentacije, koja bi mu rekla da se smanji, nikada ne stiže.

Popravite to na interfejsu, na MSS-u ili oboje. Stezanje ne traži saradnju gosta:

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

Stezanje prepisuje samo TCP. UDP, uključujući QUIC, treba umjesto toga spušten interfejs gosta, s --opt com.docker.network.driver.mtu=1400 pri kreiranju mreže ili odgovarajućim compose ključem.

DNS i provjere koje vrijedi ponavljati

Razrješavanje imena slijedi istu putanju. Dockerov ugrađeni resolver na 127.0.0.11 prosljeđuje na konfigurisane servere hosta, pa kontejner nasljeđuje ono što host koristi. Kada privatnu zonu odgovara samo resolver dostupan kroz tunel, usmjerite gosta na tu adresu i testirajte s getent hosts unutar kontejnera, a ne na hostu.

  • Pokrenite neuspjeli zahtjev unutar gosta; test na strani hosta koristi drugu izvornu adresu i skriva problem.
  • Čitajte brojače na NAT i filter pravilima koja ste dodali. Pravilo s nula podudaranja je greška rutiranja, ne politike.
  • Potvrdite da je adresa tunela u vašem SNAT pravilu ona koju klijent zaista drži.
  • Snizite MTU gosta ili stegnite MSS prije nego okrivite server za zastoj.

Prođite kroz taj spisak i zaustavite se na prvom kvaru. Taj sloj treba popraviti; sve što se doda dalje samo skriva granicu koja se lomi.

Povezani članci