Les bizarreries réseau de Docker Desktop : accès LAN, ports et correctifs DNS qui fonctionnent

Cet article vous a aidé ?

Vous lancez docker run -p 8080:80, naviguez vers localhost:8080 et ça marche. Vous communiquez l’URL à un collègue sur le même Wi‑Fi, et… rien.
Ou votre conteneur peut faire un curl vers Internet mais ne parvient pas au NAS sur votre LAN. Ou le DNS se met à déconner à chaque connexion VPN.

Le réseau de Docker Desktop n’est pas « cassé ». Il ne correspond simplement pas au modèle réseau Linux que vous pensez utiliser.
C’est une VM, un NAT, une couche d’adaptations spécifiques à la plateforme, et quelques noms spéciaux qui existent principalement pour nous sauver la mise.

Modèle mental : pourquoi Docker Desktop est différent

Sur Linux, Docker branche généralement les conteneurs sur un réseau bridge de l’hôte, utilise iptables/nftables pour NATer le trafic sortant,
et ajoute des règles DNAT pour les ports publiés. Votre hôte est l’hôte. Le noyau qui exécute les conteneurs est le même que celui qui exécute votre shell.

Docker Desktop sur macOS et Windows est différent par conception. Il exécute une petite VM Linux (ou un environnement Linux via WSL2),
et les conteneurs vivent derrière une frontière de virtualisation. Cette frontière explique pourquoi le « network host » se comporte étrangement,
pourquoi l’accès LAN n’est pas symétrique, et pourquoi la publication de ports peut sembler destinée uniquement à localhost.

Pensez en couches :

  • Votre système d’exploitation physique (macOS/Windows) : possède votre interface Wi‑Fi/Ethernet, votre client VPN et votre pare‑feu.
  • La VM Docker / WSL2 : a sa propre NIC virtuelle, sa propre table de routage, ses propres iptables et son comportement DNS.
  • Réseaux de conteneurs : ponts à l’intérieur de cet environnement Linux ; vos conteneurs touchent rarement directement le LAN physique.
  • Couche de redirection des ports : Docker Desktop transfère les ports de l’OS hôte vers la VM puis vers le conteneur.

Donc quand quelqu’un dit « le conteneur ne peut pas atteindre le LAN », votre première réponse devrait être : « Quelle couche ne parvient pas à atteindre quelle couche ? »

Faits intéressants et bref historique (ce qui explique les douleurs actuelles)

  1. Le modèle réseau original de Docker supposait Linux. Docker a popularisé le schéma « bridge + NAT + iptables » parce que Linux le rendait simple et portable.
  2. macOS ne peut pas exécuter des conteneurs Linux nativement. Docker Desktop sur macOS a toujours reposé sur une VM Linux parce que les conteneurs nécessitent des fonctionnalités du noyau Linux (namespaces, cgroups).
  3. Windows a connu deux ères. D’abord Docker Desktop basé sur Hyper‑V ; ensuite WSL2 est devenu la voie par défaut pour de meilleurs comportements de système de fichiers et de ressources, avec des particularités réseau différentes.
  4. host.docker.internal existe parce que « l’hôte » est ambigu. Dans un conteneur, « localhost » est le conteneur ; Docker Desktop avait besoin d’un nom stable pour « l’OS hôte ».
  5. Les ports publiés ne sont pas que des règles iptables sur Desktop. Sur Linux, oui ; sur Desktop, ils sont souvent implémentés par un proxy/forwardeur en espace utilisateur à travers la frontière VM.
  6. Les clients VPN aiment réécrire votre DNS et vos routes. Ils installent souvent un nouveau serveur DNS, bloquent le split DNS ou ajoutent une interface virtuelle avec une priorité supérieure au Wi‑Fi.
  7. Les solutions de sécurité d’entreprise injectent fréquemment un proxy local. Cela peut casser le DNS des conteneurs, MITM le TLS ou détourner silencieusement le trafic vers une infrastructure d’inspection.
  8. ICMP vous ment dans les réseaux virtuels. « Impossible de pinguer » ne signifie pas forcément « impossible de se connecter », surtout quand des pare‑feu bloquent ICMP mais autorisent TCP.

Blague #1 : Le réseau Docker Desktop est comme un organigramme—il y a toujours une couche de plus que vous pensez, et ce n’est jamais la couche responsable.

Playbook de diagnostic rapide (vérifier premier/deuxième/troisième)

La façon la plus rapide de gagner est d’arrêter de deviner. Diagnostiquez dans cet ordre, car cela isole les couches avec un minimum d’effort.

1) S’agit‑il d’un problème de publication de port ou d’un problème de routage/DNS ?

  • Si localhost:PORT fonctionne sur votre machine mais que des clients LAN ne peuvent pas y accéder, vous avez probablement un problème de pare‑feu hôte / adresse de binding / filtrage de routes par le VPN.
  • Si les conteneurs ne peuvent pas résoudre les noms ou atteindre aucun hôte externe, commencez par le DNS et le routage sortant depuis l’intérieur du conteneur/VM.

2) Identifiez où le paquet meurt (OS hôte → VM → conteneur)

  • Depuis l’OS hôte : pouvez‑vous joindre la cible LAN ?
  • Depuis l’intérieur d’un conteneur : pouvez‑vous joindre la même cible LAN par IP ?
  • Depuis l’intérieur d’un conteneur : pouvez‑vous résoudre le nom ?

