Ihr Tunnelcontainer funktioniert, docker compose exec vpn curl -s https://ifconfig.me antwortet mit der Adresse des Anbieters, und trotzdem verlässt der Browser auf Ihrem Desktop das Netz über Ihren ISP. Diese Lücke ist zu erwarten. Ein Container, der einen VPN-Client ausführt, hält einen Netzwerk-Namespace, Routen und eine Tunnelschnittstelle, und nichts, das HTTP oder SOCKS spricht; einen Port darauf zu veröffentlichen kann keinen Listener erzeugen, der nie da war.

Wo der Client läuft, entscheidet das Design. Ein Nachbarcontainer kann in den Namespace des Tunnels wechseln, eine Desktop-Anwendung nicht, und ein Router braucht Routen statt eines Proxy-Felds. Ordnen Sie es falsch zu, ist das Symptom dasselbe: Verbindung abgelehnt oder Verkehr, der still den alten Weg nimmt.

Passen Sie die Anordnung daran an, wo der Client läuft

Stellen Sie eine Frage, bevor Sie Compose bearbeiten: Lässt sich der Client im Netzwerk-Namespace des VPN-Containers starten? Alles, was bereits als Linux-Container läuft, kann das. Ein Browser, eine IDE oder eine CLI auf Ihrem Schreibtisch kann es nicht, weil ihre Proxy-Einstellung einen Protokolldienst erwartet, keinen Namespace.

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

Diese drei mischen sich nicht. Ein HTTP-Proxyfeld, das auf die Bridge-Adresse des Containers zeigt, erreicht nichts, solange dort kein Proxy lauscht, und ein Proxy-Listener routet keinen Router-Verkehr. Entscheiden Sie zuerst und folgen Sie dann dem passenden Abschnitt.

Teilen Sie den Tunnel-Namespace mit einem Nachbarcontainer

Das ist die günstigste Variante, weil kein Proxy-Protokoll beteiligt ist. Die App tritt dem Namespace des vpn-Dienstes bei und erbt dessen Schnittstellen, Routen und Portraum:

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

Aus der Tatsache, dass der Namespace einem anderen Dienst gehört, folgen drei Einschränkungen. Die App bekommt keinen eigenen networks-Schlüssel, weil sie keinen Namespace hat, an den sie sich hängen könnte. Die Portveröffentlichung gehört zu vpn, denn dort lebt der Namespace. Und das Neuerstellen von vpn zerstört den Namespace, dem die App beigetreten ist, sodass docker compose up -d --force-recreate vpn Sie auch app neu erstellen lässt; ein einfacher Neustart reicht nicht immer. Die Compose-Dienstreferenz behandelt diese Anordnung und ihre Einschränkung bei der Portveröffentlichung.

Gehen Sie nicht davon aus, dass der Resolver mitkommt. /etc/resolv.conf ist eine Datei pro Container und nicht Teil des Netzwerk-Namespace, sodass die App weiterhin den Resolver des Hosts fragen kann, während ihre Pakete durch den Tunnel verlassen. Lesen Sie sie im Container und setzen Sie dns: an der App, wenn private Namen durch den Tunnel aufgelöst werden müssen.

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

Enthält die Routenliste die Tunnelschnittstelle und stammt die Adresse vom Anbieter, ist die App im Tunnel. Fehlt die Schnittstelle, startete app, bevor vpn einen Tunnel hatte, also erstellen Sie app neu, sobald vpn gesund meldet.

Setzen Sie einen echten Proxy-Listener in den Namespace für Host-Clients

Ein Desktop-Browser kann keinem Namespace beitreten, also muss der Tunnel als Dienst exponiert werden. Betreiben Sie einen echten SOCKS- oder HTTP-Proxy im vpn-Namespace und veröffentlichen Sie nur dessen 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"]

