Twój kontener tunelu działa, docker compose exec vpn curl -s https://ifconfig.me odpowiada adresem dostawcy, a mimo to przeglądarka na pulpicie nadal wychodzi przez twojego ISP. Ta luka jest oczekiwana. Kontener uruchamiający klienta VPN trzyma przestrzeń nazw sieci, trasy i interfejs tunelu, a nic, co mówi HTTP lub SOCKS; opublikowanie na nim portu nie może stworzyć nasłuchu, którego nigdy nie było.

To, gdzie działa klient, decyduje o projekcie. Kontener siostrzany może wejść do przestrzeni nazw tunelu, aplikacja desktopowa nie może, a router potrzebuje tras, a nie pola proxy. Jeśli pomylisz mapowanie, objaw jest ten sam: połączenie odrzucone albo ruch, który po cichu idzie starą drogą.

Dopasuj układ do miejsca, w którym działa klient

Zadaj jedno pytanie przed edycją Compose: czy klienta można uruchomić wewnątrz przestrzeni nazw sieci kontenera VPN? Wszystko, co już działa jako kontener Linux, może. Przeglądarka, IDE lub CLI na pulpicie nie może, bo jego ustawienie proxy oczekuje usługi protokołu, a nie przestrzeni nazw.

ClientArrangementWhat must already exist
Another Compose serviceShares the vpn namespace via network_mode: service:vpnA working tunnel and correct start order
Host browser, IDE or CLIProxy listener inside the namespace, published to host loopbackA configured SOCKS or HTTP proxy and its access rules
Router, VM or phoneThe container as a layer-3 gatewayForwarding, NAT and a return route

Te trzy się nie mieszają. Pole proxy HTTP wskazujące adres mostu kontenera niczego nie osiąga, jeśli nie nasłuchuje tam proxy, a nasłuch proxy nie kieruje ruchu routera. Najpierw zdecyduj, potem idź odpowiednią sekcją.

Współdziel przestrzeń nazw tunelu z kontenerem siostrzanym

To najtańsza opcja, bo nie bierze w niej udziału żaden protokół proxy. Aplikacja wchodzi do przestrzeni nazw usługi vpn i dziedziczy jej interfejsy, trasy i przestrzeń portów:

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

Z faktu, że przestrzeń nazw należy do innej usługi, wynikają trzy ograniczenia. Aplikacja nie dostaje własnego klucza networks, bo nie ma przestrzeni nazw, do której mogłaby się przyczepić. Publikacja portów należy do vpn, bo tam żyje przestrzeń nazw. A odtworzenie vpn niszczy przestrzeń nazw, do której dołączyła aplikacja, więc docker compose up -d --force-recreate vpn zostawia ci też odtworzenie app; zwykły restart nie zawsze wystarcza. Dokumentacja usług Compose omawia ten układ i jego ograniczenie publikacji portów.

Nie zakładaj, że resolver przyjdzie razem. /etc/resolv.conf to plik na kontener, a nie część przestrzeni nazw sieci, więc aplikacja nadal może pytać resolver hosta, gdy jej pakiety wychodzą przez tunel. Przeczytaj go wewnątrz kontenera i ustaw dns: na aplikacji, gdy nazwy prywatne mają być rozwiązywane przez tunel.

docker compose exec app ip route
docker compose exec app cat /etc/resolv.conf
docker compose exec app curl -s https://ifconfig.me

Jeśli lista tras zawiera interfejs tunelu, a adres jest dostawcy, aplikacja jest w tunelu. Jeśli interfejsu brakuje, app wystartowała, zanim vpn miał tunel, więc odtwórz app, gdy vpn zgłosi, że jest zdrowy.

Umieść prawdziwy nasłuch proxy w przestrzeni nazw dla klientów hosta

Przeglądarka desktopowa nie może dołączyć do przestrzeni nazw, więc tunel trzeba wystawić jako usługę. Uruchom prawdziwe proxy SOCKS lub HTTP w przestrzeni nazw vpn i opublikuj tylko jego port:

services:
  vpn:
    ports:
      - "127.0.0.1:1080:1080"
  proxy:
    image: your-proxy
    network_mode: service:vpn
    command: ["--socks", "0.0.0.0:1080"]

Adres po stronie hosta to ważna połowa tego mapowania. 127.0.0.1:1080 po lewej trzyma nasłuch poza twoją siecią LAN; gołe "1080:1080" publikuje otwarte proxy każdemu urządzeniu, które może dosięgnąć twojej maszyny.

Strona kontenera ma własną pułapkę. Opublikowane porty są dostarczane na adres kontenera na moście, a nie na jego loopback, więc proxy uruchomione z --socks 127.0.0.1:1080 w przestrzeni nazw przyjmuje połączenia od sióstr i odrzuca wszystko, co przychodzi przez opublikowany port. Zwiąż je z 0.0.0.0 i polegaj na wiązaniu loopback po stronie hosta dla izolacji. Ta sama zasada dotyczy adresu nasłuchu w pliku konfiguracyjnym proxy.

Potem zdecyduj, gdzie rozwiązywane są nazwy. W curl --socks5 rozwiązuje nazwę hosta lokalnie, a --socks5-hostname wysyła nazwę do proxy; przeglądarki pokazują ten sam przełącznik pod inną etykietą. Lokalne rozwiązywanie ujawnia zapytanie nawet wtedy, gdy strona ładuje się przez tunel.

Traktuj kontener jako bramę tylko z trasą powrotną

Routery, VM i telefony nie użyją pola proxy HTTP, a niektóre aplikacje całkowicie ignorują zmienne proxy. Kierowanie ich ruchu oznacza warstwę 3: kontener przekazuje pakiety i tłumaczy adresy na interfejsie tunelu.

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

Dwie rzeczy łatwo przeoczyć. Klient potrzebuje trasy kierującej jego bramę domyślną na tego hosta, a odpowiedzi muszą znaleźć drogę powrotną, albo przez trasę statyczną na routerze nadrzędnym, albo przez NAT na interfejsie zwróconym do klientów. Bez jednego z tych tunel wynosi żądanie na zewnątrz, a odpowiedź znika.

Ma też najszerszy zasięg. Host będący bramą dla całej sieci LAN wysyła każdego błędnie skonfigurowanego klienta przez twoje konto VPN, a spadek tunelu albo zaciemnia sieć LAN, albo wraca do ISP zależnie od kill switch.

Sprawdź ścieżkę wyjścia, a potem wycofaj w kolejności

Sprawdzaj w tej kolejności i zatrzymaj się na pierwszym błędzie: adres wyjścia w przestrzeni nazw, adres wyjścia z hosta przez proxy, potem rozwiązywanie nazw dla prywatnego hosta.

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

Pierwsze dwa adresy powinny zgadzać się z dostawcą, a trzeci powinien rozwiązać się przez proxy zamiast kończyć błędem nazwy. Zgodna pierwsza linia i błędna druga oznaczają, że zły jest nasłuch albo opublikowany port, a nie tunel.

Wycofanie jest odwrotnością konfiguracji. Usuń zmienne środowiskowe proxy z profilu powłoki i z każdej jednostki systemd, a potem zdejmij stos, aby współdzielona przestrzeń nazw zniknęła razem z właścicielem. Usuń opublikowany port przed usunięciem usługi proxy, inaczej host zachowa nasłuch loopback, którego nikt nie czyta.

Przed dalszą pracą potwierdź cztery rzeczy: klient, na którym ci zależy, naprawdę dociera do tunelu, porty publikuje tylko właściciel przestrzeni nazw, nazwy prywatne rozwiązują się tam, gdzie zamierzałeś, i nic nie nasłuchuje na adresie hosta, który nie jest loopback. Każda z nich zawodzi po cichu na produkcji, i każda jest jednolinijkowym sprawdzeniem powyżej.

Powiązane artykuły