Seu contêiner de túnel funciona, docker compose exec vpn curl -s https://ifconfig.me responde com o endereço do provedor, e mesmo assim o navegador na sua área de trabalho sai pelo seu ISP. Essa lacuna é esperada. Um contêiner que executa um cliente VPN mantém um espaço de nomes de rede, rotas e uma interface de túnel, e nada que fale HTTP ou SOCKS; publicar uma porta nele não pode criar um ouvinte que nunca existiu.

Onde o cliente é executado decide o projeto. Um contêiner irmão pode entrar no espaço de nomes do túnel, um aplicativo de desktop não pode, e um roteador precisa de rotas em vez de um campo de proxy. Se errar o mapeamento, o sintoma é o mesmo: conexão recusada, ou tráfego que silenciosamente pega o caminho antigo.

Ajuste a disposição ao local onde o cliente é executado

Faça uma pergunta antes de editar o Compose: o cliente pode ser iniciado dentro do espaço de nomes de rede do contêiner VPN? Qualquer coisa que já execute como contêiner Linux pode. Um navegador, uma IDE ou uma CLI na sua mesa não pode, porque a configuração de proxy espera um serviço de protocolo, não um espaço de nomes.

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

Esses três não se misturam. Um campo de proxy HTTP apontado para o endereço de ponte do contêiner não alcança nada a menos que um proxy escute ali, e um ouvinte de proxy não roteia o tráfego de um roteador. Decida primeiro e depois siga a seção correspondente.

Compartilhe o espaço de nomes do túnel com um contêiner irmão

Esta é a opção mais barata porque nenhum protocolo de proxy está envolvido. O aplicativo entra no espaço de nomes do serviço vpn e herda suas interfaces, rotas e espaço de portas:

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

Três restrições decorrem do fato de o espaço de nomes pertencer a outro serviço. O aplicativo não recebe uma chave networks própria, porque não tem espaço de nomes ao qual se anexar. A publicação de portas pertence ao vpn, já que é ali que o espaço de nomes vive. E recriar o vpn destrói o espaço de nomes ao qual o aplicativo se juntou, então docker compose up -d --force-recreate vpn deixa você recriar o app também; um reinício simples nem sempre basta. A referência de serviços do Compose cobre essa disposição e sua restrição de publicação de portas.

Não presuma que o resolvedor vem junto. /etc/resolv.conf é um arquivo por contêiner, não parte do espaço de nomes de rede, então o aplicativo ainda pode consultar o resolvedor do host enquanto seus pacotes saem pelo túnel. Leia-o de dentro do contêiner e defina dns: no aplicativo quando nomes privados precisarem ser resolvidos pelo túnel.

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

Se a lista de rotas contém a interface do túnel e o endereço é o do provedor, o aplicativo está dentro do túnel. Se a interface falta, o app iniciou antes de o vpn ter um túnel, então recrie o app assim que o vpn relatar saúde.

Coloque um ouvinte de proxy real no espaço de nomes para os clientes do host

Um navegador de desktop não pode entrar em um espaço de nomes, então o túnel precisa ser exposto como serviço. Execute um proxy SOCKS ou HTTP real dentro do espaço de nomes vpn e publique apenas a porta dele:

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

O endereço do lado do host é a metade importante desse mapeamento. 127.0.0.1:1080 à esquerda mantém o ouvinte fora da sua LAN; um "1080:1080" nu publica um proxy aberto a todo dispositivo que consiga alcançar sua máquina.

O lado do contêiner tem sua própria armadilha. As portas publicadas são entregues ao endereço do contêiner na ponte, não ao seu loopback, então um proxy iniciado com --socks 127.0.0.1:1080 dentro do espaço de nomes aceita conexões dos irmãos e recusa tudo o que chega pela porta publicada. Ligue-o a 0.0.0.0 e conte com a ligação de loopback do lado do host para o isolamento. A mesma regra vale para o endereço de escuta em um arquivo de configuração de proxy.

Depois decida onde os nomes são resolvidos. No curl, --socks5 resolve o nome do host localmente e --socks5-hostname envia o nome ao proxy; os navegadores expõem o mesmo comutador sob outro rótulo. A resolução local vaza a consulta mesmo quando a página carrega pelo túnel.

Trate o contêiner como gateway só com uma rota de volta

Roteadores, VMs e telefones não usarão um campo de proxy HTTP, e alguns aplicativos ignoram totalmente as variáveis de proxy. Rotear o tráfego deles significa camada 3: o contêiner encaminha pacotes e traduz endereços na interface do túnel.

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

Duas coisas passam fácil despercebidas. O cliente precisa de uma rota que aponte seu gateway padrão para este host, e as respostas precisam encontrar o caminho de volta, seja por uma rota estática no roteador acima, seja por NAT na interface voltada aos clientes. Sem uma das duas, o túnel leva a requisição para fora e a resposta desaparece.

Também tem o maior raio de impacto. Um host atuando como gateway para toda a LAN envia cada cliente mal configurado pela sua conta VPN, e uma queda do túnel ou apaga a LAN ou volta ao ISP conforme o kill switch.

Verifique o caminho de saída e depois reverta em ordem

Verifique nesta ordem e pare na primeira falha: endereço de saída dentro do espaço de nomes, endereço de saída do host pelo proxy, depois resolução de nomes para um host privado.

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

Os dois primeiros endereços devem coincidir com o provedor, e o terceiro deve resolver pelo proxy em vez de falhar com erro de nome. Uma primeira linha coincidente e uma segunda que falha significam que o ouvinte ou a porta publicada está errado, não o túnel.

Reverter é o contrário da configuração. Remova as variáveis de ambiente de proxy do perfil do shell e de qualquer unidade systemd, e depois derrube a pilha para que o espaço de nomes compartilhado desapareça com seu dono. Apague a porta publicada antes de apagar o serviço de proxy, ou o host mantém um ouvinte de loopback que ninguém lê.

Antes de continuar, confirme quatro coisas: o cliente que lhe importa realmente chega ao túnel, só o dono do espaço de nomes publica portas, nomes privados resolvem onde você pretendia, e nada escuta em um endereço de host que não seja loopback. Cada uma delas falha em silêncio em produção, e cada uma é uma verificação de uma linha acima.

Artigos relacionados