Я правёў большую частку раніцы гэтай вясной, ганяючыся за кантэйнерам, які не мог развязаць ніводнага ўнутранага імя, пакуль браўзер побач адкрываў вікі без скаргаў. Тунель быў узняты, браўзер гэта даказваў, а кантэйнер заставаўся ў іншай прасторы імёнаў сеткі з іншай табліцай маршрутаў і іншым рэзалверам. Далей ідзе парадак, які я цяпер правяраю: спачатку табліцы маршрутаў, потым развязанне імёнаў, потым перасылка, і апошняй — платформа хаста. Спыніцеся на першым кроку, які ўжо не спраўджваецца, бо даданне правілаў ніжэй у стэку хавае мяжу замест таго, каб яе перасунуць.

Параўнайце тры табліцы маршрутаў, перш чым змяніць правіла

Кантэйнер на стандартным масточку Docker мае адзін маршрут па змаўчанні, які паказвае на шлюз docker0, і не мае ўяўлення, што tun0 існуе. Хаст мае tun0, маршруты, якія праштурхнуў VPN, і магчыма правіла палітыкі, якое адпраўляе пробны трафік праз іншы інтэрфейс, чым вы чакаеце. WSL дадае трэцюю табліцу за сваім уласным віртуальным адаптарам. Выведзіце ўсе тры, перш чым рэдагаваць любую з іх.

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

Калі адзіны маршрут кантэйнера — гэта мост, і tun0 ніколі не з'яўляецца ў яго прасторы імёнаў, нішто ніжэй не дапаможа: ніякая змена сервера імёнаў не стварае маршрут, і ніякі маршрут не стварае ідэнтычнасць тунеля, якую прыме партнёр. Парадак важны, бо збой DNS і збой маршруту выглядаюць аднолькава знутры абалонкі.

Накіруйце рэзалвер на тунель, а не на публічны

На хасце, resolvectl status пералічвае DNS-серверы па кожным злучэнні, а readlink -f /etc/resolv.conf паказвае, ці файл з'яўляецца заглушкай systemd, файлам NetworkManager або нечым, што перапісаў кліент пастаўшчыка. Унутры кантэйнера ў сетцы, вызначанай карыстальнікам, /etc/resolv.conf называе 127.0.0.11, убудаваны рэзалвер Docker, які перасылае на той верхні паток, з якім быў наладжаны дэман. А кантэйнер на стандартным масточку атрымаў пры стварэнні копію файла хаста і трымае яе.

Гэтая копія — прычына таго, што тунель, які ўзнімаецца пасля запуску кантэйнера, пакідае кантэйнер запытваць стары верхні паток. Альбо перадайце рэзалвер VPN явна праз docker run --dns 10.8.0.53, альбо задайце "dns": ["10.8.0.53"] у /etc/docker/daemon.json і перазапусціце дэман, альбо перастварыце кантэйнер, калі тунель ужо ўзняты. Перастварэнне звычайна таннейшае за адладку рэзалвера, які быў правільным гадзіну таму.

Замена на публічны рэзалвер — гэта рамонт, які захоўвае сімптом. Ён развязвае інтэрнэт і збаіць у кожнай прыватнай зоне, і можа ціха абыйсці фільтрацыю, дзеля якой існуе тунель. Калі вы паспрабуеце запісаць унутры /etc/resolv.conf кантэйнера, што працуе, чакайце Device or resource busy: Docker прымацоўвае гэты файл праз bind, таму для правакі спачатку патрэбна адмацаванне. Заданне --dns пры стварэнні — гэта паслядоўнасць, якая перажывае перазапуск.

Перасылайце і транслюйце, калі кантэйнеры пазычаюць тунель

Са стандартным масточкам хаст з'яўляецца маршрутызатарам паміж docker0 і tun0, і па змаўчанні ён не выконвае ніводнай з гэтых задач. Уключыце перасылку і транслюйце адрас крыніцы, каб партнёр бачыў адрас тунеля хаста, а не той, на які ў яго няма зваротнага маршруту (замяніце tun0 на тое, як ваш кліент называе інтэрфейс):

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 маюць значэнне на хастах, чыя палітыка перасылкі — DROP. Docker устаўляе свае ланцугі, таму правярайце пакетам, а не чытаннем iptables -S: з кантэйнера, ip route get 10.8.0.20 павінен называць шлюз моста, а nc -vz 10.8.0.20 443 павінен злучыцца або скончыцца тайм-аўтам на тунелі, а не на рэзалверы.

Без MASQUERADE партнёру патрэбен зваротны маршрут да 172.17.0.0/16, які большасць VPN аддаленага доступу не праштурхне. Тут жа ламаецца план у кліента на аснове палітык: калі ён захоплівае трафік толькі ад пэўных UID або cgroups, вашы правілы перасылкі ніколі не ўбачаць пакет унутры тунеля, і выпраўленне належыць палітыцы маршрутызацыі кліента, а не iptables. Правілы, дададзеныя ўручную, знікаюць пры перазапуску, таму пакладзіце іх у модуль systemd або табліцу netfilter-persistent і паўторна праверце пасля перазапуску, бо тунель, які ўзнімаецца позна, можа іх скінуць.

