Контейнер, який не досягає нічого за корпоративним VPN, зазвичай живе в іншому мережевому просторі імен, ніж тунель. Маршрути, DNS-сервери та позначки міжмережевого екрана клієнта хоста належать стеку хоста; у контейнера в мостовій мережі свій стек. Цей єдиний факт пояснює більшість звернень, які надходять до команди платформи, і він же причина того, що «VPN зламаний» і «Docker обходить VPN» можуть бути неправильними одночасно. За одним симптомом ховаються дві зовсім різні конфігурації, тому визначте, яка у вас, перш ніж змінювати налаштування.

З'ясуйте, кому належить простір імен тунелю

У Docker Desktop для macOS або Windows контейнери працюють усередині віртуальної машини Linux. VPN-клієнт, встановлений на робочому столі, створює тунель в операційній системі робочого стола, а віртуальна машина його ніколи не бачить. Жодна конфігурація контейнера це не виправить, бо інтерфейсу, який знадобився б контейнеру, в його мережевому просторі імен просто немає.

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

docker info --format '{{.OperatingSystem}}'
docker run --rm --net=host alpine ip route | head -5
docker run --rm alpine ip route | head -5

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

Мережа хоста — це перевірка, а не проєкт

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

docker run --rm --net=host curl -sS -o /dev/null -w '%{http_code}\n' https://api.internal/health

Залиште це як діагностику. Мережа хоста відмовляється від публікації портів і більшої частини ізоляції, яку ви хотіли отримати від контейнера, а в Docker Desktop «host» означає віртуальну машину, а не робочий стіл, тож успіх або невдача там можуть ввести в оману, якщо ви забудете, до якого хоста зверталися.

Пристрій tun і привілеї, що з ним ідуть

VPN-клієнту всередині контейнера потрібен тунельний пристрій і дозвіл налаштувати його. Звичне скорочення — --privileged, яке надає доступ до всіх пристроїв і послаблює політику безпеки в усьому контейнері. Це набагато більше, ніж вимагає це завдання, і це важко обґрунтувати на рев'ю.

sudo modprobe tun
docker run --rm -it --device /dev/net/tun --cap-add NET_ADMIN myvpn-client

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

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

NAT мосту змінює джерело, яке бачить VPN

За стандартного мосту вихідний трафік контейнера транслюється в адресу хоста. Тому за VPN корпоративна сторона бачить адресу тунелю хоста, а не контейнера, правила доступу за джерелом поводяться так, ніби запит зробив хост, і два контейнери стають невідрізнюваними для VPN-сервера.

macvlan або ipvlan, навпаки, дає контейнеру адресу LAN, що допомагає виявленню в LAN і робить контейнер рівноправним у сегменті. Він також повністю обходить тунель хоста, тож такий контейнер не досягне префіксів лише-VPN, якщо VPN недоступний і з цієї LAN.

Обидві форми законні; вони відповідають різним вимогам. Якщо вимога — «цей контейнер має досягати сервісу лише-VPN», ви вибираєте між маршрутизацією через хост і завершенням тунелю всередині контейнера. Запишіть цей вибір до розгортання, а не після першого невдалого запиту. Щоб ви не обрали, зафіксуйте адресу джерела, яку побачить віддалена сторона, бо саме її покажуть її правила доступу та її журнали. Дизайн, який не може назвати цю адресу, не завершений.

DNS іде за демоном, а не за тунелем

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

Задайте резолвер явно для сервісу або спрямуйте його на внутрішній резолвер, доступний через тунель. Контейнер, який розв'язує приватне ім'я в публічну адресу, зазвичай використовує публічний резолвер, а не страждає від зламаного маршруту, і виправлення належить до файлу compose або конфігурації демона.

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

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

Схожі статті