3) Vérifiez l’adresse réelle d’écoute/binding et le forwarder

  • Le service écoute‑t‑il sur 0.0.0.0 à l’intérieur du conteneur, ou seulement sur 127.0.0.1 ?
  • Docker publie‑t‑il le port sur toutes les interfaces ou seulement sur localhost ?
  • Le pare‑feu hôte bloque‑t‑il les entrées depuis le LAN ?

4) Vérifiez tôt le comportement VPN et override DNS

  • Si le problème apparaît/disparaît avec le VPN, arrêtez de le traiter comme un bug Docker. C’est une politique, des routes, du DNS ou de l’inspection.

5) Ce n’est qu’ensuite que vous touchez aux réglages Docker Desktop

  • Changer les serveurs DNS ou les plages réseau peut aider, mais faites‑le avec des preuves. Sinon vous allez juste créer un nouveau mystère.

Tâches pratiques : commandes, sorties et décisions (12+)

Voici les vérifications que j’exécute vraiment. Chacune inclut : commande, sortie d’exemple, ce que cela signifie et la décision suivante.
Les commandes sont montrées avec un prompt générique ; adaptez les noms d’interface et les IP à votre environnement.

Task 1: Confirm which Docker context you’re using

cr0x@server:~$ docker context ls
NAME                DESCRIPTION                               DOCKER ENDPOINT
default *           Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
desktop-linux       Docker Desktop                            unix:///Users/me/.docker/run/docker.sock

Sens : Si vous pensez parler à Desktop mais que vous êtes connecté à un daemon distant (ou inversement), toutes les hypothèses réseau seront fausses.
Décision : Si le contexte étoilé n’est pas celui attendu, changez‑le : docker context use desktop-linux.

Task 2: Inspect a container’s IP and network attachment

cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Ports}}'
NAMES          PORTS
web            0.0.0.0:8080->80/tcp
db             5432/tcp
cr0x@server:~$ docker inspect -f '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}} {{.Gateway}}{{end}}' web
/web 172.17.0.2 172.17.0.1

Sens : Le conteneur vit sur un bridge interne (ici 172.17.0.0/16). Ce n’est pas votre LAN.
Décision : Si vous essayez d’atteindre 172.17.0.2 depuis un autre ordinateur sur le Wi‑Fi, stoppez. Publiez un port ou utilisez un autre schéma réseau.

Task 3: Check what address your service is actually listening on

cr0x@server:~$ docker exec -it web sh -lc "ss -lntp | head -n 5"
State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
LISTEN 0      4096   0.0.0.0:80         0.0.0.0:*     users:(("nginx",pid=1,fd=6))

Sens : Écouter sur 0.0.0.0 est bon ; cela accepte le trafic depuis le réseau des conteneurs.
Si vous voyez 127.0.0.1:80, la publication de port « fonctionnera » de façon confuse ou échouera complètement.
Décision : Si le service est lié à localhost, corrigez la config de l’application : lier sur 0.0.0.0.

Task 4: Verify published port bindings on the Docker side

cr0x@server:~$ docker port web
80/tcp -> 0.0.0.0:8080

Sens : Docker pense avoir publié sur toutes les interfaces.
Décision : Si cela affiche 127.0.0.1:8080, les clients LAN ne pourront pas y accéder. Relancez avec -p 0.0.0.0:8080:80 (ou corrigez votre fichier compose).

Task 5: Confirm the host OS is listening on the expected port

cr0x@server:~$ ss -lntp | grep ':8080'
LISTEN 0      4096      0.0.0.0:8080     0.0.0.0:*    users:(("com.docker.backend",pid=2314,fd=123))

Sens : Sur Desktop, vous voyez souvent le processus backend de Docker écoutant, pas le PID du conteneur. C’est normal.
Décision : Si rien n’écoute, votre publication n’a pas appliqué, ou un autre processus a pris le port.

Task 6: Test from the host OS to confirm the forward path works

cr0x@server:~$ curl -sS -D- http://127.0.0.1:8080/ | head
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Sat, 03 Jan 2026 09:12:52 GMT
Content-Type: text/html

Sens : Le transfert de port host→container fonctionne localement.
Décision : Si les clients LAN ne peuvent pas se connecter, concentrez‑vous sur le pare‑feu/VPN/binding‑sur‑localhost, pas sur l’application du conteneur.

Task 7: Test from a LAN peer (simulate with another namespace/host if you can)

cr0x@server:~$ nc -vz 192.168.1.50 8080
Connection to 192.168.1.50 8080 port [tcp/http-alt] succeeded!

Sens : Le port est joignable depuis le LAN.
Décision : Si cela échoue par « timed out » vous avez probablement des problèmes de pare‑feu/routage. Si « refused », quelque chose écoute mais n’accepte pas sur cette interface ou le forwarder n’est pas lié correctement.

Task 8: Check container DNS configuration

cr0x@server:~$ docker exec -it web sh -lc "cat /etc/resolv.conf"
nameserver 192.168.65.5
search localdomain
options ndots:0

Sens : Docker Desktop injecte souvent une IP de stub resolver (exemple : 192.168.65.5) à l’intérieur du réseau de la VM.
Décision : Si ce serveur de noms est inaccessible ou dysfonctionnel (commun avec les VPN), surchargez le DNS au niveau du daemon/compose.

Task 9: Test DNS resolution inside the container (don’t guess)

