Браузер на хості показує адресу виходу VPN, контейнер повідомляє про ваше домашнє підключення, і хтось називає це витоком. Нічого не витекло. Тунель хоста — це набір інтерфейсів, маршрутів і правил, що належать мережевому простору імен хоста, а контейнер на мосту — інший простір імен зі своїм маршрутом за замовчуванням. Я сидів на стеку Compose, де клієнт VPN був підключений тижнями, а жоден пакет контейнера так до нього й не потрапив.

Перевірте простір імен до значка в лотку

Простори імен — це весь аргумент. Коли Docker запускає контейнер у мостовій мережі, він створює мережевий простір імен, приєднує всередину нього один кінець пари veth і дає цьому простору адресу та маршрут за замовчуванням. Інтерфейс тунелю, як би він не називався, живе в просторі імен хоста. Трафік усередині контейнера його не бачить, і жодне налаштування клієнта не змінює цього ззовні.

Два підтримувані кроки виводять вас з ізоляції. --network host розміщує контейнер у просторі імен хоста, тож він успадковує маршрути хоста і, отже, тунель. --network container:vpn ділить простір імен іншого контейнера, і саме так більшість схем із sidecar надсилають робоче навантаження через контейнер VPN. Обидва — явний вибір; зазвичай лише один із них той, що мав на увазі той, хто розгортає.

Режим хоста — не безкоштовне оновлення. Контейнер перестає мати власну адресу, публікація портів перестає діяти, а процес із NET_ADMIN там може переписати маршрутизацію хоста. Прийняти це може бути правильним рішенням. Вважати, що він уже ввімкнений, — так люди проводять вечір, налагоджуючи тунель, якого ніколи не було на шляху.

Простежте пакет за межі мосту

Контейнер на мосту надсилає свій пакет на шлюз за замовчуванням, тобто на інтерфейс мосту на хості. Далі хост пересилає пакет і в типовій конфігурації транслює адресу джерела у свою, перш ніж пакет вийде. Саме цей крок трансляції тихо знецінює політику.

Коли пакет досягає кінцевої точки тунелю, він несе адресу хоста. Будь-яке правило, політичний маршрут або рішення про розділений тунель, прив'язане до початкової адреси контейнера, перестає збігатися, і два контейнери, призначені вийти через різні виходи, не можна розрізнити лише за джерелом. Якщо вам потрібна маршрутизація для кожного контейнера, рішення має ухвалюватися до трансляції або всередині простору імен, який ви контролюєте.

Docker сам підтримує ці правила через той бекенд міжмережевого екрана, з яким був налаштований демон. Правила, написані вручну поза підтримуваним хуком для пересиланого трафіку, DOCKER-USER, можуть бути перевпорядковані або відкинуті, коли демон перезапускається або хост оновлюється. Документація Docker щодо фільтрації пакетів показує порядок ланцюжків; практичний сенс у тому, що кожному правилу потрібен власник і шлях перезавантаження, який ви справді перевірили.

Перевіряйте дві системи окремо

Докази з одного простору імен нічого не кажуть про інший. Запит, виконаний усередині контейнера VPN, доводить, що тунель працює для цього контейнера. Це не доказ про хост, і та сама команда, виконана з контейнера з --network host, не є доказом про міст.

# host namespace
ip route get 1.1.1.1
ip -brief addr show

# a fresh container on the same bridge network
docker run --rm --network appnet alpine sh -c 'ip route; cat /etc/resolv.conf; ip -6 route'

Потім повторіть тест усередині справжнього контейнера застосунку, а не схожого образу. Застосунок може нести додатковий маршрут, змінну середовища проксі або інший резолвер, і кожне з цього змінює відповідь. Щоразу перевіряйте розв'язання імен і другу родину адрес: контейнери на мостах, визначених користувачем, розв'язуються через вбудований резолвер Docker за адресою 127.0.0.11, а маршрут за замовчуванням IPv6 на мосту може винести трафік назовні, поки ви зайняті тестуванням тунелю лише для IPv4.

Прочитайте закономірність, перш ніж торкатися облікових даних

Коли хост працює, а контейнер ні, причина майже завжди структурна, і зміна ключів або перевстановлення клієнта марнує півдня. Спершу впорядкуйте симптом, і виправлення зазвичай саме себе називає.

What you seeUsual causeFirst check
Host exits through the VPN, container shows the ISP addressThe container never entered the tunnelDefault route inside the container
Container works until the daemon restarts, then stopsRules outside the supported hook, or a changed backendDaemon's reported firewall backend and DOCKER-USER
Container loses internal hosts after the tunnel comes upVPN prefix overlaps the Docker bridge subnetip route on the host, then docker network inspect
Small replies arrive, large ones hangTunnel MTU or missing MSS clampingLink MTU on both ends of the tunnel
Container falls back to the ISP address when the tunnel dropsNothing enforces the kill switch for forwarded trafficStop the tunnel with a request in flight

Рядок про перекриття заслуговує на окреме речення. Профіль VPN, який проштовхує маршрут для 172.17.0.0/16 або для 10.0.0.0/8, що випадково містить префікс вашого мосту, змушує тунель претендувати на підмережу контейнерів, і відповіді на транслявані адреси підуть не туди або зникнуть. Перенумерація мережі Docker зазвичай чистіша за накопичення винятків.

Зробіть покриття явним, а потім навмисно зламайте його

Дві схеми витримують перевірку. Поділіть простір імен контейнера VPN, і кожне охоплене навантаження використовує один тунель з однією межею перезапуску, що просто, поки одному сервісу не знадобиться інший вихід або власний шлях DNS. Або спрямуйте мережу контейнерів на шлюз VPN на хості: пересилання ввімкнено, префікс мосту спрямовано в тунель, зворотний трафік оброблено явно. Це купує контроль для кожної мережі й коштує вам набору правил, який треба підтримувати під час оновлень демона та клієнта.

Як би там не було, важливий тест — це випадок відмови. Зупиніть тунель, коли запит контейнера в дорозі, і подивіться, куди йде пакет. Якщо він повертається на прямий шлях, покриття ніколи не застосовувалося, бо kill switch клієнта керує власним трафіком клієнта, а не пересиланими пакетами з мосту. Потім перезапустіть демон і хост і підтвердьте результат удруге; саме в цей момент написані вручну правила зникають.

Перш ніж назвати це завершеним, підтвердьте чотири речі: інтерфейс тунелю в тому самому просторі імен, що й трафік, тест контейнера виконувався всередині справжнього контейнера, розв'язання імен і обидві родини адрес перевірено, і тест відмови тунелю досі завершується відмовою в закритому стані. Потім запишіть бекенд міжмережевого екрана, версію клієнта й власника кожного правила. Саме цей запис не дає наступному оновленню залишити недокументовану суміш старих припущень.

Схожі статті