Змясціце тунель унутры кантэйнера, калі мэта — ізаляцыя

Калі адзін кантэйнер павінен выходзіць праз пэўны вузел, запусціце кліент у гэтым кантэйнеры. Яму патрэбны --cap-add NET_ADMIN і --device /dev/net/tun, і тады ён валодае сваім tun0, маршрутамі і файрволам. Калі іншыя кантэйнеры праходзяць праз яго, яму таксама патрэбны перасылка і правіла MASQUERADE унутры кантэйнера, а тым партнёрам патрэбен явны маршрут або проксі, які паказвае на яго; стандартны мост Docker не дадае гэты маршрут за вас.

Сетка хаста, docker run --network host, — гэта танны варыянт: кантэйнер далучаецца да прасторы імёнаў хаста і бачыць маршруты тунеля напрамую. Ён таксама скідае ізаляцыю сеткі, запрашае канфлікты портаў і на практыцы з'яўляецца варыянтам Linux. Апублікаваны порт не замяняе гэтага. -p 8080:80 робіць сэрвіс дасяжным з хаста; ён не робіць унутраныя адрасы 10.x дасяжнымі знутры кантэйнера, і змешванне гэтых двух — гэта шлях ад праблемы маршрутызацыі да адкрытага порта базы даных.

Адрозніце збой імёнаў ад супадаючай падсеткі

Стандартны мост Docker — 172.17.0.0/16, і шмат карпаратыўных пулаў VPN знаходзяцца ўнутры 172.16.0.0/12. Як толькі тунель валодае гэтым дыяпазонам, трафік, прызначаны кантэйнеру, можа быць накіраваны ў тунель замест гэтага. Імёны збаяць, памылка выглядае як DNS, а праблема ў адрасах. Правярайце пункт прызначэння, а не рэзалвер: ip route get 172.17.0.5 на хасце, затым параўнайце прэфікс моста з пулам VPN і з docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.

Рамантаванне — перасунуць адзін з дыяпазонаў: альбо іншы пул адрасоў па змаўчанні для Docker у daemon.json або іншы пул на канцэнтратары. Даданне яшчэ аднаго сервера імёнаў тут нічога не дае, і таксама не дае ssh -b: прывязванне кліента да інтэрфейса змяняе адрас крыніцы, а не маршрут прызначэння, які паказвае ў тунель. Хуткі падзел працы: імя, якое развязваецца на хасце, але не ў кантэйнеры, — гэта праблема рэзалвера, тады як прамое IP-злучэнне, якое вісіць у кантэйнеры, але працуе на хасце, — гэта праблема маршруту або ланцуга перасылкі.

WSL і Docker Desktop не прытрымліваюцца правілаў Linux

У сваім стандартным рэжыме NAT WSL стаіць за ўласным віртуальным адаптарам і не атрымлівае ўнутраныя маршруты або DNS-суфіксы VPN Windows, таму браўзер працуе, а ping унутры дыстрыбутыва не працуе. Люстраваная сетка, даступная ў нядаўніх зборках Windows 11, замест гэтага абагульвае інтэрфейсы хаста, але яе трэба ўключыць у .wslconfig і перазапусціць дыстрыбутыў праз wsl --shutdown. Пацвердзіце актыўны рэжым праз wslinfo --networking-mode перад ужываннем выпраўлення, бо адказ для NAT няправільны для люстраванага рэжыму і наадварот.

Docker Desktop дадае яшчэ адзін узровень. Linux-кантэйнеры на Windows стаяць за рэалізацыяй NAT, гістарычна вядомай як vpnkit, і іх шлях адрозніваецца ад натыўнага хаста Linux. Windows-кантэйнеры далучаюцца праз віртуальны камутатар Hyper-V, і карпаратыўны кліент можа блакіраваць трафік з гэтага камутатара, таму Linux-кантэйнеры на той жа машыне могуць дасягаць сеткі, пакуль Windows-кантэйнеры не маюць нічога. Знешні камутатар, прывязаны да фізічнага адаптара, — звычайны абыход, і ён змяняе сеткавую ідэнтычнасць кантэйнера так, як палітыка можа не дазваляць.

Перш чым пісаць яшчэ адно правіла, пройдзіце чатыры ўзроўні па парадку і спыніцеся там, дзе адзін з іх няправільны:

  • Унутры кантэйнера: ці ip route дасягае прыватнага прэфікса, і ці /etc/resolv.conf называе верхні паток, які яго развязвае?
  • На хасце: ці ўключана перасылка, ці прывязаны NAT да інтэрфейса тунеля, і ці прымае ланцуг перасылкі пераход ад моста да тунеля?
  • На партнёры: ці ведае ён зваротны шлях, ці вы спадзяецеся на NAT, каб схаваць падсетку кантэйнера?
  • На платформе: WSL у рэжыме NAT ці люстраваным, і кантэйнер — Linux ці Windows?

Паўтарыце кожны тэст у свежым кантэйнеры або абалонцы. Доўгажывучы працэс з кэшаваным адказам або ўсталяваным злучэннем будзе збаіць і пасля таго, як канфігурацыя стане правільнай, і гэты збой выглядае як выпраўленне, якое не спрацавала.

Падобныя артыкулы