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.
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
-
Wie Cilium eBPF nutzt: Identität, Policy und Sockets Was im Kernel tatsächlich passiert, wenn ein Pod startet, mit einem anderen Pod spricht oder eine Policy-Entscheidung für ein Paket erhält – und warum das schnell ist. -
Einen Cluster für Cilium einrichten: Zuerst das Standard-CNI deaktivieren Cilium ersetzt das Netzwerk deines Clusters vollständig, daher ist die Reihenfolge der Schritte wichtiger als bei den meisten Add-ons. -
Services mit Ciliums Gateway-API-Unterstützung bereitstellen GatewayClass, Gateway und HTTPRoute – hostbasiertes Routing, Pfad-Matching, URL-Rewrites und TLS, ohne separate Gateway-Pods.