Kontin paketti ei koskaan saavuta tun0 ja kontin lisäominaisuudet eivät muuta sitä. Kävijän ja tunnelin välissä on kaksi reittihakua: yksi kävijän verkkonimiavaruuden sisällä, yksi isännällä sen jälkeen kun kehys laskeutuu sillalle, ja toinen kysyy toista taulukkoa eri täsmäytyssäännöillä. Vertailuasennukseni on Linux-isäntä, jolla on OpenVPN -asiakas, joka pitää 10.8.0.10 osoitteessa tun0, Docker-silta osoitteessa 172.18.0.0/16, ja yksityinen osoite, johon vain tunnelin puoli pääsee. Tavoite pysyy kapeana: tuo kävijän aliverkko poistuu tunnelin kautta, kun isäntä pitää tavallisen uplinkinsa.

VPN-asiakkaan ajaminen kontin sisällä on eri suunnittelu, ei oikotie. Silloin kontti omistaa tunnelinsa ja tunnuksensa, ja työ siirtyy Dockerfileen sen sijaan että jäisi iptablesiin.

Edelleenlähetys valitsee reitin kohteen mukaan, sitten NAT kirjoittaa lähteen uudelleen

Kun net.ipv4.ip_forward=1 on asetettu, edelleenlähetetty paketti reititetään vain kohteen mukaan päätaulukossa. Lähde ei osallistu siihen päätökseen. Vasta reitin valinnan jälkeen ydin suorittaa POSTROUTING, ja juuri siinä Dockerin oma sääntö on: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. Kaikki, mikä lähtee sillalta toiseen liitäntään, käännetään, ja osoite, joksi se muuttuu, riippuu liitännästä, johon reitti sen lähetti.

Tämä sääntö selittää suurimman osan oireista, joista minulta kysytään. Jos päätaulukossa ei ole reittiä yksityiselle kohteelle, paketti lähtee uplinkin kautta isännän fyysisellä osoitteella. Palveluntarjoajasi pudottaa kohteen, tai etäsivusto pudottaa RFC 1918 -lähteen, jota se ei koskaan odottanut, ja kontti aikakatkaistaan, kun tunneli pysyy pystyssä ja terveenä. Kaappaus tun0 -liitännällä ei näytä mitään, ja se on merkki. Jos reitti todella osoittaa tun0 -liitännälle, MASQUERADE kirjoittaa lähteen uudelleen 10.8.0.10 -osoitteeksi ja sama kontti toimii ilman, että kosket mihinkään.

Pyydä hakua, joka nimeää kävijän lähteen. Ilman from ja iif isäntä vastaa oman pistokkeensa puolesta ja raportoi polun, jota edelleenlähetetty paketti ei koskaan ota:

ip route get 192.168.11.222 from 172.18.0.5 iif br-appnet

Jos vastaus nimeää uplinkin, tunneli ei ole kuvassa eikä mikään tunnelin puolen asetus auta.

Siirrä kävijä tunnelin omalle osoitteelle

Pienin toimiva muutos on selkeä lähdeuudelleenkirjoitus ulospäin. Lisää se sen sijaan että liittäisit sen: Docker istuu jo POSTROUTING -kohdan kärjessä, joten -A -parametrilla lisätty sääntö arvioidaan Dockerin säännön jälkeen eikä koskaan laukea.

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

Kaksi ehtoa ratkaisee, pysyykö tämä. Osoitteen on oltava se, joka asiakkaalla todella on, joten kiinnitä se asiakaskohtaisella merkinnällä, joka sisältää ifconfig-push 10.8.0.10 255.255.255.0; sääntö, joka nimeää osoitteen, jota ei koskaan annettu, tuottaa hiljaisia pudotuksia, ei virhettä. Ja kaukokään on hyväksyttävä tämä lähde ja reititettävä vastaukset takaisin, siksi siellä oleva sallittujen lista nimeää tunnelin osoitteen eikä kävijän aliverkkoa. Kun asiakkaan osoite on dynaaminen, -j MASQUERADE ja -o tun0 seuraa mitä liitännällä on, mutta sellaisen etäkäytännön kustannuksella, jota vasten et voi kirjoittaa kiinteää arvoa.

Suodatus on toinen puoli. Docker asettaa FORWARD käytännön dropiksi ja tyhjentää omistamansa ketjut joka daemonin uudelleenkäynnistyksellä, joten laita paikallinen käytäntö DOCKER-USER -ketjuun, joka säilyy:

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

