Network-Segmentierung in Multi-Tenant Plattformen
Zero-Trust-Netzwerkdesign, Mesh und Tenant-Grenzen.
Lernziele
Nach dieser Lektion kannst du erklären, warum Kubernetes-Netzwerke standardmäßig flach und offen sind und wie du daraus segmentierte Tenant-Zonen machst: NetworkPolicies mit Default-Deny, deren Grenzen, und wann ein Service Mesh mit mTLS und Identitäts-basierter Autorisierung den nächsten Schritt liefert. Du kannst das Zero-Trust-Prinzip auf Plattform-Netzwerke übersetzen und kennst die Betriebsfallen segmentierter Umgebungen.
Das Problem: Standardmäßig darf jeder Pod mit jedem reden
Das Kubernetes-Netzwerkmodell garantiert: Jeder Pod erreicht jeden anderen Pod im Cluster, ohne NAT, über alle Namespaces hinweg. Für den Einstieg ist das großartig — nichts muss konfiguriert werden. Für eine Multi-Tenant-Plattform ist es ein Sicherheitsproblem mit Ansage: Der Webshop von Team A kann die Staging-Datenbank von Team B erreichen, wenn er ihren Service-Namen kennt. Ein kompromittierter Pod — eine verwundbare Library reicht — kann den gesamten Cluster scannen und sich lateral bewegen. Die Tenant-Grenzen aus Lektion 19 und 20 existieren auf Netzwerkebene schlicht nicht, solange niemand sie zieht.
Die klassische Antwort — eine Firewall am Perimeter — greift hier nicht: Der Angreifer ist schon drinnen. Das ist der Kern von Zero Trust auf Netzwerkebene: Kein Vertrauen aufgrund von Netzwerkposition; jede Verbindung muss explizit erlaubt und idealerweise authentifiziert sein.
NetworkPolicies: Default-Deny als Fundament
Das eingebaute Werkzeug sind NetworkPolicies — Namespace-gebundene Regeln, die der CNI-Plugin-Layer (Cilium, Calico und andere; das Standard-kindnet z. B. setzt sie nicht durch!) als Paketfilter umsetzt. Die wichtigste Eigenschaft: Policies sind additiv und whitelist-basiert. Sobald irgendeine Policy einen Pod selektiert, ist alles verboten, was keine Policy erlaubt.
Daraus folgt das Grundmuster jeder Tenant-Segmentierung — eine Default-Deny-Policy pro Tenant-Namespace, ausgerollt automatisch beim Namespace-Provisioning (Lektion 16):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: team-orders
spec:
podSelector: {} # alle Pods im Namespace
policyTypes: [Ingress, Egress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace-and-dns
namespace: team-orders
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
ingress:
- from:
- podSelector: {} # Verkehr innerhalb des Tenants
egress:
- to:
- podSelector: {}
- to: # DNS muss immer erlaubt sein
- namespaceSelector:
kubernetes.io/metadata.name: kube-system
ports:
- { protocol: UDP, port: 53 }
- { protocol: TCP, port: 53 }
Darauf bauen gezielte Öffnungen: Ingress vom Ingress-Controller-Namespace zu den Service-Ports, Egress zu explizit benannten Nachbar-Tenants für genehmigte Service-zu-Service-Beziehungen, Egress zu externen Endpunkten. Cross-Tenant-Kommunikation wird damit zum dokumentierten Vertrag statt zum Zufallsprodukt — die Policy ist die maschinenlesbare Form der Abhängigkeit.
Die Grenzen von NetworkPolicies solltest du genauso parat haben: Sie arbeiten auf IP/Port-Ebene (L3/L4) — keine HTTP-Methoden, keine Pfade. Sie kennen keine Identität, nur Selektoren und IP-Blöcke. Egress zu externen Diensten per IP zu pflegen ist fragil (Cloud-IPs wechseln); DNS-basierte Egress-Regeln bieten erst CNI-Erweiterungen wie Cilium mit eigenen CRDs. Und: keine Verschlüsselung, keine Authentifizierung — ein Pod, der die erlaubte Route nutzt, ist deshalb noch nicht der, für den er sich ausgibt.
Service Mesh: Identität statt IP
Genau diese Lücken adressiert ein Service Mesh (Istio, Linkerd, Cilium Service Mesh). Der Mechanismus: Jeder Workload bekommt eine kryptografische Identität als Zertifikat (im SPIFFE-Format, ausgestellt von der Mesh-CA, automatisch rotiert), und die Datenebene — Sidecar-Proxies oder beim Sidecar-losen Ansatz Node-Komponenten — erzwingt mTLS für jede Verbindung: verschlüsselt und beidseitig authentifiziert.
Damit verschiebt sich die Autorisierung von „welche IP darf zu welchem Port” zu „welche Identität darf welche Operation aufrufen”:
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: orders-db-access
namespace: team-orders
spec:
selector:
matchLabels: { app: orders-db }
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/team-orders/sa/orders-api"]
to:
- operation:
ports: ["5432"]
Das ist Zero Trust in Reinform: Die Regel überlebt Pod-Neustarts, IP-Wechsel und sogar Cluster-Migrationen, weil sie an der ServiceAccount-Identität hängt. Für Multi-Tenancy heißt das: Tenant-Grenzen werden zu Identitäts-Grenzen — auch L7-fein („nur GET auf /api/v1”).
Der ehrliche Preis: Ein Mesh ist ein verteiltes System im deinem verteilten System — eigene Control Plane, eigene Upgrade-Zyklen, Latenz- und Ressourcen-Overhead pro Proxy, und eine steile Debugging-Lernkurve („liegt’s an der App, der Policy oder dem Proxy?”). Die Abwägung in der Praxis: NetworkPolicies sind der nicht verhandelbare Boden für jede Multi-Tenant-Plattform; das Mesh kommt dazu, wenn mTLS-Pflicht (Compliance), L7-Autorisierung oder mesh-typische Traffic-Features (Retries, Canary-Routing) den Betriebsaufwand rechtfertigen. „Mesh statt NetworkPolicies” ist dabei falsch gedacht — Defense in Depth nutzt beide Schichten: Pakete, die das CNI verwirft, erreichen den Proxy gar nicht erst.
Segmentierung über den Cluster hinaus
Tenant-Grenzen enden nicht am Cluster-Rand. Drei Punkte, die in Plattform-Designs regelmäßig fehlen:
- Egress-Kontrolle: Default-Deny nach außen plus kontrollierte Ausgänge (Egress-Gateway oder NAT mit FQDN-Filter) verhindert, dass kompromittierte Pods beliebig nach Hause telefonieren — für viele Compliance-Regime Pflicht und der am häufigsten vergessene Teil.
- Cloud-Netzwerk-Schicht: Security Groups, Subnets und private Endpoints für Managed Services (Datenbank nur aus dem Cluster-Subnetz erreichbar) bilden die Schicht unter den NetworkPolicies. Wer Tenants auf Cloud-Konten trennt (Lektion 19), zieht die Grenze hier ohnehin.
- Geteilte Plattformdienste: Monitoring-Agents, Log-Shipper und der Ingress-Controller brauchen definierte Routen in die Tenant-Namespaces — diese Ausnahmen gehören in die Standard-Policy-Suite des Namespace-Provisionings, sonst bricht mit dem ersten Default-Deny die Observability.
Praxis: Default-Deny live erleben
In einem kind-Cluster mit einem Policy-fähigen CNI (z. B. Cilium):
kind create cluster --config kind-no-cni.yaml # disableDefaultCNI: true
cilium install --wait
kubectl create ns tenant-a tenant-b
kubectl -n tenant-a run web --image=nginx --port=80
kubectl -n tenant-a expose pod web --port=80
kubectl -n tenant-b run attacker --image=busybox --restart=Never -- sleep 3600
# Vorher: flaches Netz — Cross-Tenant-Zugriff klappt:
kubectl -n tenant-b exec attacker -- wget -qO- --timeout=2 web.tenant-a
# liefert die nginx-Startseite
# Default-Deny + Intra-Namespace-Allow (YAML von oben) anwenden:
kubectl apply -f tenant-a-policies.yaml
# Nachher: derselbe Zugriff läuft in den Timeout:
kubectl -n tenant-b exec attacker -- wget -qO- --timeout=2 web.tenant-a
# wget: download timed out
Erweitere das Lab um eine gezielte Öffnung (Ingress von tenant-b nur auf Port 80 mit Label-Selektor) und prüfe beide Richtungen. Wenn du Cilium nutzt, sieh dir mit hubble observe an, welche Policy ein Paket verworfen hat — genau dieses Tooling ist im echten Betrieb der Unterschied zwischen Minuten und Stunden Debugging.
Typische Stolperfallen
Policies geschrieben, CNI setzt sie nicht durch: NetworkPolicies ohne Policy-fähiges CNI sind stille Dekoration — der Cluster nimmt sie an und ignoriert sie. Immer mit einem Verbindungstest verifizieren, nie nur mit kubectl get netpol.
DNS vergessen: Die erste Default-Deny-Egress-Policy ohne DNS-Ausnahme legt scheinbar „das Netzwerk” lahm — tatsächlich scheitert nur die Namensauflösung. Port 53 zu kube-system gehört in jede Egress-Basis-Policy.
Segmentierung nachträglich einführen: In einem gewachsenen Cluster Default-Deny zu aktivieren, ohne die real existierenden Verkehrsflüsse zu kennen, produziert einen Großausfall. Der gangbare Weg: Flows erst beobachten (Hubble, VPC Flow Logs, Mesh-Telemetrie), Policies generieren, im Audit-Modus prüfen, dann durchsetzen — Namespace für Namespace.
Mesh als Sicherheits-Pflaster: Wer mTLS einführt, aber Authorization-Policies auf „allow all” lässt, hat Verschlüsselung gewonnen und Zugriffskontrolle weiterhin nicht. mTLS authentifiziert; autorisieren musst du explizit.
Interview-Vorbereitung
Auf „Wie segmentierst du das Netzwerk einer Multi-Tenant-Plattform?” antwortest du geschichtet: Ausgangslage (Kubernetes-Netz ist flach, jeder Pod erreicht jeden — Tenant-Grenzen existieren netzwerkseitig nicht von selbst) → Fundament (Default-Deny-NetworkPolicies pro Tenant-Namespace, automatisch beim Provisioning, Öffnungen als explizite Verträge) → Grenzen & nächste Stufe (L3/L4 ohne Identität → Service Mesh mit mTLS und identitätsbasierter L7-Autorisierung, wenn Compliance oder Feinheit es verlangen) → Über den Cluster hinaus (Egress-Kontrolle, Cloud-Netzwerkschicht, definierte Routen für Plattformdienste).
Wahrscheinliche Follow-ups:
- „NetworkPolicy oder Service Mesh?” — beides ist kein Entweder-oder: Policies als durchsetzbarer Boden (L3/L4, kein Overhead), Mesh on top für mTLS und Identitäts-/L7-Autorisierung; Defense in Depth.
- „Wie führst du Default-Deny in einem Bestandscluster ein?” — Flows beobachten und inventarisieren, Policies daraus ableiten, Audit-/Monitor-Phase, schrittweise pro Namespace scharf schalten, Rollback-Plan je Schritt.
- „Was ist Zero Trust im Cluster konkret?” — keine Rechte aus Netzwerkposition: jede Verbindung explizit erlaubt, authentifiziert über Workload-Identität (mTLS/SPIFFE) und minimal autorisiert — statt „intern = vertrauenswürdig”.
- „Zwei Tenants müssen miteinander reden — Prozess?” — Antrag als Code: PR mit der konkreten Policy (Quelle, Ziel, Port), Review durch beide Teams, damit ist die Abhängigkeit dokumentiert, auditierbar und kündbar.
Zusammenfassung
Kubernetes startet mit einem flachen Alles-darf-Netz; Multi-Tenancy verlangt das Gegenteil. Default-Deny-NetworkPolicies pro Tenant — automatisch provisioniert, mit DNS- und Plattformdienst-Ausnahmen — sind das nicht verhandelbare Fundament; ihre Grenzen (keine Identität, kein L7, keine Verschlüsselung) schließt ein Service Mesh mit mTLS und identitätsbasierter Autorisierung, zum Preis eines eigenen Betriebssystems im System. Zero Trust heißt: explizite, authentifizierte, minimale Verbindungen — auch für Egress und die Schichten unterhalb des Clusters.
Segmentierung trennt Tenants im Raum. Die nächste Lektion trennt in der Zeit und im Reifegrad: Environment-Strategien von Dev über Staging bis Production — und wie Änderungen kontrolliert dazwischen wandern.