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 most, a druga pita drugu tablicu s drugačijim pravilima podudaranja. Moja referentna postava je Linux host s OpenVPN klijentom koji drži 10.8.0.10 na tun0, Docker mostom 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čac. 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 se paket usmjerava samo po odredištu, u glavnoj tablici. 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 most prema drugom sučelju biva prevedeno, a adresa koju dobije zavisi od sučelja na koje ga je ruta poslala.

To pravilo objašnjava većinu simptoma o kojima me pitaju. Ako glavna tablica nema rutu za privatno odredište, paket izlazi kroz uplink noseći fizičku adresu hosta. Vaš pružatelj odbacuje odredište, ili udaljeno mjesto odbacuje izvor RFC 1918 koji nikada nije očekivalo, i kontejneru istekne vrijeme dok tunel ostaje podignut i zdrav. Snimka 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 što 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 uvjeta odlučuju hoće li 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 popis dopuštenih tamo imenuje adresu tunela, a ne gostujuću podmrežu. Kada je adresa klijenta dinamička, -j MASQUERADE s -o tun0 slijedi ono što sučelje drži, po cijeni udaljene politike protiv koje ne možete pisati fiksnu vrijednost.

Filtriranje je druga polovica. 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 most nije prihvaćen samo zato što je odlazni smjer bio.

Držite host izravnim pravilom izvora

Kada cijeli most treba koristiti tunel, a ništa drugo na hostu ne treba, preskočite NAT i dajte tom izvoru vlastitu tablicu. 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 savjetuju prije glavne tablice, 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 postavu stabilnom.

Putanja odgovora sada treba pomoć. Dalja strana vidi neprevedenu 172.18.0.x i nema razloga znati gdje živi. Na poslužitelju, datoteka po klijentu treba iroute 172.18.0.0 255.255.0.0 da bi poslužiteljeva unutarnja tablica 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 tablica drži 0.0.0.0/1 i 128.0.0.0/1 preko tun0, a host uopće nije izravan, jer te polovice 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

Most 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šiljatelj 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 sučelju, na MSS-u ili oboje. Stezanje ne traži suradnju 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šteno sučelje gosta, s --opt com.docker.network.driver.mtu=1400 pri stvaranju mreže ili odgovarajućim compose ključem.

DNS i provjere koje vrijedi ponavljati

Razrješavanje imena slijedi istu putanju. Dockerov ugrađeni razrješivač na 127.0.0.11 prosljeđuje na konfigurirane poslužitelje hosta, pa kontejner nasljeđuje ono što host koristi. Kada privatnu zonu odgovara samo razrješivač 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 usmjeravanja, 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 poslužitelj za zastoj.

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

Povezani članci