cr0x@server:~$ docker exec -it web sh -lc "getent hosts example.com | head -n 2"
2606:2800:220:1:248:1893:25c8:1946 example.com
93.184.216.34 example.com

Sens : Le DNS fonctionne suffisamment pour résoudre AAAA et A.
Décision : Si cela bloque ou ne retourne rien, vous avez un problème de chemin DNS. Étape suivante : essayez de résoudre en utilisant un serveur spécifique (si vous avez les outils) ou surchargez les resolvers.

Task 10: Test direct IP connectivity to a LAN resource from inside the container

cr0x@server:~$ docker exec -it web sh -lc "nc -vz 192.168.1.10 445"
192.168.1.10 (192.168.1.10:445) open

Sens : Le routage container → VM → OS hôte → LAN fonctionne pour cette destination.
Décision : Si l’IP fonctionne mais le nom échoue, c’est du DNS. Si ni l’un ni l’autre ne fonctionnent, c’est du routage/VPN/politique.

Task 11: Check the container’s default route (basic but decisive)

cr0x@server:~$ docker exec -it web sh -lc "ip route"
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link  src 172.17.0.2

Sens : Le conteneur route vers la gateway du bridge. La gateway décide ensuite comment atteindre votre LAN/internet.
Décision : Si la route par défaut manque ou est incorrecte, vous avez construit une configuration réseau personnalisée ; revenez en arrière et testez avec un réseau bridge standard.

Task 12: Check whether you’re colliding with a corporate/VPN subnet

cr0x@server:~$ ip route | head -n 12
default via 192.168.1.1 dev wlan0
10.0.0.0/8 via 10.8.0.1 dev tun0
172.16.0.0/12 via 10.8.0.1 dev tun0
192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.50

Sens : Si vos réseaux Docker utilisent 172.16.0.0/12 et que votre VPN route aussi 172.16.0.0/12, vous avez créé un routage ambigu.
Desktop est particulièrement sensible au chevauchement parce qu’il effectue déjà du NAT.
Décision : Changez les plages internes de Docker pour éviter le chevauchement avec les routes d’entreprise.

Task 13: Inspect Docker networks and their subnets

cr0x@server:~$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a1b2c3d4e5f6   bridge    bridge    local
f1e2d3c4b5a6   host      host      local
123456789abc   none      null      local
cr0x@server:~$ docker network inspect bridge --format '{{(index .IPAM.Config 0).Subnet}}'
172.17.0.0/16

Sens : Vous savez maintenant quelles sous‑réseaux Docker consomme.
Décision : Si cela chevauche les routes VPN ou votre LAN, déplacez‑les.

Task 14: Validate that the container can reach the host OS via Docker Desktop’s special name

cr0x@server:~$ docker exec -it web sh -lc "getent hosts host.docker.internal"
192.168.65.2    host.docker.internal

Sens : Le mappage spécial existe et pointe vers l’endpoint côté hôte que Docker fournit.
Décision : Si ce nom ne se résout pas, vous êtes sur une configuration ancienne, un mode réseau personnalisé ou quelque chose a modifié le DNS dans le conteneur. N’utilisez une IP explicite qu’en dernier recours.

Schémas d’accès LAN : ce qui fonctionne, ce qui trompe

Il y a trois demandes courantes :

  • LAN → votre service conteneurisé (un collègue veut atteindre votre serveur de dev).
  • Conteneur → ressource LAN (le conteneur a besoin d’atteindre un NAS, une imprimante, une API interne, Kerberos, peu importe).
  • Conteneur → OS hôte (le conteneur appelle un service qui tourne sur votre portable).

Schéma A : LAN → conteneur via ports publiés (le seul comportement sensé par défaut)

Publiez les ports sur l’OS hôte, pas en essayant de donner les IP des conteneurs.
Avec Docker Desktop, vous ne pouvez pas traiter les IP des conteneurs comme routables sur le LAN physique. Elles vivent derrière un NAT, dans une VM, derrière un autre NAT si votre OS fait aussi quelque chose d’astucieux.

Ce qu’il faut faire :

  • Lier sur toutes les interfaces : -p 0.0.0.0:8080:80 ou en Compose "8080:80" et s’assurer que la publication ne se limite pas par défaut à localhost.
  • Ouvrir le pare‑feu de l’hôte pour ce port (et limiter la portée ; n’exposez pas votre base de données de dev au Wi‑Fi d’un café).
  • Si votre VPN interdit les connexions entrantes depuis le LAN pendant qu’il est connecté, acceptez la réalité : testez sans VPN ou utilisez un vrai environnement de dev ailleurs.

Schéma B : conteneur → ressources LAN (le routage marche jusqu’à ce qu’il cesse)

Les conteneurs atteignent généralement votre LAN hors de la boîte, parce que Docker Desktop NATe le trafic sortant via l’OS hôte.
Puis vous connectez un VPN, et l’OS hôte change DNS et routes. Soudain votre conteneur ne peut plus résoudre ou atteindre des sous‑réseaux maintenant « possédés » par le VPN.

Quand ça échoue, ça échoue de façons répétables :

  • Chevauchement de sous‑réseau : Docker choisit une plage privée que votre VPN route. Les paquets disparaissent dans le tunnel.
  • Mismatch de split DNS : l’hôte résout les noms internes via le DNS d’entreprise, mais les conteneurs sont coincés sur un stub resolver qui ne relaie pas correctement les domaines séparés.
  • Politique de pare‑feu : l’agent d’entreprise refuse le trafic depuis des interfaces virtuelles « inconnues ».

