Netzwerkisolation zwischen Compose-Stacks durchsetzen
Netzwerkisolation zwischen Compose-Stacks durchsetzen
Verschiedene Compose-Stacks auf demselben Host können sich standardmäßig gegenseitig erreichen — das ist oft unerwünscht.
Das Problem: Default-Isolation existiert nicht
Compose erstellt pro Stack ein eigenes Netzwerk (stackname_default). Auf den ersten Blick sieht das nach Isolation aus — aber Container auf unterschiedlichen Compose-Netzwerken können sich dennoch erreichen, wenn sie im selben Docker-Subnetz liegen oder auf dasselbe externe Netzwerk zugreifen.
Der Docker-Default-Bridge (docker0) erlaubt Kommunikation zwischen allen Containern die kein explizites Netzwerk haben. User-defined Networks isolieren besser, aber nur wenn man die Konfiguration versteht.
Isolation testen
# Kann Stack A auf Stack B zugreifen?
docker exec appA curl http://appB:80
# Oder per IP direkt
docker inspect appB --format '{{.NetworkSettings.Networks}}'
docker exec appA curl http://172.20.0.5:80
Saubere Isolation mit internen Netzwerken
Interne Netzwerke (kein Außenzugang)
# Stack A: backend-app
services:
app:
image: myapp
networks:
- internal
- dmz # nur für Reverse-Proxy-Zugang
database:
image: postgres:15
networks:
- internal # nur intern erreichbar, nicht von außen
networks:
internal:
internal: true # kein Traffic raus, kein Traffic rein von außen
dmz:
external: true # geteilt mit Reverse Proxy
# Stack B: anderer Stack
services:
otherapp:
image: otherapp
networks:
- internal # eigenes internes Netzwerk, anderer Name → kein Overlap
- dmz
networks:
internal:
internal: true
dmz:
external: true
Da beide Stacks internal: true-Netzwerke haben, die nicht geteilt werden, können sie sich nicht gegenseitig erreichen. Der Reverse Proxy (Caddy, Traefik) ist das einzige, das im gemeinsamen dmz-Netzwerk sitzt.
Gemeinsames Netzwerk nur für Reverse Proxy
# Einmalig das DMZ-Netzwerk anlegen
docker network create dmz
# Caddy / Reverse Proxy Stack
services:
caddy:
image: caddy:latest
networks:
- dmz
networks:
dmz:
external: true
Nur der Reverse Proxy darf in beide Netzwerke — Backend-Services sind nur über ihn erreichbar.
Isolation verifizieren
# Sollte fehlschlagen wenn korrekt isoliert:
docker exec appA curl http://appB:80
# → curl: (6) Could not resolve host: appB
# Direkter IP-Test (DNS umgehen)
IPAPP_B=$(docker inspect appB --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
docker exec appA curl http://$IPAPP_B:80
# → curl: (7) Failed to connect
Docker Default Bridge abschalten
Wenn Container ohne --network gestartet werden, landen sie auf der Default Bridge und können sich alle erreichen. Lösung: Default Bridge in daemon.json deaktivieren (fortgeschritten, bricht bestehende Setups):
{
"bridge": "none"
}
Realistischer: konsequent user-defined Networks in jedem Compose-Stack verwenden und network_mode: bridge nie explizit setzen.
Checkliste für Isolation
- [ ] Jeder Stack hat eigene
internal: true-Netzwerke für Backend-Services - [ ] Datenbanken sind nicht im gemeinsamen Netzwerk
- [ ] Nur Reverse Proxy hat Zugang zum gemeinsamen DMZ-Netzwerk
- [ ] Ports werden nicht unnötig nach außen gemappt (
ports:weglassen für interne Services) - [ ] Isolation mit
curl-Test verifiziert
Zusammenfassung
Echte Isolation zwischen Compose-Stacks erfordert bewusste Netzwerk-Konfiguration: internal: true für Backend-Netzwerke, ein gemeinsames externes Netzwerk nur für den Reverse Proxy, und konsequenter Verzicht auf unnötige Port-Mappings.