Contentor Docker inacessível através da VPN? Compare as sub-redes
O Docker atribui uma sub-rede que a VPN também reclama. Compare os dois planos de endereços e escolha uma reparação que sobreviva a uma nova ligação.
Os contentores respondem na LAN do escritório e deixam de responder no momento em que a VPN da empresa liga. Nada mudou no contentor: sem reinício, sem atualização da imagem, sem edição da configuração. O que mudou foi a tabela de rotas do anfitrião, onde uma rota acrescentada pelo túnel passou a cobrir o endereço que o Docker atribuiu. Antes de editar uma sub-rede ou pedir uma exclusão da VPN, ponha os dois planos de endereços lado a lado, porque as reparações custam quantidades de tempo muito diferentes.
Leia a rota antes de alterar a configuração
Comece pela rede que o Docker realmente construiu. O bloco IPAM é o plano de endereços, e a sub-rede é a linha que importa:
docker network inspect project_default --format '{{json .IPAM.Config}}'
# [{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]Por omissão, o Compose atribui um /16 a cada novo projeto, pelo que dois serviços podem caber num intervalo de 65,536 endereços. É esta largura que torna as sobreposições comuns: um /16 escolhido ao acaso é suficientemente grande para engolir um site a que chega pelo túnel, e ninguém dá por isso até o túnel subir.
Compare depois a seleção de rota para um endereço de contentor, uma vez com o túnel desligado e outra com ele ligado:
ip route get 172.18.0.7
# 172.18.0.7 dev br-3f2a9c src 172.18.0.1Duas formas de resposta levam a sítios diferentes. Se a saída nomear tun0, wg0 ou outro dispositivo de túnel, o prefixo da VPN é pelo menos tão específico como a rota da ponte e o pacote nunca chega ao contentor. Se continuar a nomear a ponte, o anfitrião alcança o contentor e a sua falha está na filtragem, no DNS ou num caminho de retorno que falha apenas num sentido.
Porque é fácil atingir uma colisão de /16
O Docker retira as redes de ponte dos conjuntos privados, e o conjunto predefinido vem do bloco 172.16.0.0/12 em fatias de /16. As redes empresariais usam frequentemente o mesmo espaço privado, e uma VPN de acesso remoto empurra esses prefixos para todos os clientes que se ligam. Quando ambos os lados acreditam ser donos de 172.18.0.0/16, uma tabela de rotas contém duas reivindicações contraditórias.
O comprimento do prefixo decide o vencedor, não a ordem das rotas. Um túnel que empurra 10.0.0.0/8 não vence uma rota de ponte de 172.18.0.0/16, porque /8 é menos específico. Um túnel que empurra 172.18.0.0/16, ou 172.18.5.0/24 dentro dele, vence. É por isso que um portátil funciona na rede do escritório e falha no Wi-Fi do hotel com a mesma VPN: o cliente recebe prefixos diferentes.
A mesma tabela governa o sentido inverso. Um contentor que precisa de um anfitrião interno em 10.8.4.20 envia o pacote para a sua gateway de ponte, e o anfitrião encaminha-o depois para o túnel. Esse caminho normalmente funciona porque o anfitrião faz o NAT, mas falha quando o lado empresarial aceita apenas o endereço VPN do portátil, ou quando a filtragem de caminho inverso descarta a resposta assimétrica.
Compare as três reparações
Há apenas três saídas, e diferem em quem as tem de aprovar e no que se quebra enquanto aplica uma.
| Repair | Who owns it | What it costs |
|---|---|---|
| Renumber the container network | You | Recreates the network, restarts services, invalidates literal addresses in configs and scripts |
| Exclude your range from the tunnel | Network team | Changes what every laptop sees, needs a change request and a client profile update |
| Reach the service through a published port | You and the client | One port only, no direct container-to-container routing across the tunnel |
A renumeragem é a única reparação que consegue concluir sem um pedido formal, e é por isso que a maioria dos textos fica por aí. É barata num projeto jovem e cara assim que uma ferramenta de migração ou uma cadeia de ligação guardada aponta para um endereço literal. Conte essas referências antes de editar o YAML, não depois.
Uma exclusão do túnel é a resposta mais limpa quando a sobreposição está num intervalo que a sua empresa realmente possui. É também a mais lenta e é inútil para um prestador que não pode alterar o perfil do cliente. Uma porta publicada ou um pequeno proxy inverso evitam a discussão: o cliente fala com um endereço que o túnel já encaminha corretamente, e só essa porta tem de estar acessível.
Fixe um intervalo de propósito
Para um único projeto, declare a sub-rede no ficheiro compose para que o projeto deixe de retirar do conjunto partilhado:
networks:
default:
ipam:
config:
- subnet: 10.201.7.0/24Retire esse intervalo de um registo que a sua equipa mantém, não de um exemplo que encontrou a pesquisar. Verifique-o contra os prefixos que a VPN empurra, as LAN domésticas de onde as pessoas trabalham e as outras redes de contentores no mesmo anfitrião. Depois planeie a recriação, porque uma rede existente mantém a sub-rede antiga: docker compose down, e depois up -d. O docker network rm recusa enquanto houver pontos finais ligados, e qualquer entrada ipv4_address no ficheiro tem de ficar dentro do novo intervalo.
Para redes que o Docker cria sozinho, defina um conjunto do daemon em /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.201.0.0/16", "size": 24 },
{ "base": "10.202.0.0/16", "size": 24 }
]
}O conjunto aplica-se às redes criadas depois de o daemon reiniciar, e as pontes que já existem mantêm os seus endereços. Isso é cómodo e fácil de esquecer, por isso trate um projeto antigo como ainda sobreposto até alguém recriar a sua rede. A documentação de redes do motor do Docker descreve as regras de atribuição, caso precise de defender o conjunto numa revisão.
A ponte Linux e o Docker Desktop falham de forma diferente
Num anfitrião Linux, os contentores ficam em pontes no espaço de nomes do anfitrião. Uma única tabela decide tudo, pelo que a comparação acima é a história completa: leia a rota da ponte, leia a rota do túnel e veja qual vence para um endereço de contentor real.
O Docker Desktop coloca os contentores atrás de uma VM com o seu próprio NAT. O anfitrião não tem rota nenhuma para o endereço do contentor; chega ao serviço através de uma porta publicada, normalmente ligada a 127.0.0.1. Um cliente VPN pode, portanto, quebrar o serviço sem qualquer sobreposição de sub-redes: instalando uma rota para o intervalo interno da VM ou bloqueando o tráfego de loopback enquanto o túnel está ligado. Se ip route get no anfitrião indicar que não há rota para o endereço do contentor, isso é o esperado e não o erro.
A consequência prática é onde olha primeiro. Em Linux, comece pela tabela de rotas. No Desktop, comece pela porta publicada e pelas regras de firewall do cliente VPN, e verifique depois se a sub-rede interna da VM colide com algo que o túnel empurra.
Verifique com o túnel ligado
Volte a ligar a VPN duas vezes antes de considerar o problema resolvido, porque um perfil que empurra rotas ao ligar e as retira ao desligar pode parecer correto após a primeira tentativa. Depois execute as verificações que o cliente realmente executa:
ip route geta partir do anfitrião, com o endereço real do contentor.- Um pedido a partir de outro contentor, para provar que o serviço em si está saudável.
curl -sv http://127.0.0.1:8080/a partir do cliente com o túnel ligado, e depoistraceroute -npara o endereço do contentor a partir de uma máquina na LAN.sysctl net.ipv4.conf.all.rp_filterse o pedido chegar ao anfitrião mas a resposta desaparecer.
Por fim, escreva o intervalo escolhido no mesmo registo que usou para o escolher, com a data e o projeto que o possui. A próxima sobreposição virá de um projeto que ninguém documentou, e a única defesa é o intervalo ter sido reservado de propósito em vez de sorteado.