Schéma C : conteneur → services de l’OS hôte (utilisez les noms spéciaux)

Utilisez host.docker.internal. C’est pour ça que ça existe.
Ce n’est pas élégant, mais c’est stable face aux changements DHCP et moins fragile que de coder en dur une 192.168.x.y.

Si vous êtes sur Linux (pas Desktop) vous ne l’aurez peut‑être pas ; sur Desktop vous l’avez généralement.

Ports : publication, adresses de binding, et pourquoi les collègues ne peuvent pas atteindre votre serveur de dev

Les ports publiés sont la monnaie d’échange pour « rendre mon conteneur accessible ». Tout le reste est dette.

Localhost n’est pas une vertu morale, c’est une adresse de binding

Deux choses différentes sont constamment confondues :

  • Où l’app écoute à l’intérieur du conteneur (127.0.0.1 vs 0.0.0.0).
  • Où Docker lie le port publié sur l’hôte (127.0.0.1:PORT vs 0.0.0.0:PORT).

Si l’un ou l’autre est « localhost‑seulement », les clients LAN perdent. Et vous perdrez du temps à blâmer l’autre couche.

Astuce Compose : ne vous liez pas accidentellement à localhost

Compose supporte le binding explicite de l’IP hôte. C’est génial quand vous le voulez et terrible quand ce n’est pas le cas.

cr0x@server:~$ cat docker-compose.yml
services:
  web:
    image: nginx:alpine
    ports:
      - "127.0.0.1:8080:80"

Sens : Ce service est volontairement accessible uniquement depuis l’OS hôte.
Décision : Si vous voulez un accès LAN, changez‑le en "8080:80" ou "0.0.0.0:8080:80", puis gérez correctement la portée du pare‑feu.

Quand les ports publiés restent inatteignables depuis le LAN

Si Docker affiche 0.0.0.0:8080 mais que les clients LAN ne peuvent pas se connecter :

  • Pare‑feu hôte : pare‑feu d’application macOS, Windows Defender Firewall, outils d’endpoint tiers.
  • Sélection d’interface : le port peut être lié, mais l’OS peut bloquer l’entrée sur le Wi‑Fi tout en l’autorisant sur Ethernet (ou l’inverse).
  • Politique VPN : certains clients appliquent « bloquer le LAN local » pour réduire le risque de mouvement latéral.
  • Quirks NAT hairpin : certains réseaux ne vous laissent pas atteindre votre propre IP publique depuis l’intérieur ; ce n’est pas Docker, c’est votre routeur qui fait de son mieux.

Blague #2 : Rien n’améliore l’esprit d’équipe comme dire à quelqu’un « ça marche sur ma machine » en le prenant comme une déclaration d’architecture réseau.

Correctifs DNS : passer de « c’est instable » à « c’est déterministe »

Le DNS est l’endroit où les bizarreries de Docker Desktop deviennent de la légende. Le problème n’est généralement pas « Docker ne peut pas gérer le DNS ».
Le problème est : vous avez maintenant au moins deux résolveurs (OS hôte et VM), parfois trois (le VPN), et ils ne s’accordent pas sur les règles de split‑horizon.

Mode de défaillance 1 : le DNS du conteneur résout les noms publics mais pas les noms internes

Split DNS d’entreprise classique : git.corp ne se résout qu’avec des serveurs DNS internes, accessibles seulement via le VPN.
Votre OS hôte fait la bonne chose. Votre conteneur utilise un stub resolver qui ne relaie pas correctement les domaines internes.

Options de correction, du meilleur au pire :

  1. Configurer le DNS de Docker Desktop pour utiliser vos résolveurs internes quand vous êtes sur VPN, et des résolveurs publics hors VPN. Parfois c’est un basculement manuel parce que l’« auto » peut être peu fiable.
  2. DNS par projet dans Compose :
    • Définir dns: sur les IP des résolveurs qui peuvent répondre aux noms internes et externes (souvent ceux fournis par le VPN).
  3. Hardcoder /etc/hosts dans les conteneurs. C’est un bricolage tactique, pas une stratégie.

Task 15: Override DNS in Compose and verify inside container

cr0x@server:~$ cat docker-compose.yml
services:
  web:
    image: alpine:3.20
    command: ["sleep","infinity"]
    dns:
      - 10.8.0.53
      - 1.1.1.1
cr0x@server:~$ docker compose up -d
[+] Running 1/1
 ✔ Container web-1  Started
cr0x@server:~$ docker exec -it web-1 sh -lc "cat /etc/resolv.conf"
nameserver 10.8.0.53
nameserver 1.1.1.1

Sens : Le conteneur utilise désormais les serveurs DNS que vous avez spécifiés.
Décision : Si les domaines internes se résolvent maintenant, vous avez prouvé que c’est un problème de chemin DNS / split DNS, pas un problème d’application.

Mode de défaillance 2 : le DNS fonctionne, mais seulement parfois (timeouts, builds lents, installations de paquets instables)

Les échecs DNS intermittents proviennent souvent de :

  • Des serveurs DNS VPN qui perdent des paquets UDP sous charge ou nécessitent TCP pour les grandes réponses.
  • Des agents de sécurité d’entreprise interceptant le DNS et provoquant parfois des timeouts.
  • Des problèmes MTU/MSS sur les liens tunnellisés (DNS sur UDP fragmenté puis qui meurt silencieusement).