Die hostseitige Adresse ist die wichtige Hälfte dieser Zuordnung. 127.0.0.1:1080 links hält den Listener aus Ihrem LAN heraus; ein nacktes "1080:1080" veröffentlicht einen offenen Proxy für jedes Gerät, das Ihre Maschine erreichen kann.

Die Containerseite hat ihre eigene Falle. Veröffentlichte Ports werden an die Adresse des Containers auf der Bridge zugestellt, nicht an sein Loopback, sodass ein mit --socks 127.0.0.1:1080 im Namespace gestarteter Proxy Verbindungen von Nachbarn annimmt und alles ablehnt, was über den veröffentlichten Port ankommt. Binden Sie ihn an 0.0.0.0 und verlassen Sie sich zur Isolation auf die Loopback-Bindung auf der Hostseite. Dieselbe Regel gilt für die Lauschadresse in einer Proxy-Konfigurationsdatei.

Dann entscheiden Sie, wo Namen aufgelöst werden. In curl löst --socks5 den Hostnamen lokal auf, und --socks5-hostname schickt den Namen an den Proxy; Browser bieten denselben Schalter unter anderem Label. Lokale Auflösung verrät die Abfrage, selbst wenn die Seite durch den Tunnel lädt.

Behandeln Sie den Container nur mit einer Rückroute als Gateway

Router, VMs und Telefone werden kein HTTP-Proxyfeld verwenden, und manche Anwendungen ignorieren Proxy-Variablen völlig. Ihren Verkehr zu routen bedeutet Schicht 3: der Container leitet Pakete weiter und übersetzt Adressen auf der Tunnelschnittstelle.

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

Zwei Dinge werden leicht übersehen. Der Client braucht eine Route, die sein Standard-Gateway auf diesen Host zeigt, und Antworten müssen zurückfinden, entweder über eine statische Route auf dem Upstream-Router oder über NAT auf der Schnittstelle zu den Clients. Ohne eines davon trägt der Tunnel die Anfrage hinaus, und die Antwort verschwindet.

Er hat auch den größten Wirkungsradius. Ein Host, der als Gateway für das ganze LAN dient, schickt jeden falsch konfigurierten Client durch Ihr VPN-Konto, und ein Tunnelausfall blockiert entweder das LAN oder fällt je nach Kill Switch auf den ISP zurück.

Prüfen Sie den Ausgangspfad und rollen Sie dann der Reihe nach zurück

Prüfen Sie in dieser Reihenfolge und stoppen Sie beim ersten Fehler: Ausgangsadresse im Namespace, Ausgangsadresse vom Host durch den Proxy, dann Namensauflösung für einen privaten Host.

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

Die ersten beiden Adressen sollten zum Anbieter passen, und die dritte sollte durch den Proxy aufgelöst werden, statt mit einem Namensfehler zu scheitern. Eine passende erste Zeile und eine scheiternde zweite Zeile bedeuten, dass der Listener oder der veröffentlichte Port falsch ist, nicht der Tunnel.

Das Zurückrollen ist die Umkehrung des Aufbaus. Entfernen Sie die Proxy-Umgebungsvariablen aus dem Shell-Profil und aus jeder systemd-Unit, und fahren Sie dann den Stack herunter, damit der gemeinsame Namespace mit seinem Eigentümer verschwindet. Löschen Sie den veröffentlichten Port, bevor Sie den Proxy-Dienst löschen, sonst behält der Host einen Loopback-Listener, den niemand liest.

Bevor Sie weitermachen, bestätigen Sie vier Dinge: Der Client, der Ihnen wichtig ist, erreicht tatsächlich den Tunnel, nur der Eigentümer des Namespace veröffentlicht Ports, private Namen werden dort aufgelöst, wo Sie es beabsichtigt haben, und nichts lauscht auf einer Host-Adresse, die nicht Loopback ist. Jedes davon scheitert in der Produktion still, und jedes ist eine einzeilige Prüfung oben.

Ähnliche Artikel