Уявіть хост Linux, який завершує VPN і водночас запускає контейнер у мережі macvlan. LAN — це 192.168.60.0/24, контейнер має 192.168.60.210, а VPN-клієнти приходять з 10.95.0.0/24. Клієнт досягає сервісу, відповідь ніколи не повертається, і кожне правило брандмауера на хості виглядає правильним. Цей симптом створюють два окремі обмеження, і виправлення одного залишає інше на місці.

Два обмеження, один симптом

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

Друге обмеження — зворотний шлях. Шлюз за замовчуванням у контейнера зазвичай LAN-маршрутизатор, тому відповідь, адресована VPN-клієнту, виходить через цей маршрутизатор. Якщо в маршрутизатора немає маршруту назад до пулу VPN, відповідь відкидається десь посередині.

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

Побудуйте shim на боці хоста, вузько

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

ip link add macvlan-vpn link eth0 type macvlan mode bridge
ip address add 192.168.60.250/32 dev macvlan-vpn
ip link set macvlan-vpn up
ip route add 192.168.60.210/32 dev macvlan-vpn src 192.168.60.250

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

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

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

Вирішіть, як зберігається джерело

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

Щоб це працювало, мережева конфігурація контейнера для цього шляху має надсилати трафік для 10.95.0.0/24 на 192.168.60.250 як на свій шлюз. Це підтримуване налаштування маршруту або шлюзу всередині власної конфігурації контейнера, а не хитрість на боці хоста. Підтвердьте його зсередини контейнера, а не припускайте, що налаштування набуло чинності. Шлюз за замовчуванням, який усе ще вказує на LAN-маршрутизатор, продовжуватиме надсилати відповіді в ту саму діру.

Якщо затверджений дизайн натомість приховує джерела VPN, обмежте переклад вузько: реальний пул VPN, shim як вихідний інтерфейс і конкретний контейнер та сервіс.

iptables -t nat -A POSTROUTING -s 10.95.0.0/24 -o macvlan-vpn -j SNAT --to-source 192.168.60.250

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

Перевіряйте з боку клієнта, а не хоста

Запит із самого хоста, прив'язаний до адреси shim, доводить, що shim може дістатися до контейнера. Він не доводить, що контейнер може повернути трафік збереженому джерелу 10.95.0.x, бо адреса джерела в цьому тесті — власна адреса shim.

curl -sS --interface 192.168.60.250 -o /dev/null -w '%{http_code}\n' http://192.168.60.210:8080/health
sudo tcpdump -ni macvlan-vpn host 192.168.60.210

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

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

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

Схожі статті