Task 16: Detect DNS timeouts vs NXDOMAIN inside container

cr0x@server:~$ docker exec -it web-1 sh -lc "time getent hosts pypi.org >/dev/null; echo $?"
real    0m0.042s
user    0m0.000s
sys     0m0.003s
0

Sens : Succès rapide.
Décision : Si cela prend des secondes ou échoue de façon intermittente, préférez changer de résolveurs (ou forcer TCP via un résolveur différent) plutôt que de faire tourner indéfiniment vos scripts de build.

Mode de défaillance 3 : le service interne fonctionne par IP mais pas par nom (et seulement sur VPN)

C’est encore du split DNS, avec une couche en plus : parfois le VPN pousse un suffixe DNS et des domaines de recherche à l’OS hôte,
mais le resolver de Docker Desktop n’hérite pas proprement de ces paramètres.

Task 17: Confirm search domains inside container

cr0x@server:~$ docker exec -it web-1 sh -lc "cat /etc/resolv.conf"
nameserver 10.8.0.53
search corp.example
options ndots:0

Sens : Le domaine de recherche est présent.
Décision : S’il manque, les noms courts peuvent échouer tandis que les FQDN fonctionnent. Utilisez des FQDN ou configurez les domaines de recherche au niveau du conteneur.

VPN, split‑tunnels et l’« aide » des agents d’entreprise

Les VPN provoquent deux grandes classes de problèmes : changements de routage et changements DNS. Docker Desktop amplifie les deux parce qu’il est effectivement un réseau imbriqué.

Routage : quand le VPN vous vole l’espace RFC1918

Beaucoup de réseaux d’entreprise routent de larges plages privées comme 10.0.0.0/8 ou 172.16.0.0/12 via le tunnel.
Docker utilise souvent 172.17.0.0/16 pour le bridge et d’autres plages 172.x pour les réseaux utilisateur.

Sur un hôte Linux pur, vous pouvez généralement gérer cela avec des subnets bridge personnalisés et iptables. Sur Desktop, vous pouvez toujours le faire, mais il faut en faire une configuration de première classe.

Task 18: Create a user-defined network on a “safe” subnet

cr0x@server:~$ docker network create --subnet 192.168.240.0/24 devnet
9f8c7b6a5d4e3c2b1a0f
cr0x@server:~$ docker run -d --name web2 --network devnet -p 8081:80 nginx:alpine
b1c2d3e4f5a6
cr0x@server:~$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web2
192.168.240.2

Sens : Vous avez déplacé le réseau du conteneur hors des plages communes aux routes d’entreprise.
Décision : Si la reachabilité liée au VPN s’améliore, institutionnalisez une politique de sous‑réseau pour les réseaux de dev.

Endpoint security : le middlebox invisible

Certains outils d’endpoint traitent les NIC de virtualisation comme « non fiables ». Ils peuvent bloquer l’entrée ou la sortie, ou forcer le trafic via un proxy.
Les symptômes incluent : les ports publiés ne fonctionnent que lorsque l’agent de sécurité est mis en pause, le DNS devient lent, ou les services internes échouent TLS à cause de l’inspection.

Vous ne pouvez pas « SRE » votre chemin hors d’une politique. Ce que vous pouvez faire, c’est obtenir rapidement des preuves, puis escalader avec des éléments concrets.

Task 19: Prove it’s local firewall/policy with a quick inbound test

