infra / docker-compose
Réseaux personnalisés
Explication
Ce que vous allez apprendre
- Comprendre le réseau bridge par défaut que Compose crée automatiquement pour un projet
- Créer des réseaux personnalisés pour isoler des groupes de services entre eux
- Utiliser la résolution DNS interne : contacter un service par son nom, jamais par son IP
- Bloquer tout accès Internet sortant d'un réseau sensible avec
internal: true - Connecter un service à un réseau déjà créé par un autre projet Compose (
external: true)
Dans quel contexte ?
Sur la stack boutique-api, le service frontend, exposé publiquement sur Internet, n'a strictement aucune raison de pouvoir contacter directement la base de données db qui contient les informations de paiement des clients. Pourtant, avec la configuration réseau par défaut de Compose, c'est exactement ce qui se passe : tous les services d'un même projet se voient et se parlent librement. Cette leçon corrige ce problème avec des réseaux séparés.
Le comportement par défaut de Compose
D'abord, il faut savoir ce que fait Compose sans aucune configuration réseau particulière : il place tous les services d'un projet sur un même réseau, où ils se contactent par leur nom, comme un carnet d'adresses interne automatique.
Pratique, mais parfois trop permissif
C'est pratique pour commencer, mais ça pose un problème de sécurité : une base de données n'a par exemple aucune raison d'être joignable directement depuis un service "frontend" exposé au public. Tout le monde parle à tout le monde, sans distinction.
L'idée : segmenter comme des VLAN
La solution consiste à créer des réseaux personnalisés, un peu comme des VLAN séparés dans un réseau d'entreprise. Chaque groupe de machines ne voit alors que ce dont il a réellement besoin, et rien de plus.
Comment un service en trouve un autre
Une fois les réseaux définis, il reste à comprendre comment les services se trouvent entre eux. Chaque service connecté à un réseau Compose reçoit automatiquement un enregistrement DNS interne portant son nom. Le service api peut donc contacter la base simplement en visant l'hôte db, sans connaître son adresse IP, qui peut de toute façon changer à chaque redémarrage.
Un service absent d'un réseau ne le voit pas
Il faut bien retenir une règle simple : un service qui n'est PAS connecté à un réseau donné ne peut tout simplement pas contacter les services de ce réseau, même s'ils tournent sur la même machine hôte. C'est exactement l'effet d'isolation recherché.
Bonne pratique
Donne toujours un rôle clair à chaque réseau plutôt que de tout regrouper : un public_net pour ce qui doit être joignable de l'extérieur, un private_net pour ce qui ne doit parler qu'en interne. Un service critique comme une base de données ne devrait jamais figurer sur le même réseau qu'un service exposé publiquement.
Aller plus loin : internal et external
Deux options méritent d'être connues. Un réseau marqué internal: true empêche tout trafic sortant vers Internet — utile pour un réseau de base de données qui n'a besoin de parler qu'aux autres services internes. À l'inverse, external: true permet de rejoindre un réseau déjà créé ailleurs, par exemple partagé entre plusieurs projets Compose, comme un reverse proxy central.
Le piège à éviter
Le piège le plus courant reste de mettre tous les services sur un seul réseau "par défaut" sans jamais se demander qui doit vraiment parler à qui. Cela fonctionne, mais annule tout le bénéfice de sécurité de la segmentation.
| Type de réseau | Déclaration | Effet |
|---|---|---|
| Bridge par défaut | Aucune (implicite) | Tous les services du projet se voient entre eux |
| Réseau custom | networks: au niveau service et racine | Isole des groupes de services |
internal: true | Sur le réseau racine | Aucun accès sortant vers Internet |
external: true | Sur le réseau racine | Rejoint un réseau créé par un autre projet |
Maintenant que la stack est isolée proprement au niveau réseau, la leçon suivante aborde un besoin différent : activer ou désactiver certains services selon le contexte, avec les profiles.
Commandes & code
Réseaux Compose
Par défaut, Compose crée un réseau bridge unique où tous les services se résolvent par leur nom. Des réseaux custom permettent d'isoler des groupes de services.
services:
frontend:
image: myorg/frontend:latest
networks:
- public_net
api:
build: ./api
networks:
- public_net # joignable depuis "frontend"
- private_net # joignable depuis "db" uniquement
db:
image: postgres:16
networks:
- private_net # PAS accessible depuis "frontend" -> isolation
networks:
public_net:
private_net:
internal: true # aucun accès sortant vers Internet depuis ce réseau# Alias réseau et sous-réseau custom
services:
api:
build: ./api
networks:
backend:
aliases:
- api.internal
ipv4_address: 172.28.0.10
networks:
backend:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16# Lister les réseaux créés par le projet
docker network ls
# Inspecter : voir les conteneurs connectés, IP, sous-réseau
docker network inspect docker-compose_public_net
# Tester la résolution DNS interne entre services
docker compose exec api ping -c 2 db
docker compose exec api nslookup db# Rejoindre un réseau externe déjà créé (ex: partagé entre plusieurs stacks Compose)
networks:
shared_proxy:
external: true
name: traefik_proxyRésumé
- Chaque service voit les autres par leur NOM DE SERVICE comme hostname.
internal: truebloque tout accès Internet sortant depuis ce réseau.aliasesdonne des noms DNS supplémentaires à un service sur un réseau.external: trueconnecte à un réseau créé hors de ce fichier Compose.
Exercices pratiques
Mission : le frontend qui parle directement à la base de paiement
Objectif : Segmenter une stack en réseaux isolés et repérer un service qui reste un point de pivot.
Contexte
Sur boutique-api, aucune configuration réseau particulière n'a été ajoutée : frontend (exposé publiquement), api et db (qui contient les données de paiement) tournent tous sur le même réseau bridge par défaut. Un audit signale que frontend peut contacter db directement en visant simplement l'hôte db.
Segmente la stack et évalue ce que cette segmentation protège réellement.