Paluusääntö on tärkeä: sillan avaamalle yhteydelle tun0 -liitännälle saapuvaa vastausta ei hyväksytä vain siksi, että lähtevä suunta hyväksyttiin.

Pidä isäntä suorana lähdesäännöllä

Kun koko sillan pitää käyttää tunnelia mutta minkään muun isännällä ei, ohita NAT ja anna tuolle lähteelle oma taulukko. Kävijän osoitteet pysyvät silloin näkyvinä päästä päähän.

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

Säännöt tarkistetaan ennen päätaulukkoa, ja from täsmää lähteen. Tunnelin omat ulommat paketit kantavat isännän fyysistä lähdettä, joten ne eivät koskaan täsmää from 172.18.0.0/16 kanssa eikä tunneli voi reitittää itseään takaisin itseensä. Juuri tämä itsensä poissulkeminen tekee asennuksesta vakaan.

Paluupolku tarvitsee nyt apua. Kaukokää näkee kääntämättömän 172.18.0.x eikä tiedä, missä se asuu. Palvelimella asiakaskohtainen tiedosto tarvitsee iroute 172.18.0.0 255.255.0.0, jotta palvelimen sisäinen taulukko antaa tämän etuliitteen tälle asiakkaalle, ja työnnetyn route 172.18.0.0 255.255.0.0, jotta muut asiakkaat tietävät sen olevan olemassa.

Kaksi tarkistusta ennen luottamista. Suorita ip route show default: redirect-gateway def1 kanssa taulukossa on 0.0.0.0/1 ja 128.0.0.0/1 kautta tun0, eikä isäntä ole lainkaan suora, koska nuo puolioletukset voittavat korvaamatta default. Myös ip rule add -parametrilla lisätyt säännöt katoavat uudelleenkäynnistyksessä, joten kanna ne up-skriptissä.

Paketin koko on toinen epäonnistuminen, jonka kohtaat

1500 tavun siltaliitäntä antaa tunnelille enemmän kuin se jaksaa kantaa, kun ulommat UDP- ja IP-otsikot lisätään. Pienet vaihdot menevät läpi, joten DNS ratkeaa ja TLS-kättely valmistuu, sitten suurempi lataus jää jumiin ilman virhettä missään. Lähettäjä toistaa samaa segmenttikokoa, koska ICMP-viesti pirstaloinnin tarpeesta, joka käskisi sitä pienentämään, ei koskaan saavu.

Korjaa se liitännässä, MSS:ssä tai molemmissa. Puristus ei tarvitse kävijän yhteistyötä:

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

Puristus kirjoittaa uudelleen vain TCP:n. UDP, QUIC mukaan lukien, tarvitsee sen sijaan madalletun kävijän liitännän, --opt com.docker.network.driver.mtu=1400 -asetuksella verkon luonnissa tai vastaavalla compose-avaimella.

DNS ja tarkistukset, jotka kannattaa toistaa

Nimien ratkaisu seuraa samaa polkua. Dockerin sisäänrakennettu ratkaisija 127.0.0.11 -osoitteessa välittää isännän määritetyille palvelimille, joten kontti perii sen, mitä isäntä käyttää. Kun yksityiseen vyöhykkeeseen vastaa vain tunnelin kautta tavoitettava ratkaisija, osoita kävijä siihen osoitteeseen ja testaa getent hosts -komennolla kontin sisällä eikä isännällä.

  • Suorita epäonnistuva pyyntö kävijän sisällä; isännän puolen testi käyttää toista lähdeosoitetta ja piilottaa ongelman.
  • Lue lisäämiesi NAT- ja suodatinsääntöjen laskurit. Sääntö, jolla ei ole yhtään osumaa, on reititysvirhe, ei käytäntövirhe.
  • Varmista, että SNAT-sääntösi tunneliosoite on se, joka asiakkaalla todella on.
  • Laske kävijän MTU:ta tai purista MSS, ennen kuin syytät palvelinta jumista.

Käy lista läpi ja pysähdy ensimmäiseen epäonnistumiseen. Juuri se kerros on korjattava; kaikki alempana lisätty vain piilottaa rajan, joka rikkoutuu.

Aiheeseen liittyvät artikkelit