cr0x@server:~$ python3 -m http.server 18080 --bind 0.0.0.0
Serving HTTP on 0.0.0.0 port 18080 (http://0.0.0.0:18080/) ...

Sens : Ce n’est pas Docker. C’est un processus hôte simple.
Décision : Si un pair LAN ne peut pas atteindre ceci non plus, arrêtez de déboguer Docker et corrigez les réglages pare‑feu/VPN « bloquer le réseau local ».

Windows + WSL2 spécificités (où les paquets vont se reposer)

Sur Windows moderne, Docker Desktop exécute souvent son moteur dans WSL2. WSL2 a son propre réseau virtuel (NAT derrière Windows).
Cela signifie que vous pouvez avoir : NAT du conteneur derrière Linux, derrière le NAT de WSL2, derrière les règles de pare‑feu Windows. C’est du NAT jusqu’au fond.

Symptômes typiques Windows

  • Port publié joignable depuis le localhost Windows mais pas depuis le LAN. Habituellement règles d’entrée du Windows Defender Firewall, ou binding en loopback seulement.
  • Les conteneurs ne peuvent pas atteindre un sous‑réseau LAN que Windows peut atteindre. Habituellement les routes VPN ne sont pas propagées comme vous le pensez dans WSL2, ou la politique bloque les interfaces WSL.
  • Le DNS diffère entre Windows et WSL2. WSL2 écrit son propre /etc/resolv.conf ; parfois il pointe vers un résolveur côté Windows qui ne voit pas le DNS du VPN.

Task 20: Check WSL2’s resolv.conf and route table (from inside WSL)

cr0x@server:~$ cat /etc/resolv.conf
nameserver 172.29.96.1
cr0x@server:~$ ip route | head
default via 172.29.96.1 dev eth0
172.29.96.0/20 dev eth0 proto kernel scope link src 172.29.96.100

Sens : WSL2 utilise une gateway/resolver virtuelle côté Windows.
Décision : Si le DNS casse uniquement sur VPN, envisagez de configurer le comportement DNS de WSL2 (resolv.conf statique) et d’aligner le DNS de Docker avec les résolveurs fournis par le VPN.

macOS spécificités (pf, vmnet, et l’illusion de localhost)

Sur macOS, Docker Desktop exécute une VM Linux et redirige les ports vers macOS.
Vos conteneurs ne sont pas des citoyens de première classe sur votre LAN physique. Ce sont des invités derrière un concierge très poli.

Ce qui embrouille les utilisateurs macOS

  • « Ça marche sur localhost mais pas depuis mon téléphone. » Habituellement le pare‑feu macOS ou un port publié uniquement sur la loopback.
  • Le DNS change quand le Wi‑Fi change de réseau. Le résolveur hôte change rapidement ; la VM met parfois du temps ou met en cache des bizarreries.
  • Le VPN d’entreprise bloque l’accès au sous‑réseau local. Votre téléphone ne peut pas atteindre votre portable lorsque le VPN est connecté, indépendamment de Docker.

Task 21: Confirm the host OS has the right IP and interface for LAN testing

cr0x@server:~$ ip addr show | sed -n '1,25p'
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 192.168.1.50/24 brd 192.168.1.255 scope global dynamic wlan0

Sens : Votre IP LAN est 192.168.1.50.
Décision : C’est l’adresse qu’un pair LAN doit utiliser pour atteindre votre port publié. Si les pairs utilisent une IP ancienne, ils testent la mauvaise machine.

Erreurs courantes : symptôme → cause racine → correction

1) Symptom: localhost:8080 works, coworker can’t reach 192.168.x.y:8080

  • Cause racine : Port publié seulement sur 127.0.0.1, ou pare‑feu hôte bloquant l’entrée.
  • Correction : Publiez sur toutes les interfaces (-p 0.0.0.0:8080:80), puis autorisez l’entrée pour ce port sur le pare‑feu de l’hôte pour le profil réseau approprié.

2) Symptom: container can reach internet but not 192.168.1.10 (LAN NAS)

  • Cause racine : Politique VPN « block local LAN » ou routes poussant les sous‑réseaux LAN dans le tunnel.
  • Correction : Testez sans VPN ; si cela règle le problème, demandez des exceptions split‑tunnel ou exécutez la charge de travail dans un vrai environnement (VM distante, staging). Ne combattez pas la politique avec des bricolages.

3) Symptom: container can reach LAN IPs but internal hostnames fail

  • Cause racine : Split DNS non propagé dans Docker Desktop ; les conteneurs utilisent un stub resolver qui ne voit pas les zones internes.
  • Correction : Configurez le DNS au niveau du projet (dns: dans Compose) pour inclure les serveurs DNS d’entreprise accessibles via VPN ; vérifiez avec getent hosts.

4) Symptom: DNS flaps during builds (apt/npm/pip failing randomly)

  • Cause racine : DNS UDP peu fiable via VPN, problèmes MTU, interception par l’endpoint.
  • Correction : Préférez des résolveurs stables ; utilisez deux résolveurs (interne + public) lorsque la politique le permet ; réduisez le risque de fragmentation en traitant le MTU côté VPN si vous le contrôlez.

5) Symptom: service is published, but you get “connection refused” from LAN

  • Cause racine : L’app écoute seulement sur le localhost du conteneur, ou mauvais port conteneur publié.
  • Correction : Vérifiez ss -lntp à l’intérieur du conteneur ; corrigez l’adresse de binding ; vérifiez docker port et le mapping de ports du conteneur.

6) Symptom: can’t connect to host.docker.internal from container

  • Cause racine : Override DNS a supprimé le nom spécial, ou vous utilisez un mode réseau où Desktop ne l’injecte pas.
  • Correction : Évitez d’écraser le DNS aveuglément ; si vous devez le faire, assurez‑vous que le nom spécial se résout toujours (ou ajoutez une entrée hôte explicite via extra_hosts en dernier recours).

7) Symptom: everything breaks only on one Wi‑Fi network

  • Cause racine : Ce réseau isole les clients (AP isolation) ou bloque les connexions entrantes entre appareils.
  • Correction : Utilisez un réseau approprié (ou filaire), ou exécutez le service derrière un tunnel inverse ; ne supposez pas que « même Wi‑Fi » signifie « mutuellement joignable ».

Trois mini‑histoires d’entreprise (réalistes, anonymisées, douloureuses)

Mini‑histoire 1 : L’incident causé par une mauvaise hypothèse

Une équipe produit a construit un environnement de démonstration sur des portables pour un atelier client sur site. Le plan était simple : exécuter quelques services dans Docker Desktop, publier des ports,
et permettre aux participants de se connecter via le Wi‑Fi de l’hôtel. Tout le monde avait fait « -p 8080:8080 » mille fois. L’hypothèse erronée était que Docker Desktop se comporte comme un hôte Linux sur un LAN plat.

Le matin de l’atelier, la moitié des participants ne pouvait pas se connecter. Les services étaient pourtant démarrés. Le curl local fonctionnait. Les présentateurs se connectaient parfois entre eux.
Les gens ont commencé à redémarrer comme en 1998. Le problème réseau n’était pas Docker ; c’était le Wi‑Fi de l’hôtel qui faisait de l’isolation client—les appareils atteignaient Internet mais pas entre eux.

