Kubernetes NetworkPolicies mit Cilium und Hubble: Netzwerksegmentierung durchsetzen und Datenverkehr sichtbar machen
Kubernetes NetworkPolicies sind API-Ressourcen, mit denen festgelegt wird, welche eingehenden und ausgehenden Verbindungen für Pods erlaubt sind. In einer Standard-Kubernetes-Umgebung ist die Netzwerkkommunikation zunächst weitgehend offen: Pods können in der Regel andere Pods erreichen, und viele Workloads dürfen ebenfalls externe Ziele ansprechen. NetworkPolicies liefern die Regeln für die Segmentierung, doch ob diese Regeln tatsächlich greifen, hängt vollständig vom verwendeten CNI ab. Cilium setzt Richtlinien mithilfe von eBPF direkt im Kernel durch und bringt zusätzlich eine wichtige Fähigkeit mit, die bei klassischen Implementierungen oft fehlt: Observability. Ohne Einblick auf Flow-Ebene bleiben Fehlkonfigurationen leicht unentdeckt, und Sicherheitsprüfungen fehlt häufig der Nachweis, dass Richtlinien wirklich wie vorgesehen arbeiten.
Die Kombination aus Cilium und eBPF verbessert sowohl die Durchsetzung als auch die Transparenz. Statt auf iptables-Ketten im Userspace zu bauen, werden Regeln direkt im Kernel-Datapath umgesetzt. Das sorgt für effizientes Filtern und ermöglicht zusätzlich Layer-7-Kontrollen wie die Auswertung von HTTP-Methoden und Pfaden. Hubble liefert detaillierte Telemetriedaten auf Flow-Ebene, sodass Policy-Fehler erkannt werden können, bevor sie produktive Workloads beeinträchtigen. In verwalteten Kubernetes-Umgebungen lässt sich Cilium als CNI einsetzen, während Hubble genutzt wird, um Policy-Rollouts zu validieren und Fehler zu analysieren.
Diese Anleitung zeigt, wie sich in Kubernetes eine durchsetzbare Netzwerksegmentierung mit gleichzeitiger Traffic-Observability aufbauen lässt: Sie installieren Cilium, stellen eine Beispielanwendung mit mehreren Services bereit, wenden grundlegende und erweiterte Richtlinien an und nutzen Hubble, um das Verhalten zu überprüfen und Probleme zu beheben. Dabei richten Sie Regeln für die Kommunikation zwischen Pods ein und setzen ein Zero-Trust-Netzwerkmodell um, das sich besonders für Multi-Tenant- oder Compliance-orientierte Umgebungen eignet.
Wichtige Erkenntnisse
- Kubernetes NetworkPolicies bleiben standardmäßig offen, solange kein policy-fähiges CNI sie erzwingt. Ohne diese Durchsetzung ist sämtlicher Traffic erlaubt.
- Cilium verwendet eBPF für leistungsfähige Paketfilterung im Kernel, was den Overhead senkt und sowohl L3/L4- als auch L7-Policies möglich macht.
- Hubble exportiert Flow-Logs und Metriken aus den eBPF-Hooks von Cilium und stellt Sichtbarkeit auf Namespace- und Service-Ebene bereit.
- Layer-7-Regeln wie das Erlauben von
GET /healthbei gleichzeitigem Blockieren vonPOST /adminsind mitCiliumNetworkPolicymöglich, gehören jedoch nicht zum Standardumfang klassischer NetworkPolicies. - Zero-Trust-Networking lässt sich in verwalteten Kubernetes-Umgebungen durch explizite Ingress- und Egress-Regeln in Verbindung mit Namespace-Isolation umsetzen.
- Observability verhindert unbemerkte Policy-Fehler. Mit Hubble können Regeln vor und nach der Durchsetzung geprüft und für Sicherheits-Audits nachvollzogen werden.
Konzeptionelle Grundlagen
Was sind Kubernetes NetworkPolicies?
NetworkPolicies sind Regeln, die innerhalb eines Namespace gelten oder gezielt ausgewählte Pods betreffen. Sie definieren eingehenden Traffic, also wer Daten an bestimmte Pods senden darf, sowie ausgehenden Traffic, also welche Ziele diese Pods erreichen dürfen. Sie arbeiten deklarativ: Sie beschreiben erlaubte Kommunikationspartner und Ports, während die eigentliche Durchsetzung durch das CNI erfolgt. Wenn kein Pod von einer Policy erfasst wird, hängt das Verhalten von der jeweiligen Implementierung ab. Bei Cilium gilt für Pods ohne passende Policy üblicherweise zunächst ein offenes Kommunikationsmodell, bis zunächst eine Default-Deny-Regel und anschließend eine gezielte Allow-List eingeführt wird.
Ingress und Egress unterscheiden sich dabei klar: Ingress-Regeln betreffen den Traffic, der zu den ausgewählten Pods hineinläuft, während Egress-Regeln festlegen, welche Verbindungen von diesen Pods nach außen erlaubt sind. Für eine saubere Segmentierung beginnt man häufig mit einer „Deny All Ingress“-Regel und ergänzt anschließend gezielt Freigaben für bestimmte Namespaces oder Pod-Labels.
Auch bei der Isolation gibt es zwei typische Ansätze: Entweder wird ein kompletter Namespace mit einer einzigen Policy abgesichert, oder es werden per Pod-Selector gezielt einzelne Workloads wie etwa Backend-Services isoliert. In Multi-Tenant-Szenarien ist Namespace-Isolation besonders verbreitet, meist kombiniert mit explizit erlaubtem Ingress aus einem API-Gateway- oder Ingress-Namespace.
Wie sich Cilium von anderen CNIs unterscheidet
Cilium unterstützt die Kubernetes-NetworkPolicy-API und erweitert diese zusätzlich um die eigene CRD CiliumNetworkPolicy für Layer-7-Regeln und weitere Funktionen. Im Unterschied zu vielen CNIs, die auf iptables oder Proxy-Komponenten im Userspace setzen, führt Cilium eBPF-Programme direkt im Linux-Kernel aus, um Traffic sowohl zu filtern als auch zu beobachten. Daraus ergeben sich mehrere Vorteile:
- eBPF-Datapath: Paketverarbeitung und Policy-Prüfung finden direkt im Kernel statt. Das reduziert Kontextwechsel und sorgt auch bei wachsender Umgebung für konstante Leistung.
- L7-Verständnis: Mit
CiliumNetworkPolicylassen sich HTTP-/gRPC-Methoden, Pfade und Header gezielt steuern. - Integrierte Observability: Hubble verarbeitet aus eBPF gewonnene Flow- und Metrikdaten, die von den Cilium-Agenten bereitgestellt werden.
Was ist eBPF und warum wird es eingesetzt?
eBPF, ausgeschrieben extended Berkeley Packet Filter, ist eine Kernel-Technologie, mit der isolierte Programme auf bestimmte Systemereignisse reagieren können, etwa beim Eintreffen von Paketen oder bei Socket-Aktivitäten. Cilium übersetzt NetworkPolicies und CiliumNetworkPolicies in eBPF-Programme, die auf jedem Node ausgeführt werden. Pakete werden direkt im Kernel klassifiziert und anschließend erlaubt oder verworfen. Für einfache L3/L4-Regeln ist dafür keine Verarbeitung im Userspace erforderlich. Genau deshalb skalieren eBPF-basierte Netzwerkregeln gut und werden in Kubernetes sowohl für die Durchsetzung als auch für die Sichtbarkeit von Traffic verwendet.
Warum Observability bei klassischen NetworkPolicies oft fehlt
Die Standard-API von Kubernetes NetworkPolicy definiert keine eigene Observability-Schicht. Viele CNIs können zwar erlauben und blockieren, stellen aber nicht dar, welcher Flow verworfen wurde oder aus welchem Grund. In der Praxis werden häufig „Deny All“-Regeln eingeführt, und anschließend beginnt die Fehlersuche bei Verbindungsproblemen ohne klare Sicht auf den Traffic. Genau hier schließt Hubble die Lücke, indem es Flow-Logs und Metriken aus den eBPF-Hooks von Cilium exportiert. So lässt sich beobachteter Traffic direkt mit den zugrunde liegenden Policy-Regeln in Verbindung bringen.
| Funktion | Flannel | Calico | Cilium |
|---|---|---|---|
| eBPF | Nein | Nein | Ja |
| NetworkPolicy-Durchsetzung | Nein | Ja | Ja |
| L7-Policy | Nein | Begrenzt | Ja |
| Integrierte Observability | Nein | Nein | Hubble |
Architekturüberblick: Verwaltetes Kubernetes mit Cilium
In diesem Aufbau läuft der gesamte Pod-Traffic durch den Cilium-eBPF-Datapath, der auf jedem Node aktiv ist. Hubble sammelt Flow-Informationen aus denselben eBPF-Hooks, die Cilium auch zur Policy-Durchsetzung verwendet. Dadurch greifen Durchsetzung und Sichtbarkeit auf denselben Datapath zu, ohne zusätzlichen Overhead zu erzeugen.
- Cilium in verwaltetem Kubernetes: Manche verwalteten Kubernetes-Angebote bringen Cilium nicht standardmäßig mit. In der Regel wird es per Helm installiert und ersetzt als Cluster-CNI das vorhandene Standard-CNI. Entscheidend ist, die zu Ihrer Kubernetes-Version passende Installationsanleitung zu verwenden. Nach der Einrichtung wird der gesamte Pod-Traffic durch eBPF-basierte Policy-Regeln gesteuert.
- Paketinspektion: Pakete werden im Kernel analysiert. L3/L4-Regeln setzt eBPF direkt um, während L7-Regeln mit
CiliumNetworkPolicyzusätzlich eine HTTP-Analyse im Datapath benötigen. - Hubble: Hubble Relay und optional die Oberfläche bündeln Flow-Daten der Cilium-Agenten, sodass mit
hubble observenach Namespace, Label oder Ergebnis wie erlaubt oder blockiert gefiltert werden kann. - Beispiel mit mehreren Services: Ein typischer Aufbau umfasst einen Frontend-, einen Backend-, einen Datenbank- sowie einen Observability-Namespace. In diesem Modell darf das Frontend das Backend nur über ausdrücklich freigegebene Ports erreichen, das Backend kommuniziert ausschließlich mit der Datenbank, und der Observability-Namespace darf bei Bedarf Daten scrapen oder abfragen. Die Kombination aus Default-Deny und expliziten Ingress- und Egress-Regeln sorgt für Service-Isolation und Netzwerksegmentierung.
graph TD
FE[frontend namespace<br/>app=frontend] -->|port 80 allowed| BE[backend namespace<br/>app=backend]
BE -->|port 6379 allowed| DB[database namespace<br/>app=database]
OBS[observability namespace] -.->|scrape metrics| BE
OBS -.->|scrape metrics| FE
EXT[external / internet] -. blocked by egress policy .-> BE
EXT -. blocked by egress policy .-> DB
style EXT fill:#f66,color:#fff
Default-Deny-Ingress wird pro Namespace erzwungen. Durchgezogene Pfeile stehen für ausdrücklich erlaubte Verkehrswege, gestrichelte Verbindungen kennzeichnen blockierte Kommunikationspfade.
Voraussetzungen
- Ein verwalteter Kubernetes-Cluster.
- Ein für den Cluster konfiguriertes
kubectl. - Helm zur Installation von Cilium.
- Grundlegendes Verständnis von Kubernetes und Namespaces.
- Ein bereits installiertes oder als CNI geplantes Cilium sowie Namespaces für eine Beispielanwendung wie
frontend,backendunddatabase.
Schritt-für-Schritt-Umsetzung
Schritt 1 – Cilium im Cluster installieren oder aktivieren
Fügen Sie zunächst das Helm-Repository von Cilium hinzu und installieren Sie Cilium mit aktiviertem Hubble. Ersetzen Sie die Versionsnummer durch eine Version, die mit Ihrer Kubernetes-Version kompatibel ist.
Warnung: Wenn Cilium als aktives CNI in einem Cluster mit bereits laufenden Workloads installiert wird, unterbricht das die Pod-Netzwerkkommunikation. Während des Wechsels verlieren Pods ihre Konnektivität, bis die Cilium-Agenten auf allen Nodes vollständig den Status Ready erreicht haben. In produktiven Umgebungen sollten Nodes nacheinander cordoned und drained werden, während die Einsatzbereitschaft von Cilium pro Node überprüft wird. In neuen Clustern ohne Workloads kann die Umstellung in einem Durchgang erfolgen. Für Tests empfiehlt sich ein dedizierter Cluster.
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium --version 1.16.2 \
--namespace kube-system \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true
Erwartete Ausgabe:
"cilium" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "cilium" chart repository
Update Complete.
NAME: cilium
LAST DEPLOYED: Sat Feb 21 10:00:00 2026
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
Warten Sie anschließend, bis das Rollout abgeschlossen ist:
kubectl -n kube-system rollout status ds/cilium --timeout=300s
Erwartete Ausgabe:
daemon set "cilium" successfully rolled out
Installieren Sie nun cilium-cli:
Der Befehl cilium status benötigt die separate Binärdatei cilium-cli, die nicht automatisch mit der Helm-Installation bereitgestellt wird. Installieren Sie sie daher jetzt:
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
curl -L --fail --remote-name-all \
https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz
sudo tar xzvf cilium-linux-${CLI_ARCH}.tar.gz -C /usr/local/bin
Hinweis: Unter macOS ersetzen Sie linux-amd64 durch darwin-amd64.
Prüfen Sie anschließend Cilium und bestätigen Sie, dass das CNI aktiv ist:
kubectl get pods -n kube-system -l k8s-app=cilium
cilium status
Erwartete Ausgabe für kubectl get pods:
NAME READY STATUS RESTARTS AGE
cilium-4xk2p 1/1 Running 0 2m
cilium-9rtzq 1/1 Running 0 2m
cilium-vbn3w 1/1 Running 0 2m
Erwartete Ausgabe für cilium status:
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode)
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
KVStore: Ok
Kubernetes: Ok 1.29+ (v1.29.0) [linux/amd64]
NodeMonitor: Listening for events on 4 CPUs
Cilium health daemon: Ok
IPAM: IPv4: 5/254 allocated
Stellen Sie sicher, dass alle Cilium-Pods den Status Ready haben und dass cilium status sowohl den Datapath als auch den Policy-Modus korrekt bestätigt. Wenn Sie cilium-cli nicht installieren möchten, können Sie alternativ diesen Befehl verwenden:
kubectl exec -n kube-system ds/cilium -- cilium status
Damit ist die grundlegende Einrichtung von Cilium in einem verwalteten Kubernetes-Cluster abgeschlossen.
Schritt 2 – Eine Demo-Anwendung mit mehreren Services bereitstellen
Erstellen Sie die Namespaces, versehen Sie sie mit passenden Labels, damit die Allow-Policy aus Schritt 3 korrekt greifen kann, stellen Sie die Pods bereit und erzeugen Sie Services, damit die DNS-Auflösung funktioniert. Nginx lauscht auf Port 80. Wenden Sie an dieser Stelle noch keine NetworkPolicy an, damit die Ausgangsbasis zunächst vollständig offen bleibt.
kubectl create namespace frontend
kubectl create namespace backend
kubectl create namespace database
kubectl label namespace frontend name=frontend
kubectl label namespace backend name=backend
kubectl label namespace database name=database
kubectl create deployment frontend -n frontend --image=nginx
kubectl label deployment frontend -n frontend app=frontend
kubectl create deployment backend -n backend --image=kennethreitz/httpbin
kubectl label deployment backend -n backend app=backend
kubectl create deployment db -n database --image=redis:alpine
kubectl label deployment db -n database app=database
kubectl expose deployment frontend -n frontend --port=80 --target-port=80
kubectl expose deployment backend -n backend --port=80 --target-port=80
Hinweis: Das Backend verwendet kennethreitz/httpbin, da dieses Image Routen wie /get, /post und /anything bereitstellt. Das ist notwendig, um die Layer-7-Validierung in Schritt 4 sauber testen zu können. Ein reines Nginx-Image stellt keine Pfade wie /health oder /admin bereit und würde deshalb mit 404 antworten statt mit den erwarteten HTTP-Codes.
Prüfen Sie vor dem nächsten Schritt, dass alle Pods in den jeweiligen Namespaces den Status Ready erreicht haben:
kubectl get pods -n frontend
kubectl get pods -n backend
kubectl get pods -n database
Erwartete Ausgabe (die Pod-Namen werden abweichen):
NAME READY STATUS RESTARTS AGE
frontend-6d4b7f9c8d-xk2p9 1/1 Running 0 30s
NAME READY STATUS RESTARTS AGE
backend-7f8b9d6c4d-p2r4x 1/1 Running 0 30s
NAME READY STATUS RESTARTS AGE
db-5c6d7e8f9a-m3n4o 1/1 Running 0 30s
Prüfen Sie danach die Ausgangskonnektivität aus dem Frontend-Namespace:
kubectl run curl-test --rm -it --restart=Never \
-n frontend \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" http://backend.backend.svc.cluster.local
Erwartete Ausgabe:
200
Ein HTTP-Statuscode 200 zeigt, dass die Verbindung funktioniert, bevor überhaupt eine NetworkPolicy aktiv ist. Falls stattdessen ein Verbindungsfehler erscheint, prüfen Sie zuerst, ob der Backend-Service vorhanden ist:
kubectl get svc -n backend
Erwartete Ausgabe:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend ClusterIP 10.245.78.123 <none> 80/TCP 1m
Warnung: Wenden Sie keine Default-Deny-Policy in einem produktiven Namespace an, bevor Sie mit hubble observe die vorhandenen Traffic-Muster erfasst haben. Eine sofort aktivierte Default-Deny-Regel unterbricht sämtlichen eingehenden Traffic, darunter Health-Checks, Service-zu-Service-Kommunikation und Monitoring-Scrapes. Führen Sie Schritt 5 zunächst in einer Staging-Umgebung aus, beobachten Sie die aktiven Flows und erstellen Sie daraus Ihre Allow-List, bevor Sie produktive Workloads absichern.
Schritt 3 – Eine grundlegende Kubernetes NetworkPolicy anwenden
Wenden Sie zuerst eine Default-Deny-Ingress-Policy in dem Namespace an, den Sie isolieren möchten, zum Beispiel backend:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: backend
spec:
podSelector: {}
policyTypes:
- Ingress
Speichern Sie die Policy in einer Datei und wenden Sie sie an:
kubectl apply -f default-deny-ingress.yaml
Erwartete Ausgabe:
networkpolicy.networking.k8s.io/default-deny-ingress created
Erlauben Sie anschließend Ingress ausschließlich aus dem Frontend-Namespace oder alternativ über einen noch spezifischeren Selector:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend
namespace: backend
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: frontend
ports:
- protocol: TCP
port: 80
Speichern Sie auch diese Policy in einer Datei und wenden Sie sie an:
kubectl apply -f allow-from-frontend.yaml
Erwartete Ausgabe:
networkpolicy.networking.k8s.io/allow-from-frontend created
Diese Allow-Regel greift für Namespaces mit dem Label name=frontend, das Sie bereits in Schritt 2 gesetzt haben.
Nachdem die Default-Deny-Policy aktiv ist, prüfen Sie, ob Ingress aus dem Default-Namespace nun blockiert wird:
kubectl run curl-test --rm -it --restart=Never \
-n default \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" http://backend.backend.svc.cluster.local
Erwartete Ausgabe:
000
Ein Rückgabewert 000 bedeutet, dass die Verbindung abgelehnt wurde oder in einen Timeout lief. Das bestätigt, dass die Default-Deny-Regel greift.
Wenden Sie danach die Policy allow-from-frontend an und testen Sie, ob Traffic aus dem Frontend-Namespace jetzt wieder erlaubt ist:
kubectl run curl-test --rm -it --restart=Never \
-n frontend \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" http://backend.backend.svc.cluster.local
Erwartete Ausgabe:
200
Ein 200-Status bestätigt, dass Traffic aus dem Frontend-Namespace das Backend erreichen darf. Ein Curl-Test aus dem Default-Namespace sollte weiterhin 000 liefern.
Schritt 4 – CiliumNetworkPolicy für erweiterte Layer-7-Kontrolle anwenden
Verwenden Sie CiliumNetworkPolicy, wenn Layer-7-HTTP-Filterung benötigt wird. Im folgenden Beispiel werden GET /get und GET /anything/.* erlaubt, während andere HTTP-Methoden wie POST /post für Backend-Pods blockiert werden.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: backend-http-rules
namespace: backend
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: frontend
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "GET"
path: "/get"
- method: "GET"
path: "/anything/.*"
Speichern Sie diese Policy in einer Datei und wenden Sie sie an:
kubectl apply -f backend-http-rules.yaml
Erwartete Ausgabe:
ciliumnetworkpolicy.cilium.io/backend-http-rules created
Diese Form der Kontrolle, also das gezielte Matchen von Methode und Pfad, ist mit klassischen Kubernetes NetworkPolicies nicht möglich. Sie bietet service-mesh-ähnliche Funktionen, ohne dass ein vollständiges Service Mesh erforderlich ist.
Der Label-Schlüssel k8s:io.kubernetes.pod.namespace ist eine Cilium-spezifische Kennung, die auf den Kubernetes-Pod-Namespace abbildet. Er ist nicht mit dem Feld namespaceSelector aus einer Standard-NetworkPolicy identisch. In CiliumNetworkPolicy und dort speziell in fromEndpoints werden Namespaces über diesen internen Schlüssel referenziert und nicht über Labels des Namespace-Objekts selbst. Wenn ein anderer Namespace adressiert werden soll, ersetzen Sie den Wert frontend durch den gewünschten Namespace-Namen. Um zu prüfen, welche Labels Cilium einer Pod-Identität zuweist, führen Sie folgenden Befehl aus:
kubectl exec -n kube-system ds/cilium -- cilium endpoint list
Erwartete Ausgabe (gekürzt):
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS
1234 Enabled Disabled 16385 k8s:app=backend
k8s:io.kubernetes.pod.namespace=backend
5678 Enabled Disabled 16386 k8s:app=frontend
k8s:io.kubernetes.pod.namespace=frontend
Damit werden alle Endpoints gemeinsam mit den von Cilium zugewiesenen Identitäts-Labels aufgelistet, darunter auch k8s:io.kubernetes.pod.namespace.
Um die L7-Policy zu überprüfen, testen Sie nun einen erlaubten Pfad und eine blockierte Methode aus dem Frontend-Namespace:
# Allowed: GET /get from frontend
kubectl run curl-test --rm -it --restart=Never \
-n frontend \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" \
http://backend.backend.svc.cluster.local/get
Erwartete Ausgabe:
200
# Blocked: POST /post from frontend
kubectl run curl-test --rm -it --restart=Never \
-n frontend \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" -X POST \
http://backend.backend.svc.cluster.local/post
Erwartete Ausgabe:
403
Ein HTTP-Status 403 zeigt, dass Cilium den Zugriff auf Methodenebene in Layer 7 durchsetzt. Die GET-Anfrage auf /get ist durch die L7-Regel erlaubt, während die POST-Anfrage auf /post nicht in der Allow-List enthalten ist und deshalb vom HTTP-Proxy von Cilium verworfen wird. Hubble stellt diese Vorgänge als ALLOWED– beziehungsweise DROPPED-Flows dar.
Schritt 5 – Hubble für Observability aktivieren und nutzen
Hubble Relay läuft innerhalb des Clusters, aber um es abzufragen, benötigen Sie die Hubble-CLI. Installieren Sie diese, bevor Sie hubble-Befehle verwenden:
HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
curl -L --fail --remote-name-all \
https://github.com/cilium/hubble/releases/download/${HUBBLE_VERSION}/hubble-linux-amd64.tar.gz
tar xzvf hubble-linux-amd64.tar.gz
sudo mv hubble /usr/local/bin/
hubble version
Erwartete Ausgabe:
hubble v0.13.0 compiled with go1.21.5 on linux/amd64
Hinweis: Unter macOS ersetzen Sie linux-amd64 durch darwin-amd64. Stellen Sie vor dem Fortfahren sicher, dass die Binärdatei mit hubble version korrekt erreichbar ist.
Leiten Sie danach den Hubble-Relay-Service lokal weiter:
kubectl port-forward -n kube-system svc/hubble-relay 4245:4245
Erwartete Ausgabe:
Forwarding from 127.0.0.1:4245 -> 4245
Forwarding from [::1]:4245 -> 4245
Lassen Sie dieses Terminal geöffnet und verwenden Sie für die nächsten Befehle ein zweites Terminal.
Setzen Sie dort zunächst die Server-Adresse und beobachten Sie anschließend den Traffic:
export HUBBLE_SERVER=localhost:4245
hubble observe --since 1m --namespace backend
Wenn Sie ausschließlich verworfene Flows sehen möchten, verwenden Sie:
hubble observe --since 1m --verdict DROPPED --namespace backend
Beispielausgabe, deren Format abweichen kann:
Sep 15 10:01:00.123: frontend/frontend -> backend/backend:80 (HTTP) ALLOWED
Sep 15 10:01:00.456: default/curl-xxx -> backend/backend:80 (HTTP) DROPPED
Hubble zeigt damit transparent an, welche Verbindungen erlaubt und welche blockiert wurden. Diese Informationen helfen dabei, Policy-Verhalten zu überprüfen und Konnektivitätsprobleme wie ein falsches Namespace-Label oder einen nicht freigegebenen Port schneller einzugrenzen.
Praxisnahes Multi-Tenant-Szenario
In einem gemeinsam genutzten Cluster arbeiten Mandant A und Mandant B jeweils in einem eigenen Namespace. Ein API-Gateway beziehungsweise ein Ingress-Namespace dient als einziger Einstiegspunkt von außen. Interne Datenbanken werden in einem separaten Namespace betrieben.
Isolation auf Namespace-Ebene: Jeder Mandanten-Namespace besitzt eine Default-Deny-Ingress-Policy. Eingehender Traffic ist ausschließlich aus dem Namespace des API-Gateways oder Ingress-Controllers und nur auf explizit freigegebenen Ports erlaubt.
Egress: Mandanten-Workloads erhalten Egress-Regeln, die nur die wirklich benötigten externen APIs wie DNS oder Paket-Registries sowie interne Dienste wie den Datenbank-Namespace zulassen. Dadurch werden Seitwärtsbewegungen eingeschränkt und die potenzielle Auswirkung eines Vorfalls reduziert. Für DNS wird beispielsweise UDP-Port 53 in den Namespace freigegeben, in dem CoreDNS läuft, oft kube-system. Eine Beispiel-Policy für DNS-Egress in einem Mandanten-Namespace sieht so aus:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: tenant-a
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
Zero Trust: Zwischen Namespaces gibt es kein implizites Vertrauen. Jeder Kommunikationspfad muss explizit in einer Allow-List freigegeben werden. Hubble-Flow-Logs liefern dabei einen nachvollziehbaren Prüfpfad für jeden einzelnen Verbindungsversuch zwischen Mandanten-Grenzen.
Fehlerbehebung
Policy wird nicht durchgesetzt
Symptom: Traffic ist trotz Policy weiterhin unerwartet erlaubt oder wird unerwartet blockiert.
Wahrscheinliche Ursachen:
- Der
podSelectorpasst nicht zu den vorgesehenen Pods. - Das Label im
namespaceSelectorstimmt nicht mit dem Namespace überein. - Die Policy wurde im falschen Namespace angewendet.
Schrittweise Lösung:
Prüfen Sie zunächst, ob die Policy im richtigen Namespace existiert:
kubectl get networkpolicy -n backend
Erwartete Ausgabe:
NAME POD-SELECTOR AGE
allow-from-frontend <none> 5m
default-deny-ingress <none> 6m
Sehen Sie sich danach die verwendeten Selektoren an:
kubectl describe networkpolicy -n backend
Kontrollieren Sie die Pod-Labels:
kubectl get pods -n backend --show-labels
Erwartete Ausgabe:
NAME READY STATUS RESTARTS AGE LABELS
backend-7d6b9f8c4d-p2r4x 1/1 Running 0 10m app=backend,pod-template-hash=7d6b9f8c4d
Überprüfen Sie anschließend die Namespace-Labels:
kubectl get namespace frontend --show-labels
Erwartete Ausgabe:
NAME STATUS AGE LABELS
frontend Active 15m kubernetes.io/metadata.name=frontend,name=frontend
Prüfen Sie außerdem den Status von Cilium:
cilium status
Cilium-Pods befinden sich im CrashLoop oder sind nicht bereit
Symptom: Cilium-Pods im Namespace kube-system erreichen den Status Ready nicht oder starten fortlaufend neu.
Wahrscheinliche Ursachen:
- Der Kernel unterstützt die benötigten eBPF-Funktionen nicht.
- Ein anderes CNI verursacht Konflikte mit Cilium.
- Die Nodes verfügen nicht über ausreichende Ressourcen.
Schrittweise Lösung:
Prüfen Sie zuerst die Logs der Cilium-Pods:
kubectl logs -n kube-system -l k8s-app=cilium
Stellen Sie danach sicher, dass alle Pods des Cilium-DaemonSets laufen:
kubectl get ds cilium -n kube-system
Erwartete Ausgabe:
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
cilium 3 3 3 3 3 <none> 20m
Vergewissern Sie sich abschließend, dass kein weiteres CNI aktiv ist und dass die Nodes die Kernel-Voraussetzungen für eBPF erfüllen.
Hubble zeigt keine Flows an
Symptom: hubble observe liefert keine Ausgabe.
Wahrscheinliche Ursachen:
- Hubble Relay läuft nicht.
- Das Port-Forwarding ist nicht aktiv.
- Im beobachteten Zeitraum wurde kein Traffic erzeugt.
Schrittweise Lösung:
Prüfen Sie zunächst Hubble Relay:
kubectl get pods -n kube-system -l k8s-app=hubble-relay
Erwartete Ausgabe:
NAME READY STATUS RESTARTS AGE
hubble-relay-5d8b9f7c6d-xk2p9 1/1 Running 0 15m
Stellen Sie anschließend sicher, dass das Port-Forwarding noch aktiv ist:
kubectl port-forward -n kube-system svc/hubble-relay 4245:4245
Erzeugen Sie anschließend Test-Traffic und starten Sie die Beobachtung erneut:
hubble observe --since 1m
DNS- oder externe API-Aufrufe schlagen nach Policy-Durchsetzung fehl
Symptom: Pods können nach Anwendung von Egress-Policies weder DNS auflösen noch externe APIs erreichen.
Wahrscheinliche Ursachen:
- Die Egress-Regeln erlauben keinen Traffic zu kube-dns beziehungsweise CoreDNS.
- Externe Ziele wurden nicht ausdrücklich freigegeben.
Schrittweise Lösung:
Prüfen Sie zunächst, ob DNS-Traffic verworfen wird:
hubble observe --since 1m --verdict DROPPED
Ergänzen Sie eine Egress-Regel, die DNS über UDP/TCP Port 53 zu dem Namespace erlaubt, in dem kube-dns läuft.
Definieren Sie zusätzlich explizite Egress-Regeln für benötigte externe IP-Adressen oder verwenden Sie bei Bedarf Cilium-FQDN-Policies.
Wenn Sie eine sehr detaillierte Paketansicht auf Node-Ebene benötigen, führen Sie cilium monitor direkt in einem Cilium-Pod aus. Dieser Befehl liefert einen rohen Ereignisstrom aus dem eBPF-Datapath. Die Ausgabe ist deutlich feiner aufgelöst als bei Hubble, aber auch schwerer zu filtern:
kubectl exec -n kube-system ds/cilium -- cilium monitor --type drop
Die Option --type drop beschränkt die Ausgabe auf verworfene Pakete. Dieser Ansatz ist hilfreich, wenn hubble observe nicht genügend Details für einen bestimmten Flow liefert. Für die meisten Policy-Analysen ist jedoch hubble observe --verdict DROPPED schneller und übersichtlicher:
hubble observe --since 1m --verdict DROPPED
Zusammen decken diese beiden Werkzeuge das gesamte Spektrum der Policy-Fehlersuche ab: Hubble für clusterweite Flow-Analysen und cilium monitor für nodebasierte Paketinspektion auf niedriger Ebene, wenn tiefere Details erforderlich sind.
FAQs
Was sind Kubernetes NetworkPolicies?
Sie sind API-Ressourcen, die definieren, welcher ein- und ausgehende Traffic für ausgewählte Pods zulässig ist. Die Durchsetzung übernimmt das CNI. Cilium setzt diese Regeln mithilfe von eBPF direkt im Kernel um.
Wodurch unterscheidet sich Cilium von anderen CNIs?
Cilium nutzt eBPF sowohl für die Verarbeitung im Datapath als auch für die Durchsetzung von Richtlinien, unterstützt Layer-7-HTTP-Policies über CiliumNetworkPolicy und bringt mit Hubble eine integrierte Observability-Schicht mit. Viele andere CNIs setzen auf iptables und bieten weder L7-Regeln noch Sichtbarkeit auf Flow-Ebene.
Was ist eBPF und warum wird es in Kubernetes verwendet?
eBPF ist ein Kernel-Mechanismus, mit dem sichere und effiziente Programme auf Netzwerk- und andere Systemereignisse reagieren können. Cilium nutzt eBPF, um NetworkPolicies und CiliumNetworkPolicies direkt im Kernel durchzusetzen. Das reduziert Overhead und ermöglicht sowohl Layer-7-Steuerung als auch Observability.
Was ist Hubble in Cilium?
Hubble ist die Observability-Komponente von Cilium. Sie sammelt Flow- und Metrikdaten von den Cilium-Agenten über eBPF und stellt diese so bereit, dass erlaubter und blockierter Traffic ausgewertet werden kann, beispielsweise mit hubble observe oder über die Hubble-Oberfläche.
Wie kann ich Traffic in einem Kubernetes-Cluster sichtbar machen?
Mit Cilium und Hubble lässt sich hubble observe mit Filtern wie Namespace, Label oder Verdict verwenden. Alternativ kann Hubble UI bereitgestellt werden, um Flows visuell zu untersuchen. So entsteht L3/L4-Sichtbarkeit und, sofern L7-Policies aktiv sind, auch Einblick in HTTP-Traffic, ohne dass ein Service Mesh notwendig ist.
Kann Cilium Layer-7-Policies durchsetzen?
Ja. CiliumNetworkPolicy unterstützt L7-Regeln wie das Filtern nach HTTP-Methode und Pfad. Die Standard-Kubernetes-NetworkPolicy deckt nur L3/L4 ab.
Wie verbessern NetworkPolicies die Sicherheit?
Sie schränken ein, welche Pods miteinander kommunizieren dürfen und welche Workloads externe Ziele erreichen können. In Verbindung mit Default-Deny und expliziten Allow-Lists entsteht eine saubere Segmentierung, die Seitwärtsbewegungen erschwert und gut zu Zero-Trust- sowie Compliance-orientierten Sicherheitsmodellen passt.
Wird Cilium in verwalteten Kubernetes-Umgebungen unterstützt?
Cilium lässt sich auf vielen verwalteten Kubernetes-Plattformen entweder als aktives CNI oder für erweiterte Funktionen wie Gateway API und L7-Policies einsetzen. Vor dem Einsatz sollten jedoch immer die Kompatibilität mit der eigenen Kubernetes-Version und die Vorgaben des jeweiligen Anbieters geprüft werden.
Fazit
Kubernetes NetworkPolicies allein reichen nicht aus, wenn kein CNI ihre Regeln tatsächlich durchsetzt. Ohne Observability ist es zudem schwierig zu prüfen, ob die gewünschten Effekte wirklich eintreten. Cilium stellt eine effiziente, eBPF-basierte Durchsetzung bereit, während Hubble die nötige Sichtbarkeit liefert, um jede Policy-Änderung vor und nach dem Rollout nachvollziehen zu können. In verwalteten Kubernetes-Umgebungen lässt sich Zero-Trust-Networking und Service-Isolation schrittweise umsetzen: zunächst durch Beobachtung des Basis-Traffics mit Hubble, anschließend durch Default-Deny und gezielte Allow-Lists und schließlich durch CiliumNetworkPolicy, wenn Layer-7-Kontrolle erforderlich ist.


