Хост продовжує відповідати по SSH після підняття VPN з повним тунелем, а сервіс, опублікований через Docker на тій самій машині, замовкає. Зі слухачем усе гаразд: контейнер працює, зіставлення портів існує, і пакет надходить. Контейнер здоровий, і запит справді до нього доходить. Різниця в тому, звідки починається відповідь, коли Linux обирає маршрут, і ця різниця вирішує, яка з двох схем нижче вам потрібна.

Простежте кортеж крізь NAT призначення

Візьміть хост на 192.0.2.10, що публікує TCP 2222 на контейнер на 172.30.240.5:22. Запит від 198.51.100.25 перетворюється до того, як дістатися контейнера, і контейнер відповідає як він сам.

WAN request:      198.51.100.25 -> 192.0.2.10:2222
after DNAT:       198.51.100.25 -> 172.30.240.5:22
container reply:  172.30.240.5:22 -> 198.51.100.25

На момент рішення хоста про маршрут відповідь усе ще несе адресу джерела контейнера. Зворотне перетворення відновлює опублікований кортеж пізніше, і саме тому пакет виглядає неправильно рівно тоді, коли обирається маршрут.

Чому правило лише для хоста це проґавить

Правило, що вибирає таблицю WAN лише для пакетів від 192.0.2.10, охоплює власні демони хоста і повністю проґавить цю переслану відповідь. Адреса джерела у відповіді — 172.30.240.5, а не хост.

Надмірна корекція — спрямовувати все з контейнера через WAN, що також звільняє незв'язані вихідні з'єднання контейнера від VPN. Це інша політика, ніж запитана, і зазвичай когось дивує пізніше.

Зіставлення після DNAT — інший hook

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

Ця плутанина з hook-ами породжує правила, що виглядають правильними і ніколи не збігаються. Перевірте лічильники на самому правилі, перш ніж переписувати політику. Правило без жодного влучання каже вам, що воно обчислюється там, куди пакет ніколи не доходить.

Схема A: нести рішення як стан з'єднання

Мітка з'єднання може пронести рішення про вхід крізь перетворення, але її треба повернути пакету до того пошуку маршруту, що має значення. Етапи такі:

  1. Зарезервуйте поле мітки й пріоритет правила, які не конфліктують з Docker, VPN або іншим власником політики.
  2. Визначте лише передбачуване нове з'єднання WAN у точці до DNAT, де доступне відстеження з'єднань.
  3. Запишіть рішення в мітку з'єднання, не змушуючи сам початковий запит іти до таблиці WAN.
  4. У напрямку відповіді відновіть на пакеті лише свої біти мітки перед маршрутизацією.
  5. Виберіть повну таблицю WAN достатньо рано, щоб правило VPN не могло перемогти першим.

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

Схема B: завершувати на хості

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

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

Перевіряйте зміну, а не намір

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

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

Схожі статті