La deuxième mauvaise hypothèse est arrivée immédiatement : « Utilisons les IP des conteneurs et évitons le mapping de ports. »
Ils ont essayé de distribuer des adresses 172.17.x.x visibles à l’intérieur de la VM Docker, qui bien sûr n’étaient pas joignables depuis d’autres portables.
Cela a mené à dix minutes de certitudes confuses et à un diagramme sur tableau blanc fortement regretté.

La correction fut ennuyeuse : créer un hotspot local sur un téléphone permettant le trafic pair‑à‑pair,
et publier explicitement les ports nécessaires sur 0.0.0.0 avec une règle de pare‑feu rapide.
Les services fonctionnaient. L’hypothèse sur le « même réseau » était la vraie panne.

Mini‑histoire 2 : L’optimisation qui a échoué

Une équipe plateforme voulait des builds CI plus rapides sur les machines dev. Ils ont remarqué de nombreuses requêtes DNS pendant les builds et ont décidé « d’optimiser » en forçant les conteneurs Docker à utiliser un résolveur DNS public.
Ça marchait bien en test café : résolutions plus rapides, moins de timeouts, jolis graphiques.

Puis le premier ingénieur a essayé de builder en étant sur VPN. Les registres de paquets internes n’étaient accessibles qu’via le DNS d’entreprise et des routes internes.
Soudain, les builds échouaient avec « host not found » alors que l’OS hôte résolvait correctement. Le contournement est devenu « déconnecte le VPN »,
ce qui est une excellente façon de créer le prochain incident.

La situation s’est empirée parce que certains noms internes se résolvaient publiquement en IPs factices (pour des raisons de sécurité), donc « le DNS avait réussi » mais les connexions allaient dans un trou noir.
Le débogage fut brutal : on voyait des enregistrements A, l’application timeoutait, et tout le monde blâmait TLS, les proxys et Docker dans un ordre aléatoire.

La correction finale fut d’arrêter d’optimiser le DNS globalement. Ils sont passés à des paramètres DNS par projet :
résolveurs internes en priorité quand le VPN est actif, résolveurs publics seulement hors VPN.
Ils ont aussi documenté comment tester la résolution à l’intérieur des conteneurs, parce que « ça se résout sur mon hôte » n’est pas une donnée fiable dans un réseau imbriqué.

Mini‑histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Un service sensible à la sécurité utilisait Docker Desktop pour des tests d’intégration locaux. Il devait appeler une API interne et aussi accepter des webhooks entrants d’un outil de test sur une autre machine du même bureau.
L’équipe avait une habitude que je respecte : avant de changer quoi que ce soit, ils capturaient une preuve « connue‑bonne » du réseau—routes, config DNS, bindings de ports—quand tout fonctionnait.

Un lundi, tout a cassé après une mise à jour OS. Les conteneurs ne pouvaient plus résoudre les noms internes. Les webhooks d’une machine LAN n’arrivaient plus.
Au lieu de deviner, ils ont comparé l’état actuel au baseline : les ports publiés étaient maintenant liés uniquement à localhost, et le stub DNS à l’intérieur des conteneurs pointait vers une nouvelle IP côté VM qui ne relaie pas le split DNS.

Ils ont corrigé le binding des ports dans Compose, puis figé le DNS des conteneurs vers les résolveurs internes quand le VPN est actif.
Parce qu’ils avaient le baseline, ils ont pu montrer à l’équipe sécurité endpoint exactement ce qui avait changé et pourquoi.
L’incident n’est pas devenu une semaine de blâme généralisé.

Cette pratique—capturer un état de référence, comparer lors d’une panne—est aussi excitante que regarder la peinture sécher.
Elle marche pourtant.

Listes de contrôle / plan étape par étape (soporifique volontairement)

Checklist 1: Exposer un service Docker Desktop à votre LAN de façon fiable

  1. Assurez‑vous que l’app écoute sur 0.0.0.0 à l’intérieur du conteneur (ss -lntp).
  2. Publiez le port sur toutes les interfaces : -p 0.0.0.0:8080:80 (ou Compose "8080:80").
  3. Confirmez que Docker voit le mapping : docker port CONTAINER.
  4. Confirmez que l’OS hôte écoute sur ce port : ss -lntp | grep :8080.
  5. Testez localement : curl http://127.0.0.1:8080.
  6. Testez depuis un pair LAN : nc -vz HOST_LAN_IP 8080.
  7. Si le test LAN échoue, lancez un écouteur non‑Docker (python3 -m http.server) pour isoler le pare‑feu/VPN des problèmes Docker.

Checklist 2: Faire en sorte que les conteneurs atteignent les ressources LAN internes (NAS, APIs internes)

  1. Depuis l’OS hôte, vérifiez que la cible est joignable par IP.
  2. Depuis l’intérieur du conteneur, testez la connectivité IP (nc -vz ou curl).
  3. Si l’IP échoue seulement sur VPN, vérifiez le chevauchement de routes (ip route) et les politiques VPN (« block local LAN »).
  4. Si l’IP fonctionne mais le nom échoue, vérifiez /etc/resolv.conf et résolvez avec getent hosts.
  5. Surchargez le DNS par projet via Compose dns: si nécessaire.
  6. Évitez le chevauchement de sous‑réseaux : déplacez les réseaux Docker vers une plage que votre VPN ne route pas.

