Cilium Cilium

Network Policies schreiben: Von L3/L4- bis L7-Regeln

Kubernetes startet damit, dass jeder Pod jeden anderen Pod erreichen kann – CiliumNetworkPolicy schränkt das ein, bis hinunter zur HTTP-Methode, wenn du das brauchst.

12 min Fortgeschritten

Standardmäßig hat das Kubernetes-Netzwerk keine Einschränkungen: Jeder Pod kann jeden anderen Pod im Cluster auf jedem Port erreichen. Für einen Cluster mit Mandantentrennung oder Sicherheitsanforderungen jenseits von „intern vertrauen wir allem“ ist dieser Standard meist falsch. CiliumNetworkPolicy schränkt ihn ein – von einfachen Allow-Listen bis zu Regeln, die die eigentliche HTTP-Anfrage untersuchen.

Der Ausgangspunkt: alles ist offen

Bestätige den Standard, bevor du etwas änderst:

kubectl run client --image=curlimages/curl --command -- sleep infinity
kubectl exec client -- curl -s http://some-other-service.default.svc.cluster.local

Ohne Policies gelingt diese Anfrage, unabhängig davon, was client ist oder was some-other-service tut. Sobald irgendeine Policy einen bestimmten Pod für eine Richtung (Ingress oder Egress) auswählt, wird der Traffic dieses Pods in dieser Richtung per Default-Deny gesperrt: Nur was die Policy ausdrücklich erlaubt, kommt durch. Ein Pod, den gar keine Policy auswählt, bleibt vollständig offen. Das ist das Wichtigste, was du vor deiner ersten Policy verstehen musst: Eine zu eng gefasste Policy kann unbemerkt Traffic abschneiden, den du gar nicht anfassen wolltest – für jeden Pod, den sie zufällig auswählt.

L3/L4: nach Identität und Port einschränken

Die häufigste Policy-Form beschränkt, welche Pods welche Pods auf welchen Ports erreichen können, und nutzt dabei Ciliums labelbasierte Identität (behandelt in der Anleitung dieser Reihe zu eBPF) statt IP-Adressen:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: default
spec:
  endpointSelector:
    matchLabels:
      app: api
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP

So liest du das: Die Policy gilt für Pods mit dem Label app: api (endpointSelector) und erlaubt eingehenden Traffic (ingress) nur von Pods mit dem Label app: frontend, nur auf TCP-Port 8080. Jeder andere Pod und jeder andere Port wird für api-Pods nun verweigert, da die Existenz dieser Policy sie für Ingress in Default-Deny versetzt.

Egress funktioniert genauso, in die andere Richtung

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-egress-to-database
  namespace: default
spec:
  endpointSelector:
    matchLabels:
      app: api
  egress:
    - toEndpoints:
        - matchLabels:
            app: database
      toPorts:
        - ports:
            - port: "5432"
              protocol: TCP

api-Pods können jetzt nur noch ausgehende Verbindungen zu database-Pods auf Port 5432 aufbauen. Kombiniere Ingress- und Egress-Regeln in derselben Policy oder halte sie getrennt, je nachdem, was in deinem Setup übersichtlicher ist. Funktional setzen sich getrennte Policies mit passenden endpointSelectorn für dieselben Pods trotzdem korrekt zusammen.

Traffic von oder nach außerhalb des Clusters erlauben

fromEndpoints/toEndpoints passen Pods anhand von Labels. Für Traffic von oder zu etwas außerhalb des Pod-Netzwerks des Clusters gelten stattdessen DNS- oder CIDR-basierte Regeln:

  egress:
    - toFQDNs:
        - matchName: "api.external-service.com"
      toPorts:
        - ports:
            - port: "443"
              protocol: TCP

toFQDNs ist eine Cilium-spezifische Erweiterung über die einfache Kubernetes NetworkPolicy hinaus. Sie erlaubt Egress zu einer benannten externen Domain, statt deren IP-Adressen vorab kennen und festschreiben zu müssen – wichtig bei jedem externen Dienst, dessen IPs du nicht kontrollierst und die sich ohne Vorwarnung ändern können.

L7: Policy, die die eigentliche Anfrage liest

Alles bisher arbeitet auf L3/L4: Identitäten und Ports, ohne in den Traffic hineinzusehen. Cilium kann weitergehen und Entscheidungen anhand des Inhalts von L7-Protokollen treffen, die es versteht – HTTP ist das häufigste:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-read-only-for-reporting
  namespace: default
spec:
  endpointSelector:
    matchLabels:
      app: api
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: reporting
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: "GET"
                path: "/reports/.*"

Das erlaubt dem reporting-Service, api auf Port 8080 zu erreichen, aber nur mit GET-Anfragen, deren Pfad auf /reports/.* passt. Ein POST an denselben Port oder ein GET auf einen nicht aufgeführten Pfad wird verweigert, obwohl es dieselben Pods auf demselben Port sind, die L3/L4-Regeln allein pauschal erlaubt hätten. Das wird von Ciliums eingebettetem Envoy-Proxy für die Pods durchgesetzt, für die die Regel gilt. L7-Policy hat im Vergleich zur reinen L3/L4-eBPF-Durchsetzung reale, aber meist moderate Performance-Kosten – das solltest du bei sehr durchsatzstarken Pfaden bedenken, bei denen L3/L4-Einschränkung für deine tatsächliche Sicherheitsanforderung ausreicht.

Testen, bevor du vertraust

Eine falsch angewendete Policy schlägt geschlossen fehl, sie verweigert Traffic, statt ihn durchzulassen. Das ist der sicherere Fehlermodus, aber immer noch ein Ausfall, wenn es der falsche Traffic ist. Bevor du eine neue restriktive Policy auf etwas anwendest, das echten Traffic bedient:

kubectl apply --dry-run=server -f policy.yaml

Wende sie dann zuerst in einer niedrigeren Umgebung an, erzeuge Traffic, der erlaubt sein sollte, und Traffic, der verweigert werden sollte, und bestätige, dass sich beides wie erwartet verhält, bevor du dieselbe Policy auf alles überträgst, was die Produktion bedient.

Von allow-all starten, dann bewusst einschränken

Ein vernünftiger Einführungspfad für einen Cluster ohne bestehende Policies: Versuche nicht, am ersten Tag ein vollständiges, korrektes Policy-Set zu schreiben. Beobachte zuerst echte Traffic-Muster (Hubble, behandelt auf der Landingpage dieser Technologie, ist genau dafür gebaut), schreibe Policies, die dem beobachteten legitimen Verhalten entsprechen, wende sie zuerst in einer Nicht-Produktionsumgebung an und schränke erst dann den Produktions-Traffic entsprechend ein. Restriktive Policy gegen geratene statt beobachtete Traffic-Muster zu schreiben, ist ein häufiger Weg zu einem Ausfall, der wie ein Cilium-Problem aussieht, in Wahrheit aber eine Policy ist, die ein legitimes Muster nicht berücksichtigt hat, an das niemand gedacht hat.

Beliebte Anleitungen