Как да насочите трафика на хоста през Docker контейнер с VPN
VPN контейнерът е мрежово именно пространство, а не прокси. Изберете споделено netns, истински SOCKS слушател или маршрутизиран шлюз, после проверете пътя на изхода.
Вашият контейнер за тунела работи, docker compose exec vpn curl -s https://ifconfig.me отговаря с адреса на доставчика, а браузърът на бюрото ви все още излиза през интернет доставчика ви. Тази разлика е очаквана. Контейнер, който пуска 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 изпраща името към прокси; браузърите показват същия превключвател под друг етикет. Локалното решаване издава заявката дори когато страницата се зарежда през тунела.
Третирайте контейнера като шлюз само с обратен маршрут
Рутерите, VM и телефоните няма да използват поле за HTTP прокси, а някои приложения напълно игнорират променливите за прокси. Маршрутизирането на техния трафик означава слой 3: контейнерът препраща пакети и превежда адреси на интерфейса на тунела.
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADEДве неща се пропускат лесно. Клиентът се нуждае от маршрут, който насочва шлюза му по подразбиране към този хост, а отговорите трябва да намерят път назад — през статичен маршрут на горния рутер или през NAT на интерфейса към клиентите. Без едно от двете тунелът изнася заявката навън и отговорът изчезва.
Има и най-широк радиус на действие. Хост, който служи като шлюз за цялата LAN, прекарва всеки погрешно конфигуриран клиент през вашия VPN акаунт, а падането на тунела или блокира LAN, или се връща към доставчика в зависимост от 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. Всяко от тези се проваля тихо в производство и всяко е проверка от един ред по-горе.