Як спрямувати трафік хоста через контейнер Docker VPN
VPN-контейнер — це мережевий простір імен, а не проксі. Виберіть спільний netns, справжній SOCKS-слухач або маршрутизований шлюз, потім перевірте шлях виходу.
Ваш контейнер тунелю працює, docker compose exec vpn curl -s https://ifconfig.me відповідає адресою провайдера, і все ж браузер на робочому столі виходить через вашого ISP. Цей розрив очікуваний. Контейнер, який запускає клієнт VPN, тримає мережевий простір імен, маршрути та інтерфейс тунелю, і нічого, що говорить HTTP або SOCKS; публікація порту на ньому не може створити слухач, якого ніколи не було.
Де працює клієнт, вирішує архітектуру. Сусідній контейнер може перейти в простір імен тунелю, настільний застосунок не може, а маршрутизатору потрібні маршрути, а не поле проксі. Помилитеся зіставленням — симптом той самий: з'єднання відхилено або трафік тихо йде старим шляхом.
Узгодьте схему з місцем, де працює клієнт
Поставте одне питання перед редагуванням Compose: чи можна запустити клієнт усередині мережевого простору імен контейнера VPN? Усе, що вже працює як контейнер Linux, може. Браузер, IDE або CLI на вашому столі не може, бо його налаштування проксі очікує протокольну службу, а не простір імен.
| Client | Arrangement | What must already exist |
|---|---|---|
| Another Compose service | Shares the vpn namespace via network_mode: service:vpn | A working tunnel and correct start order |
| Host browser, IDE or CLI | Proxy listener inside the namespace, published to host loopback | A configured SOCKS or HTTP proxy and its access rules |
| Router, VM or phone | The container as a layer-3 gateway | Forwarding, NAT and a return route |
Ці три не змішуються. Поле проксі HTTP, скероване на адресу моста контейнера, не досягає нічого, якщо там не слухає проксі, а слухач проксі не маршрутизує трафік маршрутизатора. Спершу вирішіть, потім ідіть за відповідним розділом.
Поділіть простір імен тунелю з сусіднім контейнером
Це найдешевший варіант, бо протокол проксі не задіяний. Застосунок входить у простір імен служби vpn і успадковує її інтерфейси, маршрути та простір портів:
services:
vpn:
image: your-vpn-client
cap_add:
- NET_ADMIN
healthcheck:
test: ["CMD-SHELL", "ip link show tun0 >/dev/null"]
interval: 10s
retries: 5
app:
image: your-app
network_mode: service:vpn
depends_on:
vpn:
condition: service_healthyТри обмеження випливають із того, що простір імен належить іншій службі. Застосунок не отримує власний ключ networks, бо йому нема до чого прикріпитися. Публікація портів належить vpn, бо там живе простір імен. А перестворення vpn руйнує простір імен, у який увійшов застосунок, тому docker compose up -d --force-recreate vpn залишає вам перестворити й app; звичайний перезапуск не завжди достатній. Довідник служб Compose описує цю схему та обмеження на публікацію портів.
Не вважайте, що резолвер додається. /etc/resolv.conf — це файл для кожного контейнера, а не частина мережевого простору імен, тому застосунок усе ще може питати резолвер хоста, поки його пакети виходять через тунель. Прочитайте його всередині контейнера і задайте dns: застосунку, коли приватні імена мають розв'язуватися через тунель.
docker compose exec app ip route
docker compose exec app cat /etc/resolv.conf
docker compose exec app curl -s https://ifconfig.meЯкщо список маршрутів містить інтерфейс тунелю, а адреса належить провайдеру, застосунок усередині тунелю. Якщо інтерфейсу немає, app стартував раніше, ніж у vpn з'явився тунель, тому перестворіть app, щойно vpn повідомить про здоров'я.
Розмістіть справжній слухач проксі в просторі імен для клієнтів хоста
Настільний браузер не може увійти в простір імен, тому тунель треба виставити як службу. Запустіть справжній проксі SOCKS або HTTP усередині простору імен vpn і опублікуйте лише його порт:
services:
vpn:
ports:
- "127.0.0.1:1080:1080"
proxy:
image: your-proxy
network_mode: service:vpn
command: ["--socks", "0.0.0.0:1080"]Адреса на боці хоста — важлива половина цього зіставлення. 127.0.0.1:1080 ліворуч тримає слухач поза вашою LAN; голий "1080:1080" публікує відкритий проксі кожному пристрою, який може дістатися вашої машини.
Бік контейнера має власну пастку. Опубліковані порти доставляються на адресу контейнера на мості, а не на його loopback, тому проксі, запущений із --socks 127.0.0.1:1080 усередині простору імен, приймає з'єднання від сусідів і відкидає все, що надходить через опублікований порт. Прив'яжіть його до 0.0.0.0 і покладайтеся на прив'язку loopback на боці хоста для ізоляції. Те саме правило діє для адреси прослуховування у файлі конфігурації проксі.
Далі вирішіть, де розв'язуються імена. У curl --socks5 розв'язує ім'я хоста локально, а --socks5-hostname надсилає ім'я проксі; браузери показують той самий перемикач під іншою міткою. Локальне розв'язання викриває запит навіть тоді, коли сторінка завантажується через тунель.
Вважайте контейнер шлюзом лише за наявності зворотного маршруту
Маршрутизатори, віртуальні машини й телефони не використовуватимуть поле проксі HTTP, а деякі застосунки повністю ігнорують змінні проксі. Маршрутизація їхнього трафіку означає рівень 3: контейнер пересилає пакети й транслює адреси на інтерфейсі тунелю.
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADEДві речі легко проґавити. Клієнту потрібен маршрут, який спрямовує його шлюз за замовчуванням на цей хост, а відповіді мають знайти шлях назад — або через статичний маршрут на вищому маршрутизаторі, або через NAT на інтерфейсі, оберненому до клієнтів. Без одного з цього тунель виносить запит назовні, і відповідь зникає.
Він має й найширший радіус дії. Хост, що слугує шлюзом для всієї LAN, відправляє кожного неправильно налаштованого клієнта через ваш обліковий запис VPN, а падіння тунелю або гасить LAN, або повертається до ISP залежно від kill switch.
Перевірте шлях виходу, потім відкочуйте за порядком
Перевіряйте в цьому порядку й зупиніться на першій помилці: адреса виходу всередині простору імен, адреса виходу з хоста через проксі, потім розв'язання імен для приватного хоста.
docker compose exec vpn curl -s https://ifconfig.me
curl -s -x socks5h://127.0.0.1:1080 https://ifconfig.me
curl -s -x socks5h://127.0.0.1:1080 http://internal.example/healthПерші дві адреси мають збігатися з провайдером, а третя має розв'язатися через проксі, а не впасти з помилкою імені. Збіг першого рядка й збій другого означають, що неправильні слухач або опублікований порт, а не тунель.
Відкат — це зворотне до налаштування. Приберіть змінні середовища проксі з профілю оболонки та з будь-якого модуля systemd, потім опустіть стек, щоб спільний простір імен зник разом зі своїм власником. Видаліть опублікований порт перед видаленням служби проксі, інакше хост збереже слухач loopback, який ніхто не читає.
Перш ніж рухатися далі, підтвердьте чотири речі: важливий для вас клієнт справді досягає тунелю, порти публікує лише власник простору імен, приватні імена розв'язуються там, де ви задумали, і ніщо не слухає на адресі хоста, яка не є loopback. Кожне з цього тихо відмовляє в продакшені, і кожне — перевірка в один рядок вище.