El paquet del contenidor no arriba mai a tun0, i les capacitats addicionals del contenidor no ho canviaran. Entre el convidat i el túnel hi ha dues consultes de ruta: una dins de l'espai de noms de xarxa del convidat, una a l'amfitrió després que la trama arribi al pont, i la segona consulta una taula diferent amb regles de coincidència diferents. La meva disposició de referència és un amfitrió Linux amb un OpenVPN client que té 10.8.0.10 a tun0, un pont Docker a 172.18.0.0/16, i una adreça privada que només pot assolir el costat del túnel. L'objectiu continua sent estret: aquesta subxarxa convidada surt pel túnel mentre l'amfitrió conserva el seu uplink habitual.

Executar el client VPN dins del contenidor és un disseny diferent, no una drecera. Llavors el contenidor posseeix el seu túnel i les seves credencials, i la feina es trasllada a un Dockerfile en lloc de quedar-se a iptables.

L'encaminament tria una ruta per destinació, i després NAT reescriu l'origen

Amb net.ipv4.ip_forward=1 configurat, un paquet encaminat es dirigeix només per destinació, a la taula principal. L'origen no hi té cap paper. Només després d'escollir la ruta el nucli executa POSTROUTING, i és aquí on hi ha la regla pròpia de Docker: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. Tot el que surt del pont cap a una altra interfície es tradueix, i l'adreça en què es converteix depèn de la interfície a la qual la ruta l'ha enviat.

Aquesta regla explica la majoria dels símptomes que em pregunten. Si la taula principal no té cap ruta per a la destinació privada, el paquet surt per l'uplink amb l'adreça física de l'amfitrió. El vostre proveïdor descarta la destinació, o el lloc remot descarta una font RFC 1918 que no esperava, i el contenidor fa temps d'espera mentre el túnel continua aixecat i sa. Una captura a tun0 no mostra res, i aquesta és la pista. Si la ruta sí que apunta a tun0, MASQUERADE reescriu l'origen a 10.8.0.10 i el mateix contenidor funciona sense que toqueu res.

Demaneu la consulta que nomena l'origen del convidat. Sense from i iif l'amfitrió respon pel seu propi sòcol i informa d'un camí que el paquet encaminat mai no prendrà:

ip route get 192.168.11.222 from 172.18.0.5 iif br-appnet

Si aquesta resposta nomena l'uplink, el túnel no hi és i cap ajustament del costat del túnel no ajudarà.

Porteu el convidat a l'adreça pròpia del túnel

El canvi més petit que funciona és una reescriptura explícita de l'origen a la sortida. Inseriu-la en lloc d'afegir-la: Docker ja seu al capdavant de POSTROUTING, així que una regla afegida amb -A s'avalua darrere de la de Docker i no s'activa mai.

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

Dues condicions decideixen si això es manté. L'adreça ha de ser una que el client realment tingui, així que fixeu-la amb una entrada per client que contingui ifconfig-push 10.8.0.10 255.255.255.0; una regla que nomena una adreça que mai es va assignar produeix descarts silenciosos, no un error. I el costat remot ha d'acceptar aquest origen i encaminar les respostes de tornada, per això una llista d'autoritzats allà nomena l'adreça del túnel i no la subxarxa convidada. Quan l'adreça del client és dinàmica, -j MASQUERADE amb -o tun0 segueix el que tingui la interfície, a costa d'una política remota contra la qual no podeu escriure un valor fix.

El filtratge és l'altra meitat. Docker posa la política FORWARD a drop i buida les cadenes que posseeix a cada reinici del dimoni, així que poseu la política local a DOCKER-USER, que sobreviu:

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

La regla de tornada importa: una resposta que arriba a tun0 per a una connexió oberta pel pont no s'accepta només perquè la direcció de sortida ho fos.

Mantingueu l'amfitrió directe amb una regla d'origen

Quan tot el pont ha d'usar el túnel però res més de l'amfitrió no, salteu-vos NAT i doneu a aquest origen la seva pròpia taula. Les adreces convidades queden llavors visibles d'extrem a extrem.

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

Les regles es consulten abans de la taula principal, i from coincideix amb l'origen. Els paquets externs propis del túnel porten l'origen físic de l'amfitrió, així que mai no coincideixen amb from 172.18.0.0/16 i el túnel no pot encaminar-se a si mateix de tornada. Aquesta autoexclusió és el que fa estable la disposició.

El camí de tornada ara necessita ajuda. El costat remot veu una 172.18.0.x no traduïda i no té cap raó per saber on viu. Al servidor, un fitxer per client necessita iroute 172.18.0.0 255.255.0.0 perquè la taula interna del servidor lliuri aquest prefix a aquest client, i un route 172.18.0.0 255.255.0.0 empès perquè els altres clients sàpiguen que existeix.

Dues comprovacions abans de confiar-hi. Executeu ip route show default: amb redirect-gateway def1 la taula té 0.0.0.0/1 i 128.0.0.0/1 via tun0, i l'amfitrió no és directe gens, perquè aquests mig-predeterminats guanyen sense substituir default. Les regles afegides amb ip rule add també desapareixen en reiniciar, així que porteu-les en un script up.

La mida del paquet és la segona fallada que trobareu

Una interfície de pont a 1500 bytes dóna al túnel més del que pot portar un cop afegits les capçaleres UDP i IP externes. Els intercanvis petits passen, així que DNS resol i l'apretada de mans TLS es completa, i llavors una pujada més gran es penja sense cap error enlloc. L'emissor segueix reintentant la mateixa mida de segment perquè el missatge ICMP de necessitat de fragmentació, que li diria que es redueixi, no arriba mai.

Arregleu-ho a la interfície, a l'MSS o tots dos. El clavament no necessita la col·laboració del convidat:

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

El clavament només reescriu TCP. UDP, QUIC inclòs, necessita en canvi una interfície convidada més baixa, amb --opt com.docker.network.driver.mtu=1400 en crear la xarxa o la clau de compose corresponent.

DNS i les comprovacions que val la pena repetir

La resolució de noms segueix el mateix camí. El resolutor incrustat de Docker a 127.0.0.11 reenvia als servidors configurats de l'amfitrió, així que un contenidor hereta el que faci servir l'amfitrió. Quan la zona privada només la respon un resolutor abastable pel túnel, adreceu el convidat a aquesta adreça, i proveu amb getent hosts dins del contenidor i no a l'amfitrió.

  • Executeu la petició que falla dins del convidat; una prova al costat de l'amfitrió fa servir una altra adreça d'origen i amaga el problema.
  • Llegiu els comptadors de les regles NAT i de filtratge que heu afegit. Una regla amb zero coincidències és un error d'encaminament, no de política.
  • Confirmeu que l'adreça del túnel a la vostra regla SNAT és una que el client realment té.
  • Baixeu l'MTU del convidat o claveu l'MSS abans de culpar el servidor d'una aturada.

Recorreu aquesta llista i atureu-vos a la primera fallada. Aquesta capa és la que cal arreglar; qualsevol cosa afegida més avall només amaga la frontera que es trenca.

Articles relacionats