Proveo sam veći dio jutra ovog proljeća jureći kontejner koji nije mogao razriješiti nijedno interno ime, dok je preglednik pored njega otvarao wiki bez pritužbe. Tunel je bio podignut, preglednik je to dokazao, a kontejner je ostao u drugom mrežnom imenskom prostoru s drugom tablicom ruta i drugim resolverom. Ono što slijedi jest redoslijed koji sada provjeravam: prvo tablice ruta, drugo razrješavanje, treće prosljeđivanje i na kraju platforma hosta. Zaustavite se na prvom koraku koji već pada, jer dodavanje pravila niže u stogu skriva granicu umjesto da je pomjera.

Uporedite tri tablice ruta prije nego promijenite pravilo

Kontejner na zadanom Docker mostu ima jednu zadanu rutu koja pokazuje na docker0 pristupnik i nema pojma da tun0 postoji. Host ima tun0, rute koje je VPN gurnuo i moguće pravilo politike koje šalje probni promet kroz drugo sučelje nego što očekujete. WSL dodaje treću tablicu iza vlastitog virtualnog adaptera. Ispišite sve tri prije nego uredite bilo koju od njih.

ContextCommandWhat a wrong answer looks like
Hostip route get 10.8.0.20Leaves a physical interface instead of tun0
Containerip routeOnly 172.17.0.0/16 via 172.17.0.1
Container DNScat /etc/resolv.confA public resolver, or an upstream the tunnel never pushed
WSLip addr; ip routeOnly the WSL subnet, no Windows VPN adapter

Ako je jedina ruta kontejnera most i tun0 se nikada ne pojavi u njegovom imenskom prostoru, ništa ispod neće pomoći: nikakva promjena poslužitelja imena ne stvara rutu, a nikakva ruta ne stvara identitet tunela koji će peer prihvatiti. Redoslijed je važan jer kvar DNS-a i kvar rute izgledaju identično iznutra ljuske.

Usmjerite resolver na tunel umjesto na javni

Na hostu, resolvectl status navodi DNS poslužitelje po vezi, a readlink -f /etc/resolv.conf vam kaže je li datoteka systemd stub, NetworkManager datoteka ili nešto što je prepisao klijent dobavljača. Unutar kontejnera na mreži koju je definirao korisnik, /etc/resolv.conf imenuje 127.0.0.11, Dockerov ugrađeni resolver, koji prosljeđuje na onaj upstream s kojim je daemon bio konfiguriran. Kontejner na zadanom mostu je pak dobio kopiju datoteke hosta kada je stvoren i drži je.

Ta kopija je razlog zašto tunel koji se podigne nakon pokretanja kontejnera ostavlja kontejner da pita stari upstream. Ili eksplicitno proslijedite VPN resolver s docker run --dns 10.8.0.53, ili postavite "dns": ["10.8.0.53"] u /etc/docker/daemon.json i ponovo pokrenite daemon, ili ponovo stvorite kontejner kada je tunel podignut. Ponovno stvaranje je obično jeftinije od otklanjanja greške u resolveru koji je bio ispravan prije sat vremena.

Zamjena javnim resolverom je popravka koja zadržava simptom. On razrješava internet i pada u svakoj privatnoj zoni i može tiho zaobići filtriranje zbog kojeg tunel postoji. Ako pokušate pisati unutar /etc/resolv.conf kontejnera koji radi, očekujte Device or resource busy: Docker bind-montira tu datoteku, pa izmjena prvo treba unmount. Postavljanje --dns pri stvaranju je slijed koji preživljava restart.

Prosljeđujte i prevodite kada kontejneri posuđuju tunel

Sa zadanim mostom, host je ruter između docker0 i tun0 i prema zadanom ne obavlja nijedan od tih poslova. Uključite prosljeđivanje i prevedite izvornu adresu tako da peer vidi adresu tunela hosta umjesto one na koju nema povratnu rutu (zamijenite tun0 onim kako vaš klijent naziva sučelje):

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A FORWARD -i docker0 -o tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -o docker0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

FORWARD pravila su važna na hostovima čija je politika prosljeđivanja DROP. Docker ubacuje vlastite lance, pa testirajte paketom umjesto čitanjem iptables -S: iz kontejnera, ip route get 10.8.0.20 treba imenovati pristupnik mosta, a nc -vz 10.8.0.20 443 treba se povezati ili isteći na tunelu umjesto na resolveru.

Bez MASQUERADE peer treba povratnu rutu do 172.17.0.0/16, koju većina VPN-ova za daljinski pristup neće gurnuti. Ovdje se također lomi plan s klijentom zasnovanim na politikama: ako hvata samo promet od određenih UID-ova ili cgroups, vaša pravila prosljeđivanja nikada ne vide paket unutar tunela, a popravka pripada rutnoj politici klijenta, a ne iptablesu. Pravila dodana ručno nestaju pri restartu, pa ih stavite u systemd jedinicu ili netfilter-persistent tablicu i ponovo testirajte nakon restarta, jer tunel koji se podigne kasno može ih isprati.

Stavite tunel unutar kontejnera kada je izolacija cilj

Ako jedan kontejner mora izaći kroz određeni čvor, pokrenite klijent u tom kontejneru. Potreban mu je --cap-add NET_ADMIN i --device /dev/net/tun, i tada posjeduje vlastiti tun0, rute i vatrozid. Ako drugi kontejneri prolaze kroz njega, treba mu i prosljeđivanje i MASQUERADE pravilo unutar kontejnera, a tim peerovima treba eksplicitna ruta ili proxy koji pokazuje na njega; zadani Docker most tu rutu ne dodaje umjesto vas.

Mreža hosta, docker run --network host, je jeftina verzija: kontejner se pridružuje imenskom prostoru hosta i vidi rute tunela izravno. Također odbacuje mrežnu izolaciju, poziva na sudare portova i u praksi je Linux opcija. Objavljeni port nije zamjena. -p 8080:80 čini uslugu dostupnom s hosta; ne čini interne 10.x adrese dostupnim iznutra kontejnera, a miješanje to dvoje je način na koji problem rutiranja postaje izložen port baze podataka.

Razlikujte kvar imena od preklapajuće podmreže

Zadani Docker most je 172.17.0.0/16, a mnogi korporativni VPN bazeni nalaze se unutar 172.16.0.0/12. Kada tunel posjeduje taj raspon, promet namijenjen kontejneru može se umjesto toga usmjeriti u tunel. Imena padaju, greška izgleda kao DNS, a problem su adrese. Provjerite odredište, ne resolver: ip route get 172.17.0.5 na hostu, zatim uporedite prefiks mosta s VPN bazenom i s docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.

Popravka je premjestiti jedan od raspona: ili drugi zadani bazen adresa za Docker u daemon.json ili drugi bazen na koncentratoru. Dodavanje još jednog poslužitelja imena ovdje ne pomaže, a ne pomaže ni ssh -b: vezivanje klijenta za sučelje mijenja izvornu adresu, a ne odredišnu rutu koja pokazuje u tunel. Brza podjela posla: ime koje se razrješava na hostu ali ne u kontejneru je problem resolvera, dok je izravna IP veza koja visi u kontejneru ali radi na hostu problem rute ili lanca prosljeđivanja.

WSL i Docker Desktop ne slijede Linux pravila

U zadanom NAT načinu, WSL stoji iza vlastitog virtualnog adaptera i ne prima interne rute ili DNS sufikse Windows VPN-a, zbog čega preglednik radi, a ping unutar distribucije ne. Zrcalno umrežavanje, dostupno u novijim Windows 11 buildovima, umjesto toga dijeli sučelja hosta, ali se mora omogućiti u .wslconfig i distribucija ponovo pokrenuti s wsl --shutdown. Potvrdite aktivni način s wslinfo --networking-mode prije primjene popravke, jer je NAT odgovor pogrešan za zrcalni način i obrnuto.

Docker Desktop dodaje još jedan sloj. Linux kontejneri na Windowsu stoje iza NAT implementacije koja se historijski zove vpnkit, a njihov put se razlikuje od nativnog Linux hosta. Windows kontejneri se povezuju kroz Hyper-V virtualni prekidač, a korporativni klijent može blokirati promet s tog prekidača, pa Linux kontejneri na istom stroju mogu doći do mreže dok Windows kontejneri nemaju ništa. Vanjski prekidač vezan za fizički adapter uobičajeno je rješenje i mijenja mrežni identitet kontejnera na načine koje politika možda ne dopušta.

Prije nego napišete još jedno pravilo, prođite četiri sloja po redu i zaustavite se gdje je jedan od njih pogrešan:

  • Unutar kontejnera: da li ip route doseže privatni prefiks i da li /etc/resolv.conf imenuje upstream koji ga razrješava?
  • Na hostu: je li prosljeđivanje uključeno, je li NAT vezan za sučelje tunela i prihvaća li lanac prosljeđivanja most prema tunelu?
  • Na peeru: zna li povratni put ili se oslanjate na NAT da sakrije podmrežu kontejnera?
  • Na platformi: je li WSL u NAT ili zrcalnom načinu i je li kontejner Linux ili Windows?

Ponovno pokrenite svaki test u novom kontejneru ili ljusci. Dugotrajni proces s keširanim odgovorom ili uspostavljenom vezom nastavit će padati i nakon što konfiguracija bude ispravna, a taj kvar izgleda upravo kao popravka koja nije uspjela.

Povezani članci