Conteneur Docker inaccessible sur le VPN ? Comparez les sous-réseaux
Docker attribue un sous-réseau que le VPN revendique aussi. Comparez les deux plans d'adressage, puis choisissez une réparation qui survit à une reconnexion.
Les conteneurs répondent sur le LAN du bureau, puis cessent de répondre dès que le VPN de l'entreprise se connecte. Rien n'a changé dans le conteneur : ni redémarrage, ni mise à jour d'image, ni modification de configuration. Ce qui a changé, c'est la table de routage de l'hôte, où une route ajoutée par le tunnel couvre désormais l'adresse attribuée par Docker. Avant de modifier un sous-réseau ou de demander une exclusion VPN, mettez les deux plans d'adressage côte à côte, car les réparations coûtent des durées très différentes.
Lisez la route avant de changer la configuration
Commencez par le réseau que Docker a réellement construit. Le bloc IPAM est le plan d'adressage, et le sous-réseau est la ligne qui compte :
docker network inspect project_default --format '{{json .IPAM.Config}}'
# [{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]Compose alloue par défaut un /16 à chaque nouveau projet, si bien que deux services peuvent tenir dans une plage de 65,536 adresses. C'est précisément cette largeur qui rend les chevauchements fréquents : un /16 choisi au hasard est assez grand pour avaler un site que vous joignez par le tunnel, et personne ne s'en aperçoit avant que le tunnel ne monte.
Comparez ensuite la sélection de route pour une adresse de conteneur, une fois tunnel baissé et une fois tunnel monté :
ip route get 172.18.0.7
# 172.18.0.7 dev br-3f2a9c src 172.18.0.1Deux formes de réponse mènent à des endroits différents. Si la sortie nomme tun0, wg0 ou un autre périphérique de tunnel, un préfixe VPN est au moins aussi spécifique que la route du pont et le paquet n'atteint jamais le conteneur. Si elle nomme encore le pont, l'hôte peut atteindre le conteneur, et votre panne est le filtrage, le DNS ou un chemin de retour qui échoue dans un seul sens.
Pourquoi une collision de /16 est facile à rencontrer
Docker tire les réseaux de pont des pools privés, et le pool par défaut sort du bloc 172.16.0.0/12 en tranches de /16. Les réseaux d'entreprise utilisent souvent le même espace privé, et un VPN d'accès distant pousse ces préfixes vers chaque client qui se connecte. Quand les deux côtés croient posséder 172.18.0.0/16, une table de routage porte deux affirmations contradictoires.
La longueur du préfixe décide du gagnant, pas l'ordre d'apparition des routes. Un tunnel qui pousse 10.0.0.0/8 ne bat pas une route de pont 172.18.0.0/16, parce que /8 est moins spécifique. Un tunnel qui pousse 172.18.0.0/16, ou 172.18.5.0/24 à l'intérieur, gagne. C'est pourquoi un portable fonctionne sur le réseau du bureau et échoue sur le Wi-Fi d'hôtel avec le même VPN : le client reçoit des préfixes différents.
La même table gouverne l'autre sens. Un conteneur qui a besoin d'un hôte interne à 10.8.4.20 envoie le paquet à sa passerelle de pont, et l'hôte le route ensuite dans le tunnel. Ce chemin fonctionne normalement parce que l'hôte fait le NAT, mais il échoue quand le côté entreprise n'accepte que l'adresse VPN du portable, ou quand le filtrage de chemin inverse rejette la réponse asymétrique.
Comparez les trois réparations
Il n'y a que trois issues, et elles diffèrent par qui doit les approuver et par ce qui casse pendant que vous en appliquez une.
| 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 |
La renumérotation est la seule réparation que vous pouvez terminer sans ticket, et c'est pourquoi la plupart des articles s'arrêtent là. Elle est bon marché sur un projet jeune et coûteuse dès qu'un outil de migration ou une chaîne de connexion enregistrée pointe vers une adresse littérale. Comptez ces références avant de modifier le YAML, pas après.
Une exclusion du tunnel est la réponse la plus propre quand le chevauchement se trouve dans une plage que votre entreprise possède réellement. C'est aussi la plus lente, et elle est inutile pour un prestataire qui ne peut pas changer le profil du client. Un port publié ou un petit proxy inverse évitent la discussion : le client parle à une adresse que le tunnel route déjà correctement, et seul ce port doit être joignable.
Épinglez une plage exprès
Pour un seul projet, déclarez le sous-réseau dans le fichier compose pour que le projet cesse de puiser dans le pool partagé :
networks:
default:
ipam:
config:
- subnet: 10.201.7.0/24Prenez cette plage dans un registre que votre équipe tient, pas dans un exemple trouvé en cherchant. Vérifiez-la contre les préfixes que pousse le VPN, les LAN domestiques depuis lesquels les gens travaillent et les autres réseaux de conteneurs du même hôte. Puis planifiez la recréation, car un réseau existant garde son ancien sous-réseau : docker compose down, puis up -d. docker network rm refuse tant que des points de terminaison sont attachés, et toute ipv4_address entrée du fichier doit tomber dans la nouvelle plage.
Pour les réseaux que Docker crée de lui-même, définissez un pool de démon dans /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.201.0.0/16", "size": 24 },
{ "base": "10.202.0.0/16", "size": 24 }
]
}Le pool s'applique aux réseaux créés après le redémarrage du démon, et les ponts qui existent déjà gardent leurs adresses. C'est pratique et facile à oublier, alors traitez un ancien projet comme toujours en chevauchement jusqu'à ce que quelqu'un recrée son réseau. La documentation Docker sur le réseau du moteur décrit les règles d'allocation si vous devez défendre le pool en revue.
Le pont Linux et Docker Desktop échouent différemment
Sur un hôte Linux, les conteneurs reposent sur des ponts dans l'espace de noms de l'hôte. Une seule table décide de tout, donc la comparaison ci-dessus est toute l'histoire : lisez la route du pont, lisez la route du tunnel, et voyez laquelle gagne pour une adresse de conteneur réelle.
Docker Desktop place les conteneurs derrière une VM dotée de son propre NAT. L'hôte n'a aucune route vers l'adresse du conteneur ; il atteint le service par un port publié, généralement lié à 127.0.0.1. Un client VPN peut donc casser le service sans aucun chevauchement de sous-réseaux, soit en installant une route pour la plage interne de la VM, soit en bloquant le trafic loopback pendant que le tunnel est connecté. Si ip route get sur l'hôte signale qu'il n'y a pas de route pour l'adresse du conteneur, c'est attendu et non le défaut.
La conséquence pratique est où vous regardez d'abord. Sous Linux, commencez par la table de routage. Sur Desktop, commencez par le port publié et les règles de pare-feu du client VPN, puis vérifiez si le sous-réseau interne de la VM entre en collision avec ce que pousse le tunnel.
Vérifiez tunnel monté
Reconnectez le VPN deux fois avant de le déclarer réparé, car un profil qui pousse des routes à la connexion et les retire à la déconnexion peut sembler correct après le premier essai. Exécutez ensuite les contrôles que le client exécute réellement :
ip route getdepuis l'hôte, avec la vraie adresse du conteneur.- Une requête depuis un autre conteneur, pour prouver que le service lui-même est sain.
curl -sv http://127.0.0.1:8080/depuis le client tunnel monté, puistraceroute -nvers l'adresse du conteneur depuis une machine du LAN.sysctl net.ipv4.conf.all.rp_filtersi la requête atteint l'hôte mais que la réponse disparaît.
Enfin, inscrivez la plage choisie dans le même registre que celui utilisé pour la choisir, avec la date et le projet qui la possède. Le prochain chevauchement viendra d'un projet que personne n'a documenté, et la seule défense est que la plage a été réservée exprès plutôt que tirée au hasard.