Checklist 3: Stabiliser le DNS pour les builds de dev (pip/npm/apt qui flanchent)

  1. Mesurez le temps de résolution dans le conteneur avec time getent hosts.
  2. Inspectez les résolveurs actuels dans /etc/resolv.conf.
  3. Si vous êtes sur VPN, préférez les résolveurs internes fournis par le VPN (et ajoutez une fallback publique seulement si autorisé).
  4. Ne hardcodez pas un DNS public globalement pour tous les projets ; vous casserez les workflows split DNS.
  5. Retestez dans le conteneur après les changements ; ne faites pas confiance aux résultats de l’OS hôte.

FAQ

1) Pourquoi ne puis‑je pas simplement utiliser l’IP du conteneur depuis une autre machine sur mon LAN ?

Parce que sur Docker Desktop cette IP est sur un bridge interne dans une VM Linux (ou un environnement WSL2). Votre LAN ne la route pas. Publiez des ports à la place.

2) Pourquoi -p 8080:80 marche localement mais pas depuis mon téléphone ?

Habituellement soit le port est lié uniquement à localhost (explicitement ou via Compose), soit votre pare‑feu/VPN hôte bloque les connexions entrantes depuis le LAN.

3) Quelle est la différence entre 127.0.0.1 et 0.0.0.0 dans ce contexte ?

127.0.0.1 signifie « n’accepter les connexions que depuis cette même pile réseau ». 0.0.0.0 signifie « écouter sur toutes les interfaces ».
Vous avez besoin de 0.0.0.0 si vous attendez des connexions depuis d’autres appareils.

4) Est‑ce que --network host est la solution pour le réseau Docker Desktop ?

Non. Sur Docker Desktop, « host network » n’est pas identique au networking Linux et ne vous donnera souvent pas ce que vous attendez. Préférez bridge + ports publiés.

5) Pourquoi le DNS fonctionne sur mon hôte mais pas dans les conteneurs ?

Le conteneur peut utiliser un chemin de résolveur différent (un stub dans la VM), et il peut ne pas hériter de la configuration split DNS de votre VPN.
Vérifiez avec cat /etc/resolv.conf et getent hosts dans le conteneur, puis surchargez le DNS par projet si nécessaire.

6) Dois‑je définir le DNS de Docker Desktop sur un résolveur public pour « tout réparer » ?

Seulement si vous n’avez jamais besoin du DNS interne. Les résolveurs publics peuvent casser les domaines d’entreprise, les registres internes et les configurations split‑horizon.
Utilisez un DNS spécifique au projet ou un comportement conditionnel lié à l’état du VPN.

7) Mon conteneur ne peut pas atteindre un appareil LAN seulement quand le VPN est connecté. Est‑ce la faute de Docker ?

Presque jamais. Les clients VPN peuvent router des sous‑réseaux privés via le tunnel ou bloquer l’accès LAN local.
Prouvez‑le en testant la même connexion depuis l’OS hôte et en déconnectant le VPN comme témoin.

8) Quelle est la manière la plus fiable pour un conteneur d’appeler un service sur mon portable ?

Utilisez host.docker.internal et gardez‑le cohérent entre les environnements. Évitez les IP hôtes codées en dur qui changent avec les réseaux Wi‑Fi.

9) Comment savoir si le problème vient du pare‑feu ou du mapping de ports Docker ?

Lancez un écouteur non‑Docker sur l’hôte (comme python3 -m http.server). Si le LAN ne peut pas atteindre cela, Docker n’est pas le problème.

10) Quel principe simple pour garder la tête froide avec le réseau Desktop ?

Traitez Docker Desktop comme « des conteneurs derrière une VM derrière votre OS ». Publiez des ports, évitez le chevauchement de sous‑réseaux, et validez le DNS depuis l’intérieur du conteneur.

Conclusion : prochaines étapes que vous pouvez faire aujourd’hui

Le réseau Docker Desktop cesse d’être étrange quand vous arrêtez de vous attendre à ce qu’il soit le networking d’un hôte Linux. C’est une frontière VM avec une couche de forwarding.
Une fois que vous l’acceptez, la plupart des problèmes se résument à trois catégories : adresses de binding, politique pare‑feu/VPN, et dérive DNS/resolvers.

Prochaines étapes pratiques :

  1. Choisissez un service de test, publiez‑le sur 0.0.0.0, et vérifiez la reachabilité LAN de bout en bout avec ss, curl et nc.
  2. Capturez un état de référence quand tout fonctionne : docker port, /etc/resolv.conf du conteneur, et la table de routage de l’hôte.
  3. Si vous utilisez un VPN, évitez que les réseaux Docker chevauchent les routes d’entreprise. Standardisez une plage « sûre » pour les réseaux de dev.
  4. Faites du DNS une configuration par projet quand des noms internes sont importants. Les « correctifs » globaux sont la meilleure façon de créer des ruptures inter‑équipes.

Une idée paraphrasée de Werner Vogels (CTO d’Amazon) : « Tout échoue ; concevez vos systèmes — et vos opérations — pour absorber cet échec. »
Le réseau Docker Desktop n’est pas spécial. C’est juste l’échec avec des couches en plus.

← Précédent
Ubuntu 24.04 : « Failed to get D-Bus connection » — réparer les sessions et services cassés (cas n°48)
Suivant →
SLI/CrossFire : pourquoi le multi-GPU était un rêve — et pourquoi il a disparu

Laisser un commentaire