Korjaa Dockerin ja WSL:n liikenne, joka ohittaa isäntäkoneen VPN:n
Kontit ja WSL pitävät omat reittinsä, resolverinsa ja edelleenlähetyspolkunsa, joten toimiva isännän tunneli jättää ne vanhentuneen DNS:n tai ilman ulospääsyä. Korjaa jokainen kerros.
Vietin tänä keväänä suurimman osan eräästä aamusta jahtaen konttia, joka ei pystynyt ratkaisemaan yhtäkään sisäistä nimeä, kun vieressä oleva selain avasi wikin valittamatta. Tunneli oli ylhäällä, selain todisti sen, ja kontti istui toisessa verkon nimiavaruudessa, jossa oli toinen reittitaulu ja toinen resolveri. Seuraava on järjestys, jonka nyt tarkistan: ensin reittitaulut, toiseksi nimien ratkaisu, kolmanneksi edelleenlähetys ja viimeiseksi isännän alusta. Pysähdy ensimmäiseen vaiheeseen, joka jo epäonnistuu, koska sääntöjen lisääminen pinon alempiin kerroksiin piilottaa rajan sen siirtämisen sijaan.
Vertaa kolmea reittitaulua ennen kuin muutat sääntöä
Dockerin oletussillalla olevalla kontilla on yksi oletusreitti docker0-yhdyskäytävään, eikä sillä ole aavistustakaan, että tun0 on olemassa. Isännällä on tun0, VPN:n työntämät reitit ja mahdollisesti käytäntösääntö, joka lähettää koeliikennettä eri liittymän kautta kuin odotat. WSL lisää kolmannen taulun oman virtuaaliadapterinsa taakse. Tulosta kaikki kolme ennen kuin muokkaat yhtäkään niistä.
| Context | Command | What a wrong answer looks like |
|---|---|---|
| Host | ip route get 10.8.0.20 | Leaves a physical interface instead of tun0 |
| Container | ip route | Only 172.17.0.0/16 via 172.17.0.1 |
| Container DNS | cat /etc/resolv.conf | A public resolver, or an upstream the tunnel never pushed |
| WSL | ip addr; ip route | Only the WSL subnet, no Windows VPN adapter |
Jos kontin ainoa reitti on silta eikä tun0 koskaan ilmesty sen nimiavaruuteen, mikään alla oleva ei auta: mikään nimipalvelimen muutos ei luo reittiä, eikä mikään reitti luo tunnelin identiteettiä, jonka vertaiskone hyväksyy. Järjestyksellä on väliä, koska DNS-vika ja reittivika näyttävät kuoren sisältä identtisiltä.
Kohdista resolver tunneliin julkisen sijaan
Isännällä, resolvectl status luettelee DNS-palvelimet linkeittäin, ja readlink -f /etc/resolv.conf kertoo, onko tiedosto systemd:n stub, NetworkManagerin tiedosto vai jotain, minkä toimittajan asiakasohjelma kirjoitti uudelleen. Käyttäjän määrittämässä verkossa olevan kontin sisällä, /etc/resolv.conf nimeää 127.0.0.11, Dockerin sisäänrakennetun resolverin, joka välittää sille upstreamille, jolla daemon konfiguroitiin. Oletussillan kontti sai sen sijaan luontihetkellä kopion isännän tiedostosta ja pitää sen.
Tuo kopio on syy siihen, miksi kontin käynnistymisen jälkeen nouseva tunneli jättää kontin kysymään vanhalta upstreamilta. Joko välitä VPN:n resolveri nimenomaisesti komennolla docker run --dns 10.8.0.53, tai aseta "dns": ["10.8.0.53"] tiedostoon /etc/docker/daemon.json ja käynnistä daemon uudelleen, tai luo kontti uudelleen, kun tunneli on ylhäällä. Uudelleenluonti on yleensä halvempaa kuin sellaisen resolverin virheenkorjaus, joka oli oikein tunti sitten.
Julkiseen resolveriin vaihtaminen on korjaus, joka säilyttää oireen. Se ratkaisee internetin ja epäonnistuu jokaisessa yksityisessä vyöhykkeessä, ja se voi hiljaa ohittaa suodatuksen, jonka vuoksi tunneli on olemassa. Jos yrität kirjoittaa /etc/resolv.conf käynnissä olevan kontin sisällä, odota Device or resource busy: Docker bind-mounttaa tuon tiedoston, joten muokkaus vaatii ensin irrotuksen. --dns asettaminen luontihetkellä on järjestys, joka selviää uudelleenkäynnistyksestä.
Välitä ja käännä, kun kontit lainaavat tunnelia
Oletussillalla isäntä on reititin docker0:n ja tun0:n välillä, eikä se oletuksena tee kumpaakaan työtä. Kytke edelleenlähetys päälle ja käännä lähdeosoite, jotta vertaiskone näkee isännän tunneliosoitteen eikä osoitetta, johon sillä ei ole paluureittiä (korvaa tun0 sillä, miksi asiakasohjelmasi liittymää kutsuu):
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 ACCEPTFORWARD-säännöt ovat tärkeitä isännillä, joiden edelleenlähetyskäytäntö on DROP. Docker lisää omat ketjunsa, joten testaa paketilla lukemisen sijaan iptables -S: kontista, ip route get 10.8.0.20 pitäisi nimetä sillan yhdyskäytävä, ja nc -vz 10.8.0.20 443 pitäisi yhdistää tai aikakatkaista tunnelilla eikä resolverilla.
Ilman MASQUERADEa vertaiskone tarvitsee paluureitin 172.17.0.0/16:een, jota useimmat etäkäyttö-VPN:t eivät työnnä. Tässä myös käytäntöpohjainen asiakasohjelma rikkoo suunnitelman: jos se sieppaa vain tiettyjen UID:iden tai cgroupsien liikennettä, edelleenlähetyssäännöt eivät koskaan näe pakettia tunnelin sisällä, ja korjaus kuuluu asiakasohjelman reitityskäytäntöön eikä iptablesiin. Käsin lisätyt säännöt katoavat uudelleenkäynnistyksessä, joten laita ne systemd-yksikköön tai netfilter-persistent-tauluun ja testaa uudelleen käynnistyksen jälkeen, koska myöhään nouseva tunneli voi pyyhkiä ne pois.
Laita tunneli kontin sisään, kun tavoitteena on eristys
Jos yhden kontin on poistuttava tietyn solmun kautta, aja asiakasohjelma siinä kontissa. Se tarvitsee --cap-add NET_ADMIN ja --device /dev/net/tun, ja silloin se omistaa oman tun0:nsa, reittinsä ja palomuurinsa. Jos muut kontit kulkevat sen kautta, se tarvitsee myös edelleenlähetyksen ja MASQUERADE-säännön kontin sisällä, ja ne vertaiset tarvitsevat nimenomaisen reitin tai välityspalvelimen, joka osoittaa siihen; Dockerin oletussilta ei lisää tuota reittiä puolestasi.
Isännän verkko, docker run --network host, on halpa versio: kontti liittyy isännän nimiavaruuteen ja näkee tunnelin reitit suoraan. Se myös pudottaa verkon eristyksen, houkuttelee porttien törmäyksiä ja on käytännössä Linux-vaihtoehto. Julkaistu portti ei ole korvike. -p 8080:80 tekee palvelun tavoitettavaksi isännältä; se ei tee sisäisiä 10.x-osoitteita tavoitettaviksi kontin sisältä, ja näiden kahden sekoittaminen on se tapa, jolla reititysongelmasta tulee altis tietokantaportti.
Erota nimivika päällekkäisestä aliverkosta
Dockerin oletussilta on 172.17.0.0/16, ja monet yritysten VPN-altaat sijaitsevat 172.16.0.0/12:n sisällä. Kun tunneli omistaa tuon alueen, kontille tarkoitettu liikenne voidaan reitittää sen sijaan tunneliin. Nimet epäonnistuvat, virhe näyttää DNS:ltä, ja ongelma on osoitteissa. Tarkista kohde, ei resolver: ip route get 172.17.0.5 isännällä, vertaa sitten sillan etuliitettä VPN-altaaseen ja docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.
Korjaus on siirtää toinen alueista: joko toinen oletusosoiteallas Dockerille tiedostossa daemon.json tai toinen allas keskittimellä. Toisen nimipalvelimen lisääminen ei tee tässä mitään, eikä tee ssh -b: asiakasohjelman sitominen liittymään muuttaa lähdeosoitetta, ei kohdereittiä, joka osoittaa tunnelin sisään. Nopea työnjako: nimi, joka ratkeaa isännällä mutta ei kontissa, on resolverin ongelma, kun taas suora IP-yhteys, joka jumittuu kontissa mutta toimii isännällä, on reitin tai edelleenlähetysketjun ongelma.
WSL ja Docker Desktop eivät noudata Linuxin sääntöjä
Oletusarvoisessa NAT-tilassaan WSL istuu oman virtuaaliadapterinsa takana eikä saa Windows-VPN:n sisäisiä reittejä tai DNS-liitteitä, minkä vuoksi selain toimii ja ping jakelun sisällä ei. Peilattu verkko, joka on saatavilla uusimmissa Windows 11 -koonnoksissa, jakaa sen sijaan isännän liittymät, mutta se on otettava käyttöön tiedostossa .wslconfig ja jakelu on käynnistettävä uudelleen komennolla wsl --shutdown. Vahvista aktiivinen tila komennolla wslinfo --networking-mode ennen korjauksen soveltamista, koska NAT-vastaus on väärä peilatulle tilalle ja päinvastoin.
Docker Desktop lisää vielä yhden kerroksen. Linux-kontit Windowsissa istuvat NAT-toteutuksen takana, jota on historiallisesti kutsuttu nimellä vpnkit, ja niiden polku eroaa natiivista Linux-isännästä. Windows-kontit liittyvät Hyper-V-virtuaalikytkimen kautta, ja yritysasiakasohjelma voi estää liikenteen tuosta kytkimestä, joten saman koneen Linux-kontit voivat päästä verkkoon, kun taas Windows-konteilla ei ole mitään. Fyysiseen adapteriin sidottu ulkoinen kytkin on tavallinen kiertotie, ja se muuttaa kontin verkkoidentiteettiä tavoilla, joita käytäntö ei ehkä salli.
Ennen kuin kirjoitat toisen säännön, käy neljä kerrosta järjestyksessä läpi ja pysähdy siihen, missä jokin niistä on väärin:
- Kontin sisällä: saavuttaako
ip routeyksityisen etuliitteen, ja nimeääkö/etc/resolv.confupstreamin, joka sen ratkaisee? - Isännällä: onko edelleenlähetys päällä, onko NAT sidottu tunnelin liittymään ja hyväksyykö edelleenlähetysketju sillan tunneliin?
- Vertaiskoneella: tunteeko se paluupolun, vai luotatko NATiin kontin aliverkon piilottamiseksi?
- Alustalla: onko WSL NAT- vai peilatussa tilassa, ja onko kontti Linux vai Windows?
Aja jokainen testi uudelleen tuoreessa kontissa tai kuoressa. Pitkäikäinen prosessi, jolla on välimuistiin tallennettu vastaus tai muodostettu yhteys, epäonnistuu edelleen, kun kokoonpano on oikein, ja se vika näyttää täsmälleen korjaukselta, joka ei toiminut.