Veröffentlicht am

Kubernetes Mikrosegmentierung: NetworkPolicies und Cilium

Teilen:
Authors

Kubernetes Network Segmentation: Mikrosegmentierung praktisch umsetzen

TL;DR

  • Ohne NetworkPolicies kann jeder Pod mit jedem anderen Pod im Cluster kommunizieren -- das ist ein offenes Scheunentor.
  • Eine Deny-all-Baseline pro Namespace ist der wichtigste erste Schritt. Danach schrittweise Allow-Regeln hinzufuegen.
  • Cilium und Calico sind die beiden relevanten CNI-Plugins. Cilium bietet Layer-7-Filtering via eBPF, Calico ist ausgereifter bei reinem L3/L4.
  • NetworkPolicies sind additiv: es gibt kein explizites "Deny", nur das Fehlen eines "Allow" nach Aktivierung einer Policy.
  • Testen laesst sich das Ganze mit kubectl exec und einfachen curl/wget-Aufrufen zwischen Pods.

Warum Mikrosegmentierung wichtig ist

In einem Standard-Kubernetes-Cluster ohne NetworkPolicies gilt eine Flat-Network-Architektur. Jeder Pod kann mit jedem anderen Pod kommunizieren, egal in welchem Namespace. Das ist bequem fuer die Entwicklung, aber ein Albtraum fuer die Sicherheit.

Wenn ein Angreifer einen einzelnen Pod kompromittiert (etwa ueber eine Schwachstelle in einer Dependency), kann er sich lateral durch den gesamten Cluster bewegen. Er erreicht Datenbanken, interne APIs, Secrets-Management-Systeme -- alles ohne weitere Huerde.

Mikrosegmentierung loest dieses Problem, indem der Netzwerkverkehr auf Pod-Ebene kontrolliert wird. Das Prinzip ist einfach: Verbiete alles, erlaube nur das Noetigste.

CNI-Plugin-Vergleich: Cilium vs. Calico

Nicht jedes CNI-Plugin unterstuetzt NetworkPolicies. Und selbst unter denen, die es tun, gibt es erhebliche Unterschiede.

FeatureCiliumCalicoFlannel
Standard NetworkPoliciesJaJaNein
Layer-7-Filtering (HTTP, gRPC)Ja (nativ via eBPF)Nur mit Envoy-IntegrationNein
Verschluesselung (WireGuard)JaJaNein
eBPF-basiertJaOptional (eBPF dataplane)Nein
DNS-basierte PoliciesJaEnterprise-onlyNein
Performance-OverheadGering (eBPF)Gering (iptables/eBPF)Minimal
Hubble-ObservabilityJa (integriert)Nein (externer Stack noetig)Nein

Fuer ernsthafte Segmentierung ist Flannel keine Option. Die Wahl steht zwischen Cilium und Calico. Cilium hat in den letzten zwei Jahren stark aufgeholt und ist mittlerweile das Default-CNI bei vielen Managed-Kubernetes-Anbietern.

Schritt 1: Deny-all-Baseline einrichten

Der erste und wichtigste Schritt ist eine Default-Deny-Policy fuer jeden Namespace. Das folgende YAML blockiert saemtlichen eingehenden und ausgehenden Traffic fuer alle Pods im Namespace:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Der leere podSelector: {} selektiert alle Pods im Namespace. Ab dem Moment, in dem diese Policy angewendet wird, ist saemtliche Kommunikation blockiert -- auch DNS-Aufloesung.

DNS muss explizit erlaubt werden, sonst funktioniert Service-Discovery nicht mehr:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Diese beiden Policies zusammen bilden eine sichere Baseline: Kein Traffic erlaubt, ausser DNS-Anfragen an kube-system.

Schritt 2: Gezielte Allow-Regeln definieren

Jetzt kommt der Teil, der tatsaechlich Arbeit macht. Fuer jede legitime Kommunikationsbeziehung braucht es eine eigene Policy. Hier ein typisches Beispiel: Ein Frontend darf mit dem Backend auf Port 8080 sprechen, das Backend darf die Datenbank auf Port 5432 erreichen.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
      tier: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
              tier: web
      ports:
        - protocol: TCP
          port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-to-database
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: postgres
      tier: database
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: backend
              tier: api
      ports:
        - protocol: TCP
          port: 5432

