Netzwerkzugriff von Containern nach außen einschränken
Netzwerkzugriff von Containern nach außen einschränken
Standardmäßig können Container frei ins Internet kommunizieren — das ist für Datenbanken und interne Services oft unnötig und ein Sicherheitsrisiko.
Das Standard-Verhalten
docker run --rm alpine wget -qO- https://example.com # Funktioniert problemlos
- Jeder Container kann standardmäßig beliebige externe Verbindungen aufbauen. Das ist ein Problem wenn:
- Eine Datenbank Daten nach außen exfiltrieren könnte
- Kompromittierte Container Command-and-Control-Server kontaktieren
- Software in Containern unerwünscht Telemetrie sendet
Lösung 1: Internes Netzwerk
networks:
backend:
internal: true # Kein Internet-Zugriff aus diesem Netz
services:
db:
image: postgres:16
networks:
- backend # Nur interner Zugriff, kein Internet
cache:
image: redis
networks:
- backend
app:
image: myapp
networks:
- backend
- frontend # Frontend-Netz HAT Internet-Zugriff (für externe APIs)
networks:
backend:
internal: true
frontend: # Standard: Internet-Zugriff erlaubt
Container im backend-Netz können sich gegenseitig erreichen, aber keine externen Verbindungen aufbauen. Die App ist in beiden Netzen und kann externe Services aufrufen.
Testen
# Sollte fehlschlagen
docker compose exec db wget -qO- https://example.com
# wget: bad address 'example.com'
# Sollte funktionieren
docker compose exec app wget -qO- https://api.example.com
Lösung 2: Outbound-Proxy
Wenn Container kontrollierten Internet-Zugriff brauchen (nur bestimmte Domains), via HTTP-Proxy routen:
services:
app:
image: myapp
environment:
HTTP_PROXY: http://squid:3128
HTTPS_PROXY: http://squid:3128
NO_PROXY: db,cache,localhost
squid:
image: ubuntu/squid
volumes:
- ./squid.conf:/etc/squid/squid.conf:ro
networks:
- proxy-net
- internet
networks:
proxy-net:
internal: true
internet:
Squid als Proxy erlaubt nur bestimmte Ziel-Domains:
# squid.conf
acl allowed_domains dstdomain .example.com .api.stripe.com
http_access allow allowed_domains
http_access deny all
Lösung 3: DNS-basierte Einschränkung
Eigener DNS-Server, der nur bestimmte Domains auflöst:
services:
app:
image: myapp
dns:
- 172.20.0.10 # Interner DNS-Server
dns:
image: coredns/coredns
networks:
backend:
ipv4_address: 172.20.0.10
volumes:
- ./Corefile:/Corefile:ro
DNS-basiertes Blocking ist einfach zu umgehen (direkte IP-Verbindungen) und daher nur als ergänzende Maßnahme geeignet.
Praxisbeispiele
Datenbank komplett isolieren:
services:
db:
image: postgres:16
networks:
- db-net
networks:
db-net:
internal: true
Air-gapped Setup (keine externen Verbindungen überhaupt):
networks:
default:
internal: true
Alle Services im Stack bekommen kein Internet — nützlich für isolierte Test-Umgebungen.
Was internal: true nicht abdeckt
internal: trueverhindert Internet-Zugriff durch Entfernen der Default-Gateway-Route. Es verhindert nicht:- Kommunikation zwischen Containern im selben Netz
- Zugriff auf den Docker-Host selbst (über die Bridge-IP)
- Direkten Zugriff über Host-Netzwerk (
network_mode: host)
Docker-eigene iptables-Regeln
Docker verwaltet iptables-Regeln selbst. Eigene iptables-Regeln können mit Docker in Konflikt geraten:
# Docker überschreibt FORWARD-Chain — eigene Rules in DOCKER-USER setzen
iptables -I DOCKER-USER -j DROP # Blockiert alle Container-zu-Container-Verbindungen
Für komplexes Traffic-Shaping ist internal: true mit einem Proxy-Container die robustere Lösung als direkte iptables-Manipulation.
Die einfachste und zuverlässigste Methode bleibt internal: true in Compose — kein Extra-Tool, kein iptables-Wissen nötig, und für die meisten Anwendungsfälle ausreichend.