CNI und NetworkPolicies
Pod-Netzwerk und Firewall-Regeln.
Lernziele
Nach dieser Lektion kannst du erklären, was das Kubernetes-Netzwerkmodell verlangt und warum es ein CNI-Plugin braucht, um es umzusetzen. Du kannst Calico, Cilium und Flannel grob einordnen, verstehst, wie NetworkPolicies aus einem offenen Cluster-Netz ein segmentiertes machen, und kennst das Default-Deny-Muster samt seiner Fallstricke — inklusive der DNS-Falle, in die fast jeder einmal läuft.
Das Netzwerkmodell: jeder Pod eine IP, keine NAT dazwischen
Kubernetes definiert ein bewusst einfaches Netzwerkmodell: Jeder Pod bekommt eine eigene IP aus dem Pod-CIDR, und jeder Pod kann jeden anderen Pod im Cluster direkt erreichen — über Node-Grenzen hinweg, ohne NAT. Anwendungen müssen sich nicht um Port-Mapping kümmern wie bei klassischem Docker; was der Pod auf Port 8080 bindet, ist unter seiner IP auf Port 8080 erreichbar.
Das Modell ist eine Anforderung, keine Implementierung. Kubernetes selbst baut kein Pod-Netzwerk — es delegiert das über das Container Network Interface (CNI): eine schmale Spezifikation, nach der das kubelet beim Pod-Start ein Plugin-Binary aufruft („gib diesem Pod ein Netzwerk-Interface und eine IP”) und beim Löschen wieder („räum auf”). Wie die Pakete dann zwischen Nodes wandern — geroutet, getunnelt, per eBPF — ist Sache des Plugins. Deshalb ist ein Cluster ohne CNI-Plugin auch nicht kaputt, sondern unfertig: Nodes bleiben NotReady, Pods hängen in ContainerCreating.
Calico, Cilium, Flannel: dieselbe Aufgabe, drei Philosophien
Die drei meistgenannten CNI-Plugins lösen dieselbe Aufgabe unterschiedlich:
Flannel ist die Minimallösung: Es spannt typischerweise ein VXLAN-Overlay auf — Pod-Pakete werden in UDP-Pakete zwischen den Nodes verpackt. Einfach zu betreiben, läuft fast überall. Aber: Flannel implementiert keine NetworkPolicies. Ein reiner Flannel-Cluster ignoriert deine Policy-Manifeste kommentarlos — sie werden angelegt, erzwingen aber nichts. Eine beliebte Prüfungsfalle.
Calico setzt klassisch auf Layer-3-Routing (oft mit BGP) statt Overlay, was Tunneling-Overhead spart, und bringt eine ausgereifte Policy-Engine mit — inklusive eigener CRDs, die mehr können als die Standard-NetworkPolicy (z. B. clusterweite Policies und explizite Deny-Regeln).
Cilium ist eBPF-basiert: Statt iptables-Ketten lädt es Programme direkt in den Kernel. Das skaliert besser, kann kube-proxy komplett ersetzen und versteht L7-Policies — also Regeln wie „nur GET /api/v1/* erlauben” statt nur „Port 8080 erlauben”. Mit Hubble liefert es Observability für jeden Flow. Der Preis: mehr bewegliche Teile, ein aktueller Kernel und mehr Know-how im Team.
Faustregel für Interviews: Flannel = einfach, ohne Policies; Calico = solide Routing- und Policy-Standardwahl; Cilium = eBPF, L7, Observability — wenn das Team es betreiben kann.
NetworkPolicies: vom Alles-darf-mit-allem zum Zonenmodell
Das offene Netzwerkmodell ist betrieblich bequem und sicherheitstechnisch ein Problem: Per Default darf jeder Pod mit jedem reden. Kompromittiert ein Angreifer einen unwichtigen Frontend-Pod, kann er von dort die Datenbank im Nachbar-Namespace direkt ansprechen — lateral movement ohne Widerstand.
Eine NetworkPolicy ist die Firewall-Regel dagegen, deklarativ und label-basiert: Sie wählt per podSelector Pods aus und definiert, welcher Ingress (eingehend) und Egress (ausgehend) Traffic erlaubt ist — nach Pod-Labels, Namespace-Labels oder IP-Blöcken, plus Ports.
Die entscheidende Semantik: Policies sind additiv und allow-only. Solange keine Policy einen Pod selektiert, ist alles erlaubt. Sobald irgendeine Policy ihn für eine Richtung selektiert, ist in dieser Richtung alles verboten außer dem explizit Erlaubten. Es gibt keine Deny-Regeln und keine Prioritäten im Standard — mehrere Policies vereinigen sich einfach.
Daraus folgt das empfohlene Muster Default-Deny: pro Namespace eine Policy, die alle Pods selektiert und nichts erlaubt — danach öffnest du gezielt, was die Anwendung braucht. Wichtig: Policies wirken pro Namespace, und durchgesetzt werden sie vom CNI-Plugin, nicht vom API-Server.
Praxis
kind mit Calico, dann Default-Deny erleben (Standard-kind-CNI kindnet kann inzwischen Policies, aber Calico ist der realistischere Stack):
# Cluster ohne Default-CNI, dann Calico installieren
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
disableDefaultCNI: true
podSubnet: 192.168.0.0/16
EOF
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
kubectl -n kube-system rollout status ds/calico-node
# Zielanwendung und Test-Client
kubectl create deployment web --image=nginx
kubectl expose deployment web --port=80
kubectl run client --rm -it --image=busybox:1.36 --restart=Never -- \
wget -qO- --timeout=2 http://web # funktioniert noch
# default-deny-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes: ["Ingress"]
---
# allow-client-to-web.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-client-to-web
spec:
podSelector:
matchLabels:
app: web
policyTypes: ["Ingress"]
ingress:
- from:
- podSelector:
matchLabels:
run: client
ports:
- port: 80
kubectl apply -f default-deny-ingress.yaml
kubectl run client --rm -it --image=busybox:1.36 --restart=Never -- \
wget -qO- --timeout=2 http://web # Timeout — Deny wirkt
kubectl apply -f allow-client-to-web.yaml
kubectl run client --rm -it --image=busybox:1.36 --restart=Never -- \
wget -qO- --timeout=2 http://web # geht wieder
Der Timeout-Moment dazwischen ist der Lerneffekt: genau so sieht eine greifende Policy von innen aus — kein „connection refused”, sondern stilles Verwerfen.
Typische Stolperfallen
Policy angelegt, CNI setzt nicht durch: Mit Flannel (oder anderen Plugins ohne Policy-Support) sind NetworkPolicies wirkungslos — ohne Fehlermeldung. Immer zuerst klären, was das CNI kann.
Egress-Deny killt DNS: Eine Default-Deny-Egress-Policy blockiert auch die Anfragen an CoreDNS. Symptom: Alle Namensauflösungen schlagen fehl, die Anwendung wirkt komplett offline. Du musst UDP/TCP Port 53 Richtung kube-system explizit erlauben — die mit Abstand häufigste Policy-Panne.
Leerer podSelector falsch verstanden: podSelector: {} heißt alle Pods im Namespace, nicht keiner. Wer das verwechselt, sperrt versehentlich den ganzen Namespace — oder glaubt, es getan zu haben.
from-Liste vs. kombinierte Bedingung: Zwei Einträge in der from-Liste sind ODER-verknüpft; namespaceSelector und podSelector im selben Listeneintrag sind UND-verknüpft. Ein verrutschtes - macht aus „Pods mit Label X im Namespace Y” ein „alle Pods mit Label X, egal wo, plus der ganze Namespace Y”.
Interview-Vorbereitung
Auf „Wie funktioniert Networking in Kubernetes?” antwortest du geschichtet: Modell (jeder Pod eine IP, flach, ohne NAT — als Anforderung) → Umsetzung (CNI-Plugins wie Calico/Cilium/Flannel bauen das Pod-Netz, vom kubelet pro Pod aufgerufen) → Sicherheit (per Default alles offen, NetworkPolicies segmentieren label-basiert, durchgesetzt vom CNI) → Best Practice (Default-Deny pro Namespace, DNS-Egress nicht vergessen).
Typische Follow-ups:
- „Was passiert konkret, wenn ein Pod startet?” — kubelet ruft das CNI-Plugin auf, das ein veth-Interface anlegt, eine IP aus dem Pod-CIDR vergibt und Routing/Tunnel konfiguriert.
- „Warum Cilium statt Calico?” — eBPF statt iptables (Skalierung, kube-proxy-Ersatz), L7-Policies, Hubble-Observability; dafür höhere Betriebskomplexität.
- „Wie verbietest du Traffic explizit?” — Standard-NetworkPolicy kennt kein Deny; Verbote entstehen durch Default-Deny plus fehlende Allow-Regel. Explizite Denys nur über CNI-eigene CRDs (z. B. Calico).
- „Policy greift nicht — wie debuggst du?” — CNI-Policy-Support prüfen, Labels von Pods und Namespaces gegen die Selektoren halten, Richtung (Ingress vs. Egress) checken, mit Test-Pod gezielt Verbindungen probieren.
Zusammenfassung
Das Kubernetes-Netzwerkmodell verspricht jedem Pod eine eigene, clusterweit erreichbare IP — gebaut wird es vom CNI-Plugin deiner Wahl: Flannel simpel ohne Policies, Calico mit Routing und starker Policy-Engine, Cilium mit eBPF, L7-Policies und Hubble. NetworkPolicies verwandeln das offene Netz in ein Zonenmodell: allow-only, additiv, label-basiert, durchgesetzt vom CNI. Default-Deny ist der richtige Ausgangspunkt — solange du DNS-Egress mitdenkst.
Womit wir beim Debugging wären: Wenn ein Service nicht antwortet, steckt der Fehler in DNS, Endpoints, kube-proxy oder einer Policy? Die nächste Lektion gibt dir die systematische Diagnose-Reihenfolge dafür.