Wichtig: Das Frontend kann die Datenbank nicht direkt erreichen. Der Backend-Pod ist der einzige erlaubte Pfad zur Datenbank. Genau das ist der Sinn von Mikrosegmentierung.

Schritt 3: Namespace-uebergreifende Regeln

In der Praxis laufen Monitoring-Stacks, Logging-Agents und Ingress-Controller in eigenen Namespaces. Diese muessen Zugriff auf Pods in anderen Namespaces erhalten. Dafuer braucht es namespaceSelector:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-monitoring-scrape
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              purpose: monitoring
          podSelector:
            matchLabels:
              app: prometheus
      ports:
        - protocol: TCP
          port: 9090
        - protocol: TCP
          port: 9091

Hinweis: namespaceSelector und podSelector im selben from-Block wirken als AND-Verknuepfung. Das bedeutet, nur Prometheus-Pods aus dem Monitoring-Namespace erhalten Zugriff. Wenn die beiden Selektoren als separate Listeneintraege stehen (jeder mit einem -), wirken sie als OR -- ein haeufiger Konfigurationsfehler.

Layer-7-Policies mit Cilium

Standard-NetworkPolicies arbeiten auf Layer 3/4 (IP-Adressen und Ports). Cilium geht einen Schritt weiter und erlaubt Regeln auf HTTP-Ebene:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-http-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: backend
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: "/api/v1/products"
              - method: POST
                path: "/api/v1/orders"

Diese Policy erlaubt dem Frontend nur GET-Anfragen auf /api/v1/products und POST-Anfragen auf /api/v1/orders. Alle anderen HTTP-Methoden und Pfade werden blockiert. Das ist ein erheblicher Sicherheitsgewinn gegenueber reinem Port-basiertem Filtering.

Policies testen und validieren

NetworkPolicies sind schwer zu debuggen, wenn etwas nicht funktioniert. Es gibt kein kubectl get denied-connections. Hier ein pragmatischer Testansatz:

# Temporaeren Debug-Pod starten
kubectl run debug-frontend --rm -it \
  --image=nicolaka/netshoot \
  --labels="app=frontend,tier=web" \
  -n production -- bash

# Aus dem Pod heraus: Erlaubte Verbindung testen
curl -v http://backend-svc:8080/api/v1/products
# Sollte funktionieren

# Verbotene Verbindung testen (direkt zur DB)
curl -v telnet://postgres-svc:5432
# Sollte mit Timeout fehlschlagen

# DNS-Aufloesung pruefen
nslookup backend-svc.production.svc.cluster.local

Fuer automatisierte Tests empfiehlt sich ein Tool wie cyclonus oder netpol-verify, das systematisch alle Kommunikationspfade prueft und gegen die definierten Policies validiert.

Haeufige Fehler bei der Umsetzung

Aus der Praxis ergeben sich immer wieder dieselben Stolperfallen:

1. DNS vergessen: Nach Aktivierung von Deny-all funktioniert nichts mehr, weil DNS-Anfragen blockiert werden. Immer eine DNS-Egress-Regel mitliefern.

2. AND vs. OR verwechseln: Zwei Selektoren im selben from-Eintrag sind AND. Zwei separate from-Eintraege sind OR. Der Unterschied ist subtil, aber sicherheitsrelevant.

3. Keine Labels auf Pods: NetworkPolicies selektieren ueber Labels. Ohne konsistente Label-Strategie ist Segmentierung nicht umsetzbar. Definieren Sie Standards wie app, tier, version und setzen Sie diese in allen Deployments durch.

4. Egress vergessen: Die meisten Teams konzentrieren sich auf Ingress-Regeln. Aber Egress ist genauso wichtig. Ein kompromittierter Pod, der Daten nach aussen exfiltrieren kann, ist ein massives Problem.

