🚀 Platform Engineer Lektion 22/50 ~11 Min. Experte

Network-Segmentierung in Multi-Tenant Plattformen

Zero-Trust-Netzwerkdesign, Mesh und Tenant-Grenzen.

📝 Meine Notizen

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.