Le réseau hôte est l’équivalent réseau de supprimer les garde‑fous parce que vous êtes « un conducteur prudent ». Parfois c’est la bonne décision — forts débits de paquets, latence faible, moins de composants. D’autres fois c’est la façon d’envoyer par erreur un conteneur qui se lie sur 0.0.0.0:22 et passe le week‑end à discuter avec des bots du monde entier.
Si vous avez déjà dit « c’est juste un service interne » et découvert plus tard qu’il écoutait depuis Internet, vous savez déjà pourquoi ce sujet mérite plus qu’un avertissement d’une ligne.
Ce que fait réellement le réseau hôte (et ce qu’il supprime silencieusement)
Dans le réseau Docker « normal », les conteneurs vivent dans leurs propres espaces de noms réseau. Ils obtiennent des interfaces virtuelles, des adresses IP privées et atteignent typiquement l’extérieur via du NAT. Les ports publiés sont gérés par une combinaison de règles iptables/nftables et parfois un proxy en espace utilisateur. Le conteneur est un locataire. L’hôte est l’immeuble.
Avec --network host, vous déplacez le conteneur dans l’espace de noms réseau de l’hôte. Pas de paire veth. Pas d’IP de conteneur distincte. Pas de bridge Docker pour ce conteneur. Le conteneur voit les interfaces de l’hôte, la table de routage de l’hôte et les ports écoutés par l’hôte. Un processus dans le conteneur qui se lie sur 0.0.0.0:8080 se lie sur l’hôte.
Ce n’est pas « juste un réseau plus rapide ». C’est un compromis d’isolation. Vous contournez une partie importante de la frontière du conteneur, ce qui signifie :
- Pas de filet de sécurité pour la publication de ports. Docker ne peut pas médiatiser l’exposition avec
-p. Le processus décide quoi écouter et où. - Les conflits de ports deviennent des échecs immédiats. Deux conteneurs ne peuvent pas écouter le même port hôte sans coordination.
- Les attentes au sujet du pare‑feu changent. Vous ne traitez plus la traduction conteneur→hôte via les chaînes DOCKER ; le trafic est juste… du trafic hôte.
- L’observabilité devient étrange. « Quel conteneur possède cette socket ? » devient une question d’enquête forensique au lieu d’une requête CLI.
- Les barrières de sécurité s’amincissent. L’isolation par espace de noms réseau disparaît ; d’autres protections restent (cgroups, espaces de noms de montage, seccomp, capacités), mais vous avez supprimé une couche significative.
Sur Linux, le réseau hôte est simple parce que les espaces de noms réseau sont une fonctionnalité du noyau Linux. Sur Docker Desktop (Mac/Windows), le « réseau hôte » n’est pas le même animal ; il s’exécute dans une VM et se comporte différemment. En production, la plupart des douleurs (et les gains de vitesse) se rencontrent sur Linux.
La citation à retenir
Werner Vogels (idée paraphrasée) : « Tout échoue, tout le temps. » Si vous traitez le réseau hôte comme « sûr parce que c’est rapide », vous prévoyez des temps d’arrêt.
Petite blague #1 : Le réseau hôte, c’est comme donner au conteneur la clé maîtresse de l’immeuble — super, jusqu’à ce qu’il commence à « réarranger » les portes.
Quand le réseau hôte en vaut la peine
Je ne suis pas là pour moraliser. Le réseau hôte est un outil. Parfois c’est l’outil adapté.
1) Chemins de paquets haute performance où le NAT/bridge pose problème
Si vous traitez un fort nombre de paquets par seconde ou réduisez la latence, supprimer le chemin veth/bridge/NAT peut aider. Pensez aux collecteurs de télémétrie, aux équilibres de charge L4, ou aux appliances réseau spécialisées exécutées en conteneur. Le réseau hôte évite aussi la pression sur conntrack due au NAT dans certains déploiements.
Mais : si vous « avez besoin du réseau hôte pour les performances », prouvez‑le. Mesurez avant et après. Dans la plupart des charges web, le goulot n’est pas le bridge Docker ; c’est TLS, les appels système, le GC, les appels base de données, ou une inefficacité applicative.
2) Participation au routage au niveau hôte ou aux démons de routage
Certains démons s’attendent à se lier à des ports bien connus sur des interfaces spécifiques, ou à interagir avec le routage de l’hôte d’une manière compliquée via un bridge. Exemples : orateurs BGP, comportements de type VRRP, ou logiciels qui manipulent les routes et attendent la visibilité de l’hôte.
3) Environnements de laboratoire simples et déploiements « un service par machine »
Si une machine est dédiée à un seul service conteneurisé et que vous la traitez comme un serveur « pet » (je sais), le réseau hôte peut réduire la friction de configuration. Moins de pièces mobiles : pas de mappage de ports, moins de cas limites du réseau Docker, moins de « pourquoi je n’y accède pas depuis ce VLAN ? » à déboguer.
4) Agents de supervision qui doivent voir le réseau hôte
Certains outils de supervision réseau, capture de paquets, IDS ou exportateurs de flux doivent s’attacher aux vraies interfaces de l’hôte. Le réseau hôte est une option. Une autre est --cap-add NET_ADMIN plus l’accès à des périphériques spécifiques ; mais il est courant de voir le réseau hôte utilisé pour ces agents.
Quand ça n’en vaut pas la peine
- Hôtes multi‑locataires. Si vous exécutez de nombreux services par hôte, le réseau hôte augmente fortement les risques d’exposition accidentelle et de conflits de ports.
- Tout ce qui reçoit des entrées non fiables. Endpoints HTTP publics, parseurs, gateways protocolaires. Vous voulez des couches, pas moins de couches.
- Toute chose que vous pourriez vouloir déplacer rapidement. Le réseau hôte intègre souvent des hypothèses sur les interfaces et les ports de l’hôte, ce qui ralentit les migrations.
- Quand « ça l’a réparé une fois ». Utiliser le réseau hôte comme superstition conduit à une plateforme fragile.
Les vrais risques : isolation, rayon d’impact et « oups, c’est l’hôte »
Risque n°1 : Vous contournez le modèle de publication de ports de Docker
Avec le réseau bridge, -p 127.0.0.1:8080:8080 est une déclaration explicite appréciable. Avec le réseau hôte, il n’y a pas de « publication ». Le processus se lie ; l’hôte écoute. Si l’appli se lie à 0.0.0.0, elle est atteignable sur toutes les interfaces de l’hôte — VLANs de production, VLANs de gestion, VPNs, partout.
C’est ainsi que des ports d’administration internes deviennent des incidents externes. Pas par malveillance. Par défauts.
Risque n°2 : La politique de pare‑feu au niveau hôte devient la seule vraie barrière
Dans une configuration bridge typique, vous verrez des chaînes DOCKER et DOCKER-USER qui médiatisent le trafic. Avec le réseau hôte, le trafic du conteneur est du trafic hôte. C’est acceptable si votre pare‑feu hôte est strict. C’est catastrophique si votre pare‑feu hôte est « on l’ajoutera plus tard ».
Risque n°3 : Conflits de ports et démarrages non déterministes
Sur un hôte chargé, deux services peuvent « fonctionner en staging » parce que l’ordre de démarrage diffère, puis échouer en production parce que quelque chose s’est lié en premier. Ce n’est pas théorique. C’est le genre d’échec que vous obtenez à 2 h du matin parce qu’un nœud a redémarré et que systemd a lancé les choses dans un ordre légèrement différent.
Risque n°4 : L’observabilité et l’attribution deviennent plus difficiles
Avec le réseau bridge, vous pouvez dire « le conteneur X possède 172.17.0.5:9000 ». Avec le réseau hôte, vous dites « quelque chose sur l’hôte possède :9000 », et maintenant vous creusez dans les arbres de processus, les cgroups et les métadonnées de conteneur pour savoir quoi. Cela ralentit la réponse aux incidents.
Risque n°5 : Certaines protections existent encore, mais ne vous illusionnez pas
Le réseau hôte ne vous donne pas automatiquement root sur l’hôte. Les espaces de noms pour les montages, PIDs, utilisateurs et cgroups ont toujours de l’importance. Seccomp aussi. Les capacités aussi. Mais la frontière réseau est un endroit commun pour appliquer le moindre privilège. Vous l’avez supprimée.
Risque n°6 : « Localhost » n’est plus local au conteneur
À l’intérieur d’un conteneur en réseau hôte, 127.0.0.1 est la loopback de l’hôte. Cela signifie :
- Le conteneur peut atteindre des services host‑only liés à localhost (sauf restrictions).
- Si le conteneur se lie à localhost, il se lie aussi sur la loopback de l’hôte, pouvant entrer en collision avec d’autres services locaux.
Petite blague #2 : Le moyen le plus rapide de « réduire la latence » est de supprimer votre pare‑feu. Merci de ne pas faire vos benchmarks ainsi.
Faits intéressants et courte histoire (contexte, pas quiz)
- Les espaces de noms réseau Linux sont arrivés dans le noyau en 2008 (vers l’ère 2.6.24), permettant des piles réseau par processus sans machines virtuelles complètes.
- Docker s’appuyait initialement fortement sur iptables pour la publication de ports, ce qui explique pourquoi le réseau Docker et les règles de pare‑feu sont liés depuis le début.
- Le « userland proxy » existait pour gérer des cas limites (comme les connexions hairpin), et au fil du temps beaucoup de déploiements ont adopté des chemins NAT noyau pour réduire la charge.
- Conntrack est une ressource partagée : lorsque vous NATtez beaucoup de trafic conteneur, vous misez sur une table d’état finie dans le noyau.
- Le réseau hôte existait avant Docker : les runtimes de conteneurs et les jails offraient souvent des modes « pile partagée » bien avant que ce soit un drapeau dans Docker.
- Kubernetes
hostNetwork: truea des compromis similaires ; les risques ne sont pas spécifiques à Docker — ils sont liés aux espaces de noms. - Certaines CNIs évitent entièrement le NAT en routant les IP des pods, ce qui explique pourquoi « réseau hôte pour la performance » n’est pas toujours la comparaison adéquate.
- Les pare‑feux Linux modernes sont passés d’iptables à nftables, et des environnements mixtes peuvent produire des jeux de règles qui paraissent corrects mais ne font pas ce que vous pensez.
Trois mini‑histoires d’entreprise depuis le terrain
Mini‑histoire 1 : L’incident causé par une mauvaise hypothèse
Une entreprise SaaS de taille moyenne exécutait une passerelle de logs sur quelques hôtes Linux. Elle ingérait des logs depuis des agents internes et les transférait vers un backend managé. Quelqu’un a changé le conteneur en --network host parce que « le NAT perdait des paquets ». Le changement est passé après un simple test de fumée. Les logs circulaient. Tout le monde est retourné à livrer des fonctionnalités.
Deux semaines plus tard, un changement réseau non lié a exposé une interface auparavant non routée vers un segment réseau d’entreprise plus large. La passerelle de logs a aussi exposé un endpoint d’administration — censé n’être accessible que sur localhost pour le débogage. En mode bridge, il avait été publié uniquement sur 127.0.0.1. En mode hôte, le service s’est lié par défaut à 0.0.0.0 parce que la config était négligente et que personne n’a remarqué.
Le premier signe n’a pas été une alerte. C’était un ticket : « Pourquoi ce service a‑t‑il une UI d’administration ? » Puis des rapports de scanners. Puis la question exécutive : « Sommes‑nous compromis ? » La journée a été passée à prouver un négatif — fouiller les logs d’accès, resserrer la politique du pare‑feu, et apprendre que « interne » n’est pas une frontière de sécurité.
La vraie cause racine n’était pas seulement le réseau hôte. C’était l’hypothèse que le runtime de conteneur garderait l’exposition contenue. Le réseau hôte a rendu cette hypothèse fausse instantanément.
Mini‑histoire 2 : L’optimisation qui s’est retournée contre eux
Une équipe fintech avait une API sensible à la latence. Quelqu’un a mesuré la latence p99 et décidé que le bridge Docker « ajoutait du jitter ». Ils ont déplacé le conteneur API en réseau hôte. Les benchmarks se sont légèrement améliorés sur un hôte tranquille. Slide de victoire. Déploiement.
Puis le trafic de production a rencontré la réalité : services co‑localisés, mises à jour du noyau, et la vérité désordonnée des ports éphémères. L’API utilisait un pool de connexions sortantes vers une base de données. Avec le réseau hôte, le trafic sortant partageait la plage de ports éphémères de l’hôte avec tout le reste. Sous charge, l’hôte a commencé à atteindre l’épuisement des ports éphémères lors de pics, causant des échecs de connexion semblant indiquer une instabilité de la base de données.
L’équipe a pourchassé la base de données pendant des jours — ajouté des réplicas, réglé des paramètres, accusé le réseau — jusqu’à ce que quelqu’un regarde ss -s et voie une montagne de sockets en TIME-WAIT. L’« optimisation de performance » n’a pas seulement déplacé le goulot ; elle l’a déplacé vers une ressource partagée de l’hôte qui impactait maintenant des services non liés.
Ils sont revenus au réseau bridge pour l’API, ont corrigé la réutilisation des connexions, ajusté prudemment tcp_tw_reuse (et pas témérairement), et ont intégré des tests de charge appropriés dans le processus de changement. Le gain de latence ne valait pas le risque systémique.
Mini‑histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la situation
Une grande entreprise exécutait quelques conteneurs en réseau hôte sur des nœuds dédiés : un forwarder DNS interne et un collecteur de métriques. Ils avaient une politique : tout conteneur en réseau hôte devait être livré avec (1) une unité systemd explicite documentant les ports, (2) une liste blanche du pare‑feu hôte, et (3) un job d’audit récurrent qui prend des instantanés des sockets à l’écoute et compare les diffs.
Ce n’était pas glamoureux. Ça produisait des tickets du type « port 8125/udp toujours ouvert, attendu ». Personne n’a été promu pour ça. Mais cela a créé un contrat stable : si vous vouliez le réseau hôte, vous payiez pour des garde‑fous.
Un jour, une actualisation d’image de conteneur a tiré une nouvelle version d’un agent de métriques qui activait par défaut un serveur HTTP de debug. Le diff d’audit a signalé un nouvel écouteur sur :6060. Le pare‑feu l’a bloqué de toute façon. L’astreinte a reçu une alerte, a confirmé que c’était nouveau, et l’a désactivé dans la configuration avant que cela ne devienne une découverte de scan.
Le postmortem a été court et presque ennuyeux — ce qui est le meilleur. Les contrôles ont fonctionné, le rayon d’impact est resté petit, et le changement ne s’est pas transformé en incident de sécurité.
Tâches pratiques : commandes, sorties et décision à prendre
Ce ne sont pas des commandes « jouet ». Ce sont les choses que vous lancez quand vous décidez si le réseau hôte est sûr, et quand vous nettoyez après que ce ne l’était pas.
Tâche 1 : Identifier les conteneurs en réseau hôte (la vérification évidente)
cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Networks}}'
NAMES IMAGE NETWORKS
api-1 myco/api:2026.01 bridge
node-exporter prom/node-exporter:v1.8.0 host
dns-forwarder myco/dns:2.3 host
Ce que cela signifie : Tout ce qui affiche host partage l’espace de noms réseau de l’hôte.
Décision : Pour chaque conteneur en réseau hôte, documentez les ports sur lesquels il doit se lier et qui en est responsable. Si vous ne pouvez pas répondre en 60 secondes, vous êtes déjà en territoire « potentiel d’incident ».
Tâche 2 : Confirmer que le conteneur est réellement dans le netns de l’hôte
cr0x@server:~$ docker inspect -f '{{.Name}} {{.HostConfig.NetworkMode}}' node-exporter
/node-exporter host
Ce que cela signifie : C’est définitif : le conteneur est en mode host.
Décision : Si ce conteneur n’est pas sur un nœud dédié, traitez‑le comme un service à risque élevé et appliquez les étapes de confinement ci‑dessous.
Tâche 3 : Lister les ports à l’écoute sur l’hôte (le réseau hôte rend ça obligatoire)
cr0x@server:~$ sudo ss -lntup
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 4096 0.0.0.0:9100 0.0.0.0:* users:(("node_exporter",pid=2314,fd=3))
tcp LISTEN 0 4096 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=1189,fd=6))
tcp LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=4128,fd=112))
Ce que cela signifie : Vous voyez ce que le monde peut potentiellement atteindre. En mode hôte, les services conteneurs apparaissent comme des processus normaux de l’hôte.
Décision : Tout ce qui écoute sur 0.0.0.0 (ou ::) nécessite une position consciente sur le pare‑feu. Si vous attendiez qu’un service soit interne uniquement, liez‑le à une interface spécifique ou bloquez‑le explicitement.
Tâche 4 : Mapper un processus à l’écoute vers un conteneur (attribution rapide)
cr0x@server:~$ ps -p 2314 -o pid,cmd,cgroup
PID CMD CGROUP
2314 /bin/node_exporter --web.listen-address=:9100 0::/docker/1e7b9d0f6a3f8b2c9f3d6a1b4a6f0b2c1d9e0a7b...
Ce que cela signifie : Le chemin cgroup inclut l’ID du conteneur.
Décision : Si vous ne pouvez pas attribuer rapidement un port à un propriétaire, construisez un runbook qui le peut. Les incidents ne sont pas le moment de redécouvrir les métadonnées des processus Linux.
Tâche 5 : Vérifier quelles interfaces possède l’hôte (et lesquelles vous exposez accidentellement)
cr0x@server:~$ ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 10.20.4.18/24
eth1 UP 172.16.12.18/24
docker0 DOWN 172.17.0.1/16
Ce que cela signifie : Plusieurs NICs veulent dire plusieurs zones de confiance. Le réseau hôte expose sur toutes sauf si vous liez prudemment.
Décision : Si un service doit être accessible uniquement sur eth0, liez‑le à 10.20.4.18 (ou bloquez eth1) au lieu de compter sur « personne ne route ça ».
Tâche 6 : Vérifier la table de routage (les chemins de sortie inattendus existent)
cr0x@server:~$ ip route
default via 10.20.4.1 dev eth0
10.20.4.0/24 dev eth0 proto kernel scope link src 10.20.4.18
172.16.12.0/24 dev eth1 proto kernel scope link src 172.16.12.18
Ce que cela signifie : Les conteneurs en réseau hôte héritent de ce routage. Si vous avez du routage splitté ou du policy routing, les conteneurs le suivront aussi.
Décision : Si vous avez besoin d’un contrôle d’egress par service, le réseau hôte vous mettra des bâtons dans les roues. Préférez des namespaces séparés ou des proxys d’egress.
Tâche 7 : Voir quel framework de pare‑feu est actif (iptables vs nftables)
cr0x@server:~$ sudo iptables -S | head
-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
-N DOCKER
-N DOCKER-USER
Ce que cela signifie : INPUT en ACCEPT par défaut est un drapeau rouge dans n’importe quel environnement avec des conteneurs en réseau hôte.
Décision : Si INPUT est à ACCEPT, corrigez‑ça en priorité. Réseau hôte plus INPUT permissif = exposition accidentelle vers Internet.
Tâche 8 : Inspecter la chaîne DOCKER-USER (où vous devriez mettre la politique allow/deny)
cr0x@server:~$ sudo iptables -S DOCKER-USER
-N DOCKER-USER
-A DOCKER-USER -j RETURN
Ce que cela signifie : Aucune politique n’est appliquée ici. Aussi : le trafic du réseau hôte ne passera pas forcément par les chaînes Docker pour l’ingress.
Décision : Pour le réseau hôte, appliquez la politique dans INPUT (et éventuellement OUTPUT). Pour les conteneurs en bridge, DOCKER-USER est un bon point de contrôle. Ne les confondez pas.
Tâche 9 : Confirmer quels ports sont atteignables depuis un autre hôte (vérification réelle)
cr0x@server:~$ nc -vz 10.20.4.18 9100
Connection to 10.20.4.18 9100 port [tcp/*] succeeded!
Ce que cela signifie : Le port est atteignable sur cette interface depuis l’endroit où vous avez lancé la commande.
Décision : Si l’accessibilité est plus large que prévu, ne « vous souvenez pas de le corriger plus tard ». Bloquez‑le maintenant, puis ajustez les adresses de liaison.
Tâche 10 : Vérifier la pression sur conntrack (le réseau hôte coïncide souvent avec du trafic élevé)
cr0x@server:~$ sudo conntrack -S
cpu=0 found=120938 invalid=12 ignore=0 insert=0 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0
Ce que cela signifie : invalid et drops peuvent indiquer une surcharge ou des problèmes de routage asymétrique. Les setups NAT‑lourds stressent conntrack ; le réseau hôte peut réduire le NAT mais n’élimine pas magiquement la pression d’état.
Décision : Si vous voyez des drops/early_drops croissants pendant les incidents, vous regardez un goulot d’état du noyau. Envisagez de réduire le NAT, d’ajuster les limites de conntrack, ou de repenser le flux de trafic.
Tâche 11 : Regarder le résumé des sockets (épuisement de ports et tempêtes TIME‑WAIT)
cr0x@server:~$ ss -s
Total: 31234 (kernel 0)
TCP: 19872 (estab 2412, closed 16011, orphaned 3, timewait 15890)
Transport Total IP IPv6
RAW 0 0 0
UDP 412 356 56
TCP 3861 3312 549
INET 4273 3668 605
FRAG 0 0 0
Ce que cela signifie : De grands comptes timewait sous charge peuvent signaler un fort turnover de connexions. Le réseau hôte fait partager à votre conteneur l’espace de ports éphémères et les limites du cycle TCP de l’hôte avec tout le reste.
Décision : Si TIME‑WAIT domine, corrigez d’abord la réutilisation des connexions et le pooling. Ensuite seulement envisagez un tuning du noyau — et soyez prudent, car le tuning peut masquer des bugs jusqu’à leur explosion.
Tâche 12 : Confirmer la plage de ports éphémères (ressource partagée, souvent négligée)
cr0x@server:~$ sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999
Ce que cela signifie : C’est la plage pour les ports éphémères sortants. Tous les workloads en réseau hôte la partagent.
Décision : Si vous exécutez beaucoup de clients à fort turnover sur le même hôte, envisagez d’élargir la plage et de réduire le churn. Mieux : isolez les workloads ou évitez le réseau hôte pour les clients bavards.
Tâche 13 : Identifier quel conteneur possède un port ouvert suspect à l’aide de lsof
cr0x@server:~$ sudo lsof -iTCP:8080 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 4128 root 112u IPv6 65741 0t0 TCP *:http-alt (LISTEN)
Ce que cela signifie : Le processus se lie sur toutes les interfaces (*). Vous devez maintenant mapper le PID au conteneur via le cgroup comme dans la Tâche 4.
Décision : Si ce n’est pas censé être public, soit liez‑le à une adresse spécifique, soit bloquez l’ingress au pare‑feu hôte immédiatement.
Tâche 14 : Vérifier que Docker n’a rien exposé silencieusement via des ports publiés (comparaison des cgroups)
cr0x@server:~$ docker port api-1
8080/tcp -> 127.0.0.1:18080
Ce que cela signifie : Le conteneur en mode bridge est explicitement lié à localhost côté hôte. C’est la sécurité que vous perdez en mode hôte.
Décision : Si vous comptiez sur des bindings 127.0.0.1 pour la sécurité, le réseau hôte est incompatible à moins de recréer cette sécurité via des binds applicatifs et le pare‑feu.
Tâche 15 : Vérifier si le conteneur a des capacités réseau supplémentaires (host networking plus NET_ADMIN, c’est épicé)
cr0x@server:~$ docker inspect -f '{{.HostConfig.CapAdd}}' dns-forwarder
[NET_ADMIN NET_RAW]
Ce que cela signifie : Le conteneur peut manipuler le réseau et forger des paquets. En mode réseau hôte, cela peut affecter directement l’hôte.
Décision : Traitez‑le comme à haut risque. Si vous avez besoin de NET_ADMIN en mode hôte, isolez‑le sur un nœud dédié et renforcez l’audit.
Tâche 16 : Valider le chemin de résolution de noms (le réseau hôte hérite du /etc/resolv.conf de l’hôte)
cr0x@server:~$ cat /etc/resolv.conf
nameserver 10.20.4.53
search corp.example
Ce que cela signifie : Les conteneurs en mode hôte partagent souvent la config de résolveur de l’hôte, qui peut changer lors de renouvellements DHCP ou de sessions VPN.
Décision : Si la stabilité DNS compte, figez la configuration DNS explicitement (configuration systemd‑resolved, gestion statique de resolv.conf, ou configurations de résolveur au niveau applicatif).
Mode d’intervention rapide : quoi vérifier en premier/deuxième/troisième
Voici la séquence « ne paniquez pas, juste triez » quand un conteneur en réseau hôte est impliqué et que le trafic échoue ou que les performances chutent.
Premier : confirmer ce qui écoute réellement et où
- Exécutez
sudo ss -lntup. Identifiez les écouteurs inattendus et si ils se lient à0.0.0.0, une IP spécifique, ou127.0.0.1. - Mapper les PIDs suspects aux conteneurs en utilisant
ps -o cgroupoucat /proc/<pid>/cgroup.
Objectif : Déterminer si l’échec est simplement un « mauvais bind » ou un « conflit de port ». Ce sont des corrections rapides.
Second : vérifier l’atteignabilité depuis le bon endroit
- Depuis un hôte pair dans le même réseau, testez avec
nc -vz <ip> <port>. - Si c’est de l’UDP, utilisez
nc -vzuou des probes applicatives, et vérifiez avec des compteurs de paquets (ip -s link).
Objectif : Décider si c’est un problème de processus local ou un problème de chemin réseau / pare‑feu.
Troisième : vérifier le pare‑feu hôte et le policy routing
- Inspectez les règles INPUT (
sudo iptables -Sou règles nft si applicable). - Confirmez les interfaces et les routes (
ip -br addr,ip route). - Si vous utilisez du policy routing, vérifiez les règles (
ip rule) et les tables pertinentes (ip route show table <n>).
Objectif : S’assurer que vous n’avez pas accidentellement exposé ou bloqué le service en changeant le modèle d’espace de noms.
Quatrième : chercher un épuisement de ressources partagées de l’hôte
ss -spour les tempêtes TIME‑WAIT et les comptes établis.sysctl net.ipv4.ip_local_port_rangeet réglages TCP généraux si vous suspectez un épuisement de ports éphémères.conntrack -Ssi des tables d’état NAT sont impliquées quelque part dans le chemin.
Objectif : Identifier si le réseau hôte vous a déplacé vers un goulot partagé (ports éphémères, conntrack, CPU softirq).
Cinquième : vérifier softirq CPU et saturation NIC (la couche « c’est le noyau »)
mpstat -P ALL 1outoppour voir la saturation CPU.cat /proc/softirqsetsar -n DEV 1(si disponible) pour voir la pression des interruptions réseau.
Objectif : Confirmer si le prétendu « overhead Docker » était en réalité de la contention CPU / interruptions.
Erreurs courantes : symptôme → cause racine → correctif
1) « Le service est accessible sur Internet »
Symptôme : Un scan de sécurité trouve un nouveau port ouvert ; les logs d’accès montrent des IP aléatoires.
Cause racine : Conteneur en réseau hôte qui se lie à 0.0.0.0 ; la politique INPUT de l’hôte le permet.
Correctif : Lier explicitement à l’interface/IP prévue ; appliquer une politique INPUT deny‑by‑default et permettre seulement les plages/ports sources nécessaires.
2) « Deux conteneurs ne peuvent plus démarrer sur le même nœud »
Symptôme : Une instance échoue avec « address already in use ».
Cause racine : Le réseau hôte supprime le mappage de ports ; vous avez un vrai conflit de ports.
Correctif : Attribuer des ports uniques par instance, ou cesser d’utiliser le mode host. Dans l’orchestration, imposer une anti‑affinité pour qu’une seule instance tombe par nœud.
3) « Les appels localhost atteignent la mauvaise cible »
Symptôme : Un conteneur tente d’appeler 127.0.0.1:xxxx en s’attendant à un sidecar, mais atteint un service hôte (ou rien).
Cause racine : En mode réseau hôte, le loopback est le loopback de l’hôte.
Correctif : Utiliser une découverte de service explicite et des ports dédiés, ou garder les sidecars dans un espace de noms réseau partagé de conteneur avec le networking bridge. Ne plus supposer que localhost signifie « ce conteneur ».
4) « Après le passage au réseau hôte, les connexions sortantes échouent par intermittence »
Symptôme : Échecs de connexion en rafales ; beaucoup de TIME‑WAIT ; erreurs comme « cannot assign requested address ».
Cause racine : Épuisement des ports éphémères ou churn de connexions sur l’hôte, partagé entre les workloads.
Correctif : Réduire le churn de connexions (keep‑alives, pooling) ; envisager d’élargir la plage éphémère ; distribuer les workloads ; reconsidérer le mode host pour les clients à fort churn.
5) « Des règles de pare‑feu qui marchaient ne s’appliquent plus »
Symptôme : Les règles DOCKER-USER ne bloquent pas le trafic vers un conteneur en réseau hôte.
Cause racine : Le réseau hôte contourne les chaînes bridge Docker ; le trafic arrive directement dans INPUT.
Correctif : Implémenter la politique dans les chaînes INPUT/OUTPUT de l’hôte (iptables/nft), pas seulement dans les chaînes spécifiques à Docker.
6) « La supervision montre de mauvaises IP sources »
Symptôme : Les logs montrent l’IP de l’hôte comme source ; l’attribution casse ; les ACL échouent.
Cause racine : Le réseau hôte écrase l’identité du conteneur sur la couche L3 ; l’IP source devient celle de l’interface de l’hôte.
Correctif : Utiliser des identités mTLS, des en‑têtes explicites avec prudence, ou des espaces de noms réseau séparés lorsque l’identité est requise. Ne construisez pas d’ACL en supposant des IP par conteneur si vous utilisez le mode host.
7) « Le trafic contourne le proxy/contrôle d’egress attendu »
Symptôme : Le conteneur rejoint des réseaux qu’il ne devrait pas ; les logs d’egress sont incomplets.
Cause racine : Le routage et la configuration du résolveur de l’hôte s’appliquent ; les contrôles d’egress basés sur les namespaces ne sont plus efficaces.
Correctif : Faire respecter l’egress au niveau du pare‑feu hôte, ou déplacer le workload hors du réseau hôte et dans un chemin d’egress contrôlé.
Comment limiter les dégâts quand vous devez utiliser le réseau hôte
Si vous choisissez le réseau hôte, vous devez à votre futur vous quelques mesures de confinement. Ce n’est pas de la paranoïa. C’est de l’hygiène opérationnelle basique.
1) Placer les workloads en réseau hôte sur des nœuds dédiés
L’isolation par ordonnancement n’est pas un substitut aux namespaces, mais c’est un limiteur efficace de rayon d’impact. Si le conteneur partage le réseau de l’hôte, au moins qu’il ne partage pas l’hôte avec tout le reste.
2) Deny‑by‑default sur INPUT, puis allowlist
Le réseau hôte transforme chaque écouteur en écouteur hôte. Votre pare‑feu hôte doit être ennuyeux et strict :
- DROP par défaut sur INPUT.
- Autoriser established/related.
- Autoriser SSH depuis les réseaux de gestion seulement.
- Autoriser les ports spécifiques du service depuis des plages sources spécifiques.
Faites‑le avec vos outils de pare‑feu standard, pas avec des commandes tapées au hasard sur des nœuds. La dérive est ce qui vous perd.
3) Lier à des interfaces explicites
Privilégiez la liaison à une IP spécifique plutôt que 0.0.0.0. Si vous avez besoin d’une exposition multi‑interface, nommez‑la explicitement dans la config. Si l’application ne supporte pas des contrôles de bind raisonnables, c’est un problème produit, pas un problème ops.
4) Retirer les capacités agressivement
Le réseau hôte n’exige pas NET_ADMIN dans la plupart des cas. Si vous n’en avez pas besoin, ne l’expédiez pas. Idem pour NET_RAW. Vous n’êtes pas en train de construire un forgeur de paquets ; vous exécutez un service.
5) Utiliser des systèmes de fichiers en lecture seule et des montages minimaux
Ce n’est pas spécifique au réseau, mais c’est important : si le conteneur est dans le netns de l’hôte et a aussi un accès en écriture à des chemins sensibles de l’hôte, vous empilez les risques. Utilisez rootfs en lecture seule quand c’est possible et des bind mounts minimaux.
6) Rendre la propriété des ports explicite dans le code et en ops
Définissez les ports en un seul endroit. Documentez‑les. Alertez quand ils changent. Pour les conteneurs en réseau hôte, la « dérive de ports » est un risque opérationnel et un risque de sécurité.
7) Préférer les conteneurs rootless quand c’est possible, mais en comprendre les limites
Docker rootless peut réduire l’impact d’une fuite de conteneur, mais le réseau hôte et rootless ne se mélangent pas toujours proprement selon l’environnement. Ne supposez pas que rootless rend le réseau hôte « sûr ». Il le rend plus sûr sur certaines dimensions, pas toutes.
8) Ne pas utiliser le réseau hôte comme raccourci pour un DNS/service discovery cassé
Si la raison de recourir au mode hôte est « le conteneur ne peut pas atteindre X », corrigez le routage, le pare‑feu, ou la découverte de service. Le mode hôte n’est pas un thérapeute réseau.
Listes de contrôle / plan étape par étape
Plan A : Décider si le réseau hôte est justifié
- Écrivez l’objectif. « Réduire la latence » n’est pas un objectif. « Réduire le p99 de 2 ms sous 10k rps » est un objectif.
- Mesurer la base. Capturez latence, CPU, softirq, paquets perdus, statistiques conntrack.
- Essayez d’abord des alternatives plus sûres. Utilisez le networking bridge avec MTU réglé, une CNI appropriée, ou des ports hôtes avec binds stricts ; corrigez le churn côté appli.
- Lancez un canari sur des nœuds dédiés. Ne mélangez pas avec d’autres workloads au début.
- Définissez les attentes de port/bind. Ports exacts, protocoles, adresses de liaison et plages sources autorisées.
- Prévoyez un rollback. Si il faut plus d’un deploy pour revenir en arrière, vous jouez.
Plan B : Si vous exécutez déjà le réseau hôte, réduire le risque sans réécriture
- Inventoriez tous les conteneurs en réseau hôte avec
docker psetdocker inspect. - Prendre des instantanés des écouteurs avec
ss -lntupet enregistrer attendu vs réel. - Mettre en place deny‑by‑default INPUT et des règles explicites pour les ports requis.
- Geler les adresses de liaison dans les configs applicatives ; éviter
0.0.0.0sauf si nécessaire. - Retirer les capacités inutiles et exécuter en non‑root si possible.
- Ajouter de la détection : alerter sur de nouveaux écouteurs et sur la dérive de la politique du pare‑feu.
Plan C : Migrer hors du réseau hôte (plan « rembourser la dette »)
- Identifier pourquoi le mode host a été choisi. Performance ? Accessibilité ? Problème de mappage de ports ?
- Reproduire le problème original dans un environnement contrôlé.
- Basculer vers bridge ou réseau routé avec des ports publiés explicites, ou une CNI qui fournit des IPs conteneur routables.
- Re‑tester les performances et vérifier que vous n’avez pas régressé en corrigeant la mauvaise chose.
- Retirer les hypothèses spécifiques à l’hôte (noms d’interfaces, ports codés en dur, dépendances localhost).
- Déployer progressivement et garder le pare‑feu hôte strict pendant la migration.
FAQ
1) Le réseau hôte Docker est‑il « insecure » par défaut ?
C’est moins isolé par conception. Si votre pare‑feu hôte est strict et que vos services lient explicitement, vous pouvez l’exécuter en toute sécurité. Si votre pare‑feu est permissif et que vos applis se lient par défaut à 0.0.0.0, c’est un piège tendu.
2) --network host rend‑il les conteneurs plus rapides ?
Parfois. Il peut supprimer l’overhead bridge/NAT et simplifier les chemins de paquets. Dans de nombreux systèmes réels, le goulot est ailleurs. Mesurez avant de décider, et mesurez encore après.
3) Puis‑je encore utiliser la publication de ports -p avec le réseau hôte ?
Non. Il n’y a rien à publier. Le conteneur se lie directement sur l’hôte. Votre « mappage de ports » devient « ce que fait le processus ».
4) Pourquoi mes règles de pare‑feu dans DOCKER-USER ne bloquent‑elles pas les conteneurs en réseau hôte ?
Parce que le trafic en réseau hôte ne traverse pas les chaînes bridge Docker comme vous l’attendez. Traitez‑le comme tout autre processus hôte : appliquez la politique dans INPUT/OUTPUT (iptables/nftables) et/ou sur les pare‑feux en amont.
5) Qu’en est‑il de Kubernetes hostNetwork: true ?
Mêmes compromis fondamentaux : vous partagez l’espace de noms réseau du nœud. Vous gagnez en performance et simplicité dans certains cas, et vous perdez l’isolation réseau. Les mesures d’atténuation (nœuds dédiés, pare‑feu nœud strict, binds explicites) s’appliquent toujours.
6) Le réseau hôte contourne‑t‑il toutes les isolations des conteneurs ?
Non. Il contourne l’isolation d’espace de noms réseau. Vous avez toujours d’autres frontières : espaces de noms de montage, cgroups, profils seccomp et capacités (si configurés correctement). Mais perdre netns est une réduction significative en défense en profondeur.
7) Comment savoir quel conteneur a ouvert un port en utilisant le réseau hôte ?
Utilisez ss -lntup pour obtenir le PID, puis mappez le PID au conteneur via les cgroups (ps -o cgroup) ou les métadonnées Docker. Construisez un runbook, car vous en aurez besoin sous pression.
8) Quelle est la posture « par défaut » la plus sûre pour la production ?
Évitez le réseau hôte sauf si vous pouvez articuler un bénéfice mesuré ou une exigence fonctionnelle. Si vous devez l’utiliser, placez ces conteneurs sur des nœuds dédiés avec un pare‑feu hôte deny‑by‑default et des adresses de liaison explicites.
9) Si je ne me lie qu’à 127.0.0.1, suis‑je en sécurité ?
Vous êtes plus en sécurité, mais pas universellement. Localhost réduit l’exposition réseau, mais vous devez toujours considérer qui peut atteindre l’hôte (utilisateurs SSH, autres processus locaux et tout chemin d’escalade de privilèges local). De plus, en réseau hôte, les conteneurs partagent ce localhost avec l’hôte.
10) Docker rootless suffit‑il à rendre le réseau hôte acceptable ?
Rootless aide à réduire certains impacts d’évasion, mais ça ne résout pas le problème fondamental : les écouteurs sont sur le réseau hôte, et la politique de pare‑feu est la vraie frontière. Traitez‑le comme une atténuation partielle, pas comme un laissez‑passer.
Conclusion : prochaines étapes pratiques
Si vous retenez une chose : le réseau hôte n’est pas « juste un mode réseau ». C’est une décision qui effondre une frontière. Cela peut être intelligent, mais cela ne doit jamais être fait à la légère.
- Inventoriez les conteneurs en réseau hôte aujourd’hui et notez les écouteurs prévus pour chacun.
- Exécutez
ss -lntupsur les hôtes et réconciliez « prévu » vs « réel ». Corrigez les surprises. - Serrurez la politique INPUT de l’hôte pour que des écouteurs accidentels ne deviennent pas des incidents externes.
- Isolez les workloads en réseau hôte sur des nœuds dédiés quand c’est possible.
- Mesurez les bénéfices de performance avant d’utiliser le mode hôte comme optimisation globale.
- Automatisez la détection : alertez sur de nouveaux écouteurs et la dérive des règles de pare‑feu, car les humains oublient et le code pousse des valeurs par défaut.
Le réseau hôte peut être la bonne option. Faites‑en simplement une décision d’ingénierie, pas une tendance.