Din tunnelcontainer fungerar, docker compose exec vpn curl -s https://ifconfig.me svarar med leverantörens adress, och ändå lämnar webbläsaren på skrivbordet din ISP. Den klyftan är väntad. En container som kör en VPN-klient håller en nätverksnamnrymd, rutter och ett tunnelgränssnitt, och inget som talar HTTP eller SOCKS; att publicera en port på den kan inte skapa en lyssnare som aldrig fanns.

Var klienten kör avgör designen. En syskoncontainer kan flytta in i tunnelns namnrymd, en skrivbordsapplikation kan inte, och en router behöver rutter framför ett proxyfält. Får du mappningen fel är symptomet detsamma: anslutning nekad, eller trafik som tyst tar den gamla vägen.

Anpassa arrangemanget till platsen där klienten kör

Ställ en fråga innan du redigerar Compose: kan klienten startas inne i VPN-containerns nätverksnamnrymd? Allt som redan körs som en Linux-container kan. En webbläsare, en IDE eller en CLI på skrivbordet kan inte, eftersom dess proxyinställning väntar sig en protokolltjänst, inte en namnrymd.

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

Dessa tre blandas inte. Ett HTTP-proxyfält riktat mot containerns broddress når ingenting om ingen proxy lyssnar där, och en proxylyssnare routar inte en routers trafik. Bestäm först och följ sedan motsvarande avsnitt.

Dela tunnelns namnrymd med en syskoncontainer

Detta är det billigaste alternativet eftersom ingen proxyprotokoll är inblandad. Appen går in i vpn-tjänstens namnrymd och ärver dess gränssnitt, rutter och portutrymme:

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

Tre begränsningar följer av att namnrymden tillhör en annan tjänst. Appen får ingen egen networks-nyckel, eftersom den inte har någon namnrymd att fästa vid. Portpublicering hör till vpn, eftersom det är där namnrymden bor. Och att återskapa vpn förstör namnrymden som appen gick in i, så docker compose up -d --force-recreate vpn lämnar till dig att återskapa app också; en vanlig omstart är inte alltid nog. Compose-tjänstreferensen täcker detta arrangemang och dess begränsning för portpublicering.

Anta inte att resolveraren följer med. /etc/resolv.conf är en fil per container, inte en del av nätverksnamnrymden, så appen kan fortfarande fråga värdens resolver medan dess paket lämnar genom tunneln. Läs den inne i containern och sätt dns: på appen när privata namn måste lösas genom tunneln.

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

Om ruttlistan innehåller tunnelgränssnittet och adressen är leverantörens är appen inne i tunneln. Om gränssnittet saknas startade app innan vpn hade en tunnel, så återskapa app när vpn rapporterar att den är frisk.

Sätt en riktig proxylyssnare i namnrymden för värdklienter

En skrivbordswebbläsare kan inte gå med i en namnrymd, så tunneln måste exponeras som en tjänst. Kör en riktig SOCKS- eller HTTP-proxy inne i vpn-namnrymden och publicera bara dess 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"]

Adressen på värdsidan är den viktiga hälften av den mappningen. 127.0.0.1:1080 till vänster håller lyssnaren utanför ditt LAN; en naken "1080:1080" publicerar en öppen proxy till varje enhet som kan nå din maskin.

Containersidan har sin egen fälla. Publicerade portar levereras till containerns adress på bryggan, inte till dess loopback, så en proxy startad med --socks 127.0.0.1:1080 i namnrymden accepterar anslutningar från syskon och nekar allt som kommer genom den publicerade porten. Bind den till 0.0.0.0 och lita på loopback-bindningen på värdsidan för isolering. Samma regel gäller lyssnaradressen i en proxykonfigurationsfil.

Bestäm sedan var namn löses. I curl löser --socks5 värdnamnet lokalt och --socks5-hostname skickar namnet till proxyn; webbläsare visar samma växel under en annan etikett. Lokal upplösning läcker frågan även när sidan laddas genom tunneln.

Behandla containern som gateway bara med en returrutt

Routrar, VM:ar och telefoner kommer inte att använda ett HTTP-proxyfält, och vissa applikationer ignorerar proxyvariabler helt. Att routa deras trafik betyder lager 3: containern vidarebefordrar paket och översätter adresser på tunnelgränssnittet.

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

Två saker missas lätt. Klienten behöver en rutt som pekar dess standardgateway mot denna värd, och svar måste hitta vägen tillbaka, antingen via en statisk rutt på uppströmsroutern eller via NAT på gränssnittet mot klienterna. Utan en av dem bär tunneln ut begäran och svaret försvinner.

Den har också den bredaste effekten. En värd som fungerar som gateway för hela LAN skickar varje felkonfigurerad klient genom ditt VPN-konto, och ett tunnelfall antingen mörklägger LAN eller faller tillbaka till ISP beroende på kill switch.

Verifiera utgångsvägen och rulla sedan tillbaka i ordning

Kontrollera i denna ordning och stanna vid första felet: utgångsadress inne i namnrymden, utgångsadress från värden genom proxyn, sedan namnlösning för en privat värd.

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

De två första adresserna ska matcha leverantören, och den tredje ska lösas genom proxyn i stället för att misslyckas med ett namnfela. En matchande första rad och en misslyckad andra rad betyder att lyssnaren eller den publicerade porten är fel, inte tunneln.

Tillbakarullning är motsatsen till uppsättningen. Ta bort proxy-miljövariablerna från skalprofilen och från varje systemd-enhet, och ta sedan ner stacken så att den delade namnrymden försvinner med sin ägare. Radera den publicerade porten innan du raderar proxytjänsten, annars behåller värden en loopback-lyssnare som ingen läser.

Innan du går vidare, bekräfta fyra saker: klienten du bryr dig om når faktiskt tunneln, bara namnrymdens ägare publicerar portar, privata namn löses där du hade tänkt, och inget lyssnar på en värdadress som inte är loopback. Var och en av dem misslyckas tyst i produktion, och var och en är en enradskontroll ovan.

Relaterade artiklar