Пакувањето на контејнерот никогаш не стигнува до tun0 и дополнителните можности на контејнерот тоа нема да го променат. Помеѓу гостинот и тунелот стојат две пребарувања на рута: едно во мрежниот именски простор на гостинот, едно на домаќинот откако рамката ќе слета на мостот, а второто прашува друга табела со други правила за совпаѓање. Мојот референтен распоред е Linux домаќин што извршува OpenVPN клиент што држи 10.8.0.10 на tun0, Docker мост на 172.18.0.0/16, и приватна адреса до која може да стигне само страната на тунелот. Целта останува тесна: таа гостинска подмрежа излегува низ тунелот додека домаќинот го задржува обичниот uplink.

Извршувањето на VPN клиентот внатре во контејнерот е друг дизајн, а не кратенка. Тогаш контејнерот го поседува својот тунел и своите акредитиви, а работата се преместува во Dockerfile наместо да остане во iptables.

Препраќањето избира рута по одредиште, потоа NAT го препишува изворот

Кога е поставено net.ipv4.ip_forward=1 , препратеното пакување се рутира само по одредиште, во главната табела. Изворот не игра никаква улога во таа одлука. Само откако ќе се избере рутата, јадрото извршува POSTROUTING , и токму таму стои сопственото правило на Docker: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE . Сѐ што го напушта мостот кон друг интерфејс се преведува, а адресата во која се претвора зависи од интерфејсот на кој рутата го испратила.

Ова правило ги објаснува повеќето симптоми за кои ме прашуваат. Ако главната табела нема рута за приватното одредиште, пакувањето излегува низ uplink со физичката адреса на домаќинот. Вашиот провајдер го отфрла одредиштето, или далечната страница отфрла извор RFC 1918 што никогаш не го очекувала, и контејнерот истекува на време додека тунелот останува подигнат и здрав. Снимање на tun0 не покажува ништо, и тоа е знакот. Ако рутата навистина покажува кон tun0 , MASQUERADE го препишува изворот на 10.8.0.10 , и истиот контејнер работи без вие да допрете нешто.

Побарајте го пребарувањето што го именува изворот на гостинот. Без from и iif домаќинот одговара за својот сокет и пријавува пат што препратеното пакување никогаш нема да го земе:

ip route get 192.168.11.222 from 172.18.0.5 iif br-appnet

Ако тој одговор го именува uplink, тунелот не е во сликата и никаква поставка од страната на тунелот нема да помогне.

Преместете го гостинот на сопствената адреса на тунелот

Најмалата промена што работи е експлицитно препишување на изворот на излезот. Вметнете го, наместо да го додавате: Docker веќе седи на челото на POSTROUTING , затоа правило додадено со -A се оценува зад Docker-овото и никогаш не се активира.

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

Два услови одлучуваат дали тоа ќе се задржи. Адресата мора да биде онаа што клиентот навистина ја држи, затоа прикачете ја со запис за секој клиент што содржи ifconfig-push 10.8.0.10 255.255.255.0 ; правило што именува адреса што никогаш не била доделена создава тивки отфрлања, а не грешка. И далечната страна мора да го прифати тој извор и да ги врати одговорите, затоа таму списокот на дозволени ја именува адресата на тунелот, а не гостинската подмрежа. Кога адресата на клиентот е динамична, -j MASQUERADE со -o tun0 го следи она што го има интерфејсот, по цена на далечна политика против која не можете да напишете фиксна вредност.

Филтрирањето е другата половина. Docker ја поставува политиката FORWARD на drop и ги брише ланците што ги поседува при секое рестартирање на демонот, затоа ставете локална политика во DOCKER-USER , која преживува:

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

Правилото за враќање е важно: одговор што пристигнува на tun0 за врска отворена преку мостот не се прифаќа само затоа што излезната насока била.

Држете го домаќинот директно со правило за извор

Кога целиот мост треба да го користи тунелот, но ништо друго на домаќинот не треба, прескокнете NAT и дајте му на тој извор сопствена табела. Гостинските адреси тогаш остануваат видливи од крај до крај.

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

Правилата се консултираат пред главната табела, и from се совпаѓа со изворот. Сопствените надворешни пакувања на тунелот ја носат физичката адреса на домаќинот, затоа никогаш не се совпаѓаат со from 172.18.0.0/16 , и тунелот не може да се рутира себеси назад во себе. Токму тоа самоисклучување го прави распоредот стабилен.

Патот за враќање сега бара помош. Далечната страна гледа непреведена 172.18.0.x и нема причина да знае каде живее. На серверот, датотека за секој клиент бара iroute 172.18.0.0 255.255.0.0 , за да може внатрешната табела на серверот да му го даде тој префикс на овој клиент, и туркана route 172.18.0.0 255.255.0.0 , за да знаат другите клиенти дека постои.

Две проверки пред да му верувате. Извршете ip route show default : со redirect-gateway def1 табелата држи 0.0.0.0/1 и 128.0.0.0/1 преку tun0 , и домаќинот воопшто не е директен, бидејќи тие полустандардни рути победуваат без да го заменат default . Правилата додадени со ip rule add исто така исчезнуваат при рестартирање, затоа носете ги во up скрипта.

Големината на пакувањето е вториот дефект што ќе го сретнете

Мост интерфејс од 1500 бајти му дава на тунелот повеќе отколку што може да понесе кога ќе се додадат надворешните UDP и IP заглавија. Малите размени поминуваат, па DNS се решава и TLS ракувањето завршува, потоа поголемо качување виси без никаква грешка каде и да е. Испраќачот ѐ уште го повторува истиот сегмент, бидејќи ICMP пораката за потреба од фрагментација, која би му кажала да се намали, никогаш не пристигнува.

Поправете го на интерфејсот, на MSS или на двете. Стискањето не бара соработка од гостинот:

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

Стискањето го препишува само TCP. UDP, вклучително QUIC, наместо тоа бара понизок гостински интерфејс, со --opt com.docker.network.driver.mtu=1400 при создавање на мрежата или соодветниот compose клуч.

DNS и проверките што вреди да се повторат

Решавањето на имиња го следи истиот пат. Вградениот resolver на Docker на 127.0.0.11 препраќа до конфигурираните сервери на домаќинот, па контејнерот го наследува она што го користи домаќинот. Кога приватната зона ја одговара само resolver достапен преку тунелот, насочете го гостинот кон таа адреса, и тестирајте со getent hosts внатре во контејнерот, а не на домаќинот.

  • Извршете го барањето што не успева внатре во гостинот; тестот од страната на домаќинот користи друга изворна адреса и го крие проблемот.
  • Читајте ги бројачите на NAT и филтер правилата што ги додадовте. Правило без ниту едно совпаѓање е грешка во рутирањето, не во политиката.
  • Потврдете дека адресата на тунелот во вашето SNAT правило е онаа што клиентот навистина ја држи.
  • Намалете го MTU на гостинот или стиснете го MSS пред да го обвините серверот за застој.

Поминете низ списокот и застанете на првиот дефект. Токму тој слој треба да се поправи; сѐ што се додава подолу само ја крие границата што се крши.

Поврзани статии