5. Policy in falschem Namespace: Eine NetworkPolicy gilt nur fuer den Namespace, in dem sie definiert ist. Das klingt offensichtlich, fuehrt aber regelmaessig zu Verwirrung.

Schrittweise Einfuehrung in bestehenden Clustern

Wer Mikrosegmentierung in einem laufenden Produktionscluster einfuehren will, sollte nicht einfach Deny-all einschalten. Das fuehrt garantiert zu Ausfaellen.

Stattdessen empfiehlt sich folgendes Vorgehen:

  1. Traffic-Analyse: Mit Hubble (Cilium) oder Calico Flow Logs den aktuellen Netzwerkverkehr dokumentieren. Welcher Pod spricht mit welchem Pod auf welchem Port?
  2. Allow-Regeln schreiben: Basierend auf der Traffic-Analyse die noetigsten Policies formulieren.
  3. Audit-Modus: Cilium bietet einen Policy-Audit-Modus, der blockierten Traffic nur loggt statt tatsaechlich zu blockieren. Damit laesst sich pruefen, ob die Policies korrekt sind, ohne den Betrieb zu stoeren.
  4. Namespace fuer Namespace: Nicht den gesamten Cluster auf einmal umstellen, sondern mit einem unkritischen Namespace beginnen und sich vorarbeiten.
  5. Monitoring einrichten: Alerts fuer blockierten Traffic konfigurieren, damit Fehlkonfigurationen schnell auffallen.

Performance-Auswirkungen

Die Frage kommt immer: "Wie stark bremsen NetworkPolicies den Cluster?" Die Antwort haengt vom CNI-Plugin ab.

Cilium nutzt eBPF-Programme, die direkt im Linux-Kernel laufen. Der Overhead liegt typischerweise unter 1% Latenz-Zunahme. Bei Calico mit iptables-Backend kann der Overhead bei sehr vielen Regeln (mehrere hundert) merkbar werden, weil iptables-Chains linear durchlaufen werden. Calicos eBPF-Dataplane behebt dieses Problem.

Fuer die meisten Cluster mit bis zu einigen tausend Pods und einigen hundert Policies ist die Performance-Auswirkung vernachlaessigbar.

Policies als Code verwalten

NetworkPolicies gehoeren ins Git-Repository, nicht in manuelle kubectl apply-Aufrufe. Verwalten Sie sie zusammen mit Ihren Deployments in einem GitOps-Workflow.

Ein sinnvoller Verzeichnisaufbau:

k8s/
  namespaces/
    production/
      network-policies/
        00-default-deny.yaml
        01-allow-dns.yaml
        10-frontend-to-backend.yaml
        11-backend-to-database.yaml
        20-monitoring-access.yaml
      deployments/
        frontend.yaml
        backend.yaml
        database.yaml

Die Nummerierung erleichtert die Uebersicht. ArgoCD oder Flux koennen die Policies automatisch synchronisieren. Aenderungen durchlaufen Code-Review, bevor sie angewendet werden. Das reduziert das Risiko von Fehlkonfigurationen erheblich.

Fuer Compliance-relevante Umgebungen laesst sich mit OPA Gatekeeper oder Kyverno zusaetzlich erzwingen, dass jeder Namespace eine Deny-all-Policy enthalten muss.

Zusammenspiel mit anderen Sicherheitsschichten

NetworkPolicies sind ein Baustein, nicht die gesamte Loesung. Eine solide Kubernetes-Sicherheitsarchitektur umfasst weitere Ebenen:

Mikrosegmentierung schuetzt den East-West-Traffic. Die anderen Massnahmen schuetzen die restlichen Angriffsflaechen.

Fazit

Kubernetes-Netzwerksegmentierung ist kein Nice-to-have, sondern gehoert zur Grundausstattung eines produktionsreifen Clusters. Der Einstieg ist mit einer Deny-all-Baseline und schrittweisen Allow-Regeln machbar, auch in bestehenden Umgebungen.

Wer Unterstuetzung bei der Planung oder Umsetzung braucht -- ob fuer einen einzelnen Cluster oder eine Multi-Cluster-Landschaft -- kann sich gerne bei uns melden unter /kontakt.

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen