Šį pavasarį didžiąją dalį ryto praleidau gaudydamas konteinerį, kuris negalėjo išspręsti nė vieno vidinio vardo, o šalia esanti naršyklė atidarė wiki be jokių nusiskundimų. Tunelis buvo pakeltas, naršyklė tai įrodė, o konteineris kitoje tinklo vardų erdvėje su kita maršrutų lentele ir kitu resolveriu. Toliau pateikiama tvarka, kurią dabar tikrinu: pirmiausia maršrutų lentelės, antra – vardų sprendimas, trečia – perdavimas, o galiausiai – pagrindinio kompiuterio platforma. Sustokite ties pirmuoju žingsniu, kuris jau nepavyksta, nes taisyklių pridėjimas žemiau krūvoje paslepia ribą, o ne ją perkelia.

Palyginkite tris maršrutų lenteles prieš keisdami taisyklę

Konteineris Docker numatytajame tilte turi vieną numatytąjį maršrutą į docker0 šliuzą ir net neįtaria, kad tun0 egzistuoja. Pagrindiniame kompiuteryje yra tun0, VPN įstumti maršrutai ir galbūt politikos taisyklė, kuri siunčia bandomąjį srautą per kitą sąsają, nei tikitės. WSL prideda trečią lentelę už savo virtualaus adapterio. Atspausdinkite visas tris prieš redaguodami bet kurią.

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

Jei vienintelis konteinerio maršrutas yra tiltas ir tun0 niekada nepasirodo jo vardų erdvėje, niekas žemiau nepadės: joks vardų serverio pakeitimas nesukuria maršruto, ir joks maršrutas nesukuria tunelio tapatybės, kurią priims partneris. Tvarka svarbi, nes DNS gedimas ir maršruto gedimas iš vidaus atrodo vienodai.

Nukreipkite resolverį į tunelį, o ne į viešą

Pagrindiniame kompiuteryje, resolvectl status išvardija DNS serverius pagal jungtį, o readlink -f /etc/resolv.conf pasako, ar failas yra systemd stub, NetworkManager failas, ar kažkas, ką perrašė tiekėjo klientas. Konteinerio viduje vartotojo apibrėžtame tinkle, /etc/resolv.conf įvardija 127.0.0.11, įtaisytąjį Docker resolverį, kuris persiunčia į tą upstream, su kuriuo buvo sukonfigūruotas demonas. O numatytojo tilto konteineris kūrimo metu gavo pagrindinio kompiuterio failo kopiją ir ją saugo.

Būtent dėl šios kopijos tunelis, pakeltas po konteinerio paleidimo, palieka konteinerį klausti senojo upstream. Arba perduokite VPN resolverį aiškiai su docker run --dns 10.8.0.53, arba nustatykite "dns": ["10.8.0.53"] faile /etc/docker/daemon.json ir paleiskite demoną iš naujo, arba iš naujo sukurkite konteinerį, kai tunelis jau pakeltas. Iš naujo sukurti paprastai pigiau nei derinti resolverį, kuris prieš valandą buvo teisingas.

Pakeitimas viešu resolveriu yra tas remontas, kuris išsaugo simptomą. Jis išsprendžia internetą ir nepavyksta kiekvienoje privačioje zonoje, ir gali tyliai apeiti filtravimą, dėl kurio tunelis egzistuoja. Jei bandysite rašyti veikiančio konteinerio /etc/resolv.conf viduje, tikėkitės Device or resource busy: Docker bind-mountina tą failą, todėl redagavimui pirmiausia reikia atjungimo. --dns nustatymas kūrimo metu yra seka, kuri išgyvena paleidimą iš naujo.

Persiųskite ir išverskite, kai konteineriai skolinasi tunelį

Su numatytuoju tiltu pagrindinis kompiuteris yra maršrutizatorius tarp docker0 ir tun0, ir pagal numatytuosius nustatymus neatlieka nė vieno iš šių darbų. Įjunkite perdavimą ir išverskite šaltinio adresą, kad partneris matytų pagrindinio kompiuterio tunelio adresą, o ne tą, į kurį neturi grįžimo maršruto (pakeiskite tun0 tuo, kaip jūsų klientas vadina sąsają):

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 taisyklės svarbios tuose kompiuteriuose, kurių perdavimo politika yra DROP. Docker įterpia savo grandines, todėl tikrinkite paketu, o ne skaitydami iptables -S: iš konteinerio, ip route get 10.8.0.20 turėtų įvardyti tilto šliuzą, o nc -vz 10.8.0.20 443 turėtų prisijungti arba baigtis laiku tunelyje, o ne resolveryje.

Be MASQUERADE partneriui reikia grįžimo maršruto į 172.17.0.0/16, kurio dauguma nuotolinės prieigos VPN neįstums. Čia pat planą sugriauna ir politika pagrįstas klientas: jei jis pagauna tik tam tikrų UID ar cgroups srautą, jūsų perdavimo taisyklės niekada nemato paketo tunelio viduje, o pataisymas priklauso kliento maršrutizavimo politikai, o ne iptables. Rankiniu būdu pridėtos taisyklės dingsta paleidžiant iš naujo, todėl dėkite jas į systemd įrenginį arba netfilter-persistent lentelę ir po paleidimo iš naujo išbandykite dar kartą, nes vėlai pakeltas tunelis gali jas nušluoti.

Įdėkite tunelį į konteinerio vidų, kai tikslas yra izoliacija

Jei vienas konteineris turi išeiti per konkretų mazgą, paleiskite klientą tame konteineryje. Jam reikia --cap-add NET_ADMIN ir --device /dev/net/tun, ir tada jis turi savo tun0, maršrutus ir užkardą. Jei kiti konteineriai eina per jį, jam taip pat reikia perdavimo ir MASQUERADE taisyklės konteinerio viduje, o tiems partneriams reikia aiškaus maršruto arba proxy, nukreipto į jį; numatytasis Docker tiltas to maršruto už jus neprideda.

Pagrindinio kompiuterio tinklas, docker run --network host, yra pigi versija: konteineris prisijungia prie pagrindinio kompiuterio vardų erdvės ir mato tunelio maršrutus tiesiogiai. Jis taip pat atsisako tinklo izoliacijos, kviečia prievadų susidūrimus ir praktiškai yra Linux parinktis. Paskelbtas prievadas nėra pakaitalas. -p 8080:80 daro paslaugą pasiekiamą iš pagrindinio kompiuterio; jis nepadaro vidinių 10.x adresų pasiekiamų iš konteinerio vidaus, o sumaišius abu, maršrutizavimo problema virsta atviru duomenų bazės prievadu.

Atskirkite vardo gedimą nuo persidengiančio potinklio

Numatytasis Docker tiltas yra 172.17.0.0/16, o daugelis įmonių VPN baseinų yra 172.16.0.0/12 viduje. Kai tunelis turi tą diapazoną, konteineriui skirtas srautas gali būti nukreiptas į tunelį. Vardai nepavyksta, klaida atrodo kaip DNS, o problema yra adresuose. Tikrinkite paskirties vietą, o ne resolverį: ip route get 172.17.0.5 pagrindiniame kompiuteryje, tada palyginkite tilto prefiksą su VPN baseinu ir su docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.

Remontas yra perkelti vieną iš diapazonų: arba kitą numatytąjį adresų baseiną Docker failui daemon.json , arba kitą baseiną koncentratoriuje. Pridėti dar vieną vardų serverį čia nieko neduoda, ir ssh -b taip pat ne: kliento prisirišimas prie sąsajos keičia šaltinio adresą, o ne paskirties maršrutą, kuris veda į tunelį. Greitas darbo pasidalijimas: vardas, kuris išsprendžiamas pagrindiniame kompiuteryje, bet ne konteineryje, yra resolverio problema, o tiesioginis IP ryšys, kuris stringa konteineryje, bet veikia pagrindiniame kompiuteryje, yra maršruto arba perdavimo grandinės problema.

WSL ir Docker Desktop nesilaiko Linux taisyklių

Savuoju numatytuoju NAT režimu WSL stovi už savo virtualaus adapterio ir negauna Windows VPN vidinių maršrutų ar DNS priesagų, todėl naršyklė veikia, o ping platinimo viduje ne. Veidrodinis tinklas, prieinamas naujausiuose Windows 11 versijose, vietoj to dalijasi pagrindinio kompiuterio sąsajomis, bet jį reikia įjungti faile .wslconfig ir iš naujo paleisti platinimą komanda wsl --shutdown. Patvirtinkite aktyvų režimą komanda wslinfo --networking-mode prieš taikydami pataisą, nes NAT atsakymas neteisingas veidrodiniam režimui ir atvirkščiai.

Docker Desktop prideda dar vieną sluoksnį. Linux konteineriai Windows aplinkoje stovi už NAT įgyvendinimo, istoriškai vadinamo vpnkit, ir jų kelias skiriasi nuo vietinio Linux kompiuterio. Windows konteineriai jungiasi per Hyper-V virtualųjį komutatorių, o įmonės klientas gali blokuoti srautą iš to komutatoriaus, todėl tos pačios mašinos Linux konteineriai gali pasiekti tinklą, o Windows konteineriai neturi nieko. Išorinis komutatorius, susietas su fiziniu adapteriu, yra įprastas sprendimas, ir jis keičia konteinerio tinklo tapatybę taip, kaip politika gali neleisti.

Prieš rašydami dar vieną taisyklę, pereikite keturis sluoksnius iš eilės ir sustokite ten, kur vienas jų neteisingas:

  • Konteinerio viduje: ar ip route pasiekia privatų prefiksą, ir ar /etc/resolv.conf įvardija upstream, kuris jį išsprendžia?
  • Pagrindiniame kompiuteryje: ar perdavimas įjungtas, ar NAT susietas su tunelio sąsaja, ir ar perdavimo grandinė priima tiltą į tunelį?
  • Pas partnerį: ar jis žino grįžimo kelią, ar pasikliaujate NAT, kad paslėptų konteinerio potinklį?
  • Platformoje: ar WSL NAT ar veidrodiniame režime, ir ar konteineris Linux ar Windows?

Paleiskite kiekvieną testą iš naujo šviežiame konteineryje arba apvalkale. Ilgai veikiantis procesas su podėlyje laikomu atsakymu arba užmegztu ryšiu ir toliau nepavyks, kai konfigūracija jau teisinga, ir tas gedimas atrodo lygiai kaip pataisa, kuri neveikė.

Susiję straipsniai