Cilium Cheatsheet

Cilium CheatsheetAlle Kapitel

Cilium Cheatsheet im Volltext

Stand Cilium 1.20 · Edition 2026.11

Cheatsheet im Volltext

Derselbe Inhalt wie im PDF, zum Mitlesen, Durchsuchen und direkten Kopieren der YAML-Snippets. Stand: Cilium v1.20.4 (Edition 2026.11).

Architektur & Identity

eBPF-Datapath

CNI mit eBPF-Datapath: Networking, Load-Balancing, Policy und Observability laufen als eBPF-Programme im Kernel, mit kube-proxy-Replacement ohne iptables-Ketten pro Service.

Komponenten: cilium-agent (DaemonSet, je Node), cilium-operator (clusterweite Aufgaben, z.B. IPAM), cilium-envoy (DaemonSet, L7-Proxy), Hubble (Observability), cilium-CLI. CNCF-graduated.

Identity statt IP

Jeder Endpoint bekommt eine clusterweite Security-Identity, abgeleitet aus den security-relevanten Pod-Labels. Endpoints mit identischem Label-Set teilen eine Identity.

Policy-Entscheidungen laufen gegen die Identity, nicht gegen flüchtige Pod-IPs. Das skaliert unabhängig von der Endpoint-Zahl.

cilium status --wait
kubectl get ciliumendpoints -A   # Identity je Pod
kubectl get ciliumidentities

Install & kube-proxy-Replacement

Helm-Install

Ohne kube-proxy müssen k8sServiceHost / k8sServicePort gesetzt sein, sonst findet der Agent den API-Server nicht (den Service löst sonst niemand auf). Chart per OCI, cosign-signiert, Version ohne v pinnen (oder per Digest).

helm install cilium \
  oci://quay.io/cilium/charts/cilium \
  --version 1.20.4 -n kube-system \
  --set kubeProxyReplacement=true \
  --set k8sServiceHost=<api-server-ip> \
  --set k8sServicePort=6443

kube-proxy-Replacement

eBPF übernimmt ClusterIP, NodePort, LoadBalancer, externalIPs und HostPort.

Socket-LB: übersetzt Service auf Backend schon beim connect()/sendmsg(), kein Paket-Rewriting auf dem Pfad.

Mit Istio (Sidecar oder Ambient) immer cni.exclusive=false, damit Istios CNI-Plugin erhalten bleibt. Kube-proxy-frei zusätzlich socketLB.hostNamespaceOnly=true, sonst greift Socket-LB schon im Pod und stört die Umleitung zum Istio-Proxy. Die Cilium-Doku empfiehlt mit Istio weiterhin kubeProxyReplacement=false. Unter Istio-mTLS sieht Cilium kein L7, L7-Policies nicht in beiden Systemen pflegen.

kubectl -n kube-system exec ds/cilium -- \
  cilium-dbg status --verbose | \
  grep KubeProxyReplacement

LoadBalancer ohne Cloud

IP-Pools per CiliumLoadBalancerIPPool (cilium.io/v2), auch für den Gateway-Service. Bekanntgabe per L2 (l2announcements.enabled=true, Beta, braucht kube-proxy-Replacement und ein höheres k8sClientRateLimit, plus CiliumL2AnnouncementPolicy) oder BGP (bgpControlPlane.enabled=true, v2-CRDs). cilium-dbg bgp ist seit 1.20 deprecated, Ersatz im Agent-Pod cilium-dbg shell -- bgp/*.

CiliumNetworkPolicy (L3/L4)

Selektoren, Entities, CIDR

endpointSelector wählt die Ziel-Endpoints; fromEndpoints/toEndpoints selektieren per Label (Identity).

toEntities: world, cluster, cluster-mesh (seit 1.20, alle verbundenen Cluster), host, remote-node, kube-apiserver.

toCIDR/toCIDRSet: für externe Ziele, Cluster-intern per Label selektieren.

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata: {name: backend-allow, namespace: shop}
spec:
  endpointSelector:
    matchLabels: {app: backend}
  ingress:
  - fromEndpoints:
    - matchLabels: {app: frontend}
    toPorts:
    - ports:
      - {port: "8080", protocol: TCP}

Clusterwide & Enforcement-Modi

CiliumClusterwideNetworkPolicy: gleiche Syntax, nicht namespaced, für Baselines über alle Namespaces. Baseline ohne Deny-Nebenwirkung: enableDefaultDeny: {ingress: false, egress: false}, gilt nicht für L7-Regeln (L7 ohne Allow-all droppt trotzdem).

policyEnforcementMode: default (alles offen, bis eine Regel den Endpoint selektiert, dann default-deny je Richtung), always, never.

L7-Policies & toFQDNs

HTTP-Rules (L7)

L7-Regeln hängen unter toPorts.rules.http: method, path, host als POSIX-Regex. Header nie per Regex: headers prüft Präsenz, als "Name: Wert" den exakten Wert; headerMatches vergleicht exakt (Wert auch aus Secret, mismatch steuert die Reaktion). Durchsetzung via node-lokalem Envoy; Verstoß liefert HTTP 403 statt Paket-Drop.

Weiterer L7-Typ: dns. Kafka- und proxylib-Regeln (kafka, l7, l7proto) sind seit 1.20 entfernt und müssen vor dem Upgrade aus den Policies raus.

spec:
  endpointSelector:
    matchLabels: {app: api}
  ingress:
  - fromEndpoints:
    - matchLabels: {app: frontend}
    toPorts:
    - ports:
      - {port: "80", protocol: TCP}
      rules:
        http:
        - method: GET
          path: /v1/.*

toFQDNs (Egress nach Namen)

matchName exakt, matchPattern mit Wildcard.

Pflicht-Voraussetzung: eine rules.dns-Regel schaltet den DNS-Proxy ein, ohne DNS-Sichtbarkeit kann Cilium FQDNs nicht auf IPs mappen und die Policy läuft leer. Selektoren ohne Namespace-Label gelten in einer CNP nur im eigenen Namespace, kube-dns also mit Namespace selektieren.

egress:
- toEndpoints:
  - matchLabels:
      k8s:io.kubernetes.pod.namespace: kube-system
      k8s-app: kube-dns
  toPorts:
  - ports: [{port: "53", protocol: ANY}]
    rules:
      dns: [{matchPattern: "*"}]
- toFQDNs:
  - matchName: api.example.com
  - matchPattern: "*.storage.example.com"
  toPorts:
  - ports: [{port: "443", protocol: TCP}]

Hubble (Observability)

Flows, Relay, UI

Hubble liest Flows direkt aus dem eBPF-Datapath, ohne Sidecars oder Paket-Mirroring. L7-Flows (HTTP, DNS) gibt es nur für Traffic, den eine L7-Regel über den Proxy leitet, und diese Regel filtert dann auch.

Relay aggregiert clusterweit (auch über ClusterMesh), UI zeigt die Service-Dependency-Map, die CLI (hubble) filtert Flows live.

Redaction: Hubble schwärzt L7-Daten (Query-Parameter, Header) standardmäßig nicht. hubble.redact.enabled=true plus hubble.redact.http.urlQuery=true, Header per hubble.redact.http.headers.allow oder .deny (nur eine der beiden Listen).

cilium hubble enable --ui
cilium hubble port-forward &
hubble status
hubble observe --namespace shop \
  --verdict DROPPED
hubble observe --protocol http --last 20

Metriken

Hubble-Metriken für Prometheus per Helm: hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,httpV2}".

Agent: prometheus.enabled=true (Default aus),
Operator: operator.prometheus.enabled (Default an).

Encryption

WireGuard

Transparente Verschlüsselung des Pod-Traffics zwischen Nodes (Device cilium_wg0, UDP 51871, Firewall freischalten!). Key-Paar erzeugt jeder Node selbst, Public-Key-Austausch via CiliumNode-Annotation.

Node-zu-Node zusätzlich: encryption.nodeEncryption=true. Nodes mit Label node-role.kubernetes.io/control-plane nimmt Cilium davon automatisch aus. Same-Node-Traffic bleibt unverschlüsselt.

Strict Mode: encryption.strictMode.egress.enabled=true plus .egress.cidr (Pod-CIDR, nur IPv4) verhindert unverschlüsselte Erstpakete. encryption.strictMode.ingress.enabled=true (nur WireGuard) droppt unverschlüsselten Pod-Traffic. Die alten Schlüssel encryption.strictMode.enabled/.cidr sind seit 1.20 entfernt.

# Konfig-Änderung: --version = installierte Chart-Version
helm upgrade cilium \
  oci://quay.io/cilium/charts/cilium \
  --version 1.20.4 -n kube-system \
  --reuse-values \
  --set encryption.enabled=true \
  --set encryption.type=wireguard
kubectl -n kube-system exec ds/cilium -- \
  cilium-dbg status | grep Encryption

IPsec

encryption.type=ipsec; Key liegt im Secret cilium-ipsec-keys (Cilium-Namespace), z.B. "3+ rfc4106(gcm(aes)) <key> 128". Das + erzwingt Per-Tunnel-Keys, globale Keys gelten als unsicher (GHSA-pwqm-x5x6-5586).

Key-Rotation ist manuell (KEYID 1–15 inkrementieren), nie während eines Upgrades rotieren.

ztunnel (Beta)

encryption.type=ztunnel: L4-mTLS je Node über Istios ztunnel, Enrollment je Namespace (Label io.cilium/mtls-enabled=true), CA internal (Default) oder spire. Nicht mit ClusterMesh kombinierbar. Nur TCP, beide Seiten müssen eingeschrieben sein, braucht iptables; L4-Policies greifen nur noch auf Port 15008 (HBONE). Empfohlene Alternative zur Mutual Authentication (seit 1.20 deprecated, in v1.21.0-pre.3 entfernt).

ClusterMesh

Multi-Cluster-Verbund

Pod-Konnektivität über Cluster-Grenzen, globale Services mit Cross-Cluster-Load-Balancing. Policies selektieren seit 1.19 nur den lokalen Cluster, Remote-Endpoints explizit per Label io.cilium.k8s.policy.cluster.

Voraussetzungen: PodCIDRs aller Cluster disjunkt, eindeutiger cluster.name + cluster.id (1–255, mit clustermesh.maxConnectedClusters=511 bis 511, nur bei der Installation setzbar, in allen Clustern gleich), gegenseitiges TLS-Vertrauen (gemeinsame CA, z.B. cilium-ca kopieren, oder tls.caBundle), IP-Konnektivität zwischen allen Nodes, clustermesh-apiserver gegenseitig erreichbar.

# CLI-Weg (Lab, Demo)
cilium clustermesh enable --context c1
cilium clustermesh connect --context c1 \
  --destination-context c2
cilium clustermesh status --wait

Helm-Setup & Zertifikate

Setup laut Doku Helm-first: clustermesh.useAPIServer=true, clustermesh.config.enabled=true, Remote-Cluster unter clustermesh.config.clusters. Die CLI ist ein Helm-Wrapper für schnelle Setups (Lab, Demo).

Zertifikate: seit 1.20 gelten auto-generierte Cluster-Mesh-Zertifikate ein Jahr. Methode helm (Default) erneuert nur beim Re-Render, also beim Upgrade. Produktiv cronJob (erneuert alle 4 Monate) oder certmanager, oder clustermesh.apiserver.tls.auto.certValidityDuration setzen.

Globale Services

Annotation service.cilium.io/global: "true" auf dem gleichnamigen Service in beiden Clustern, Cilium load-balanct auf die Backends beider Cluster.

service.cilium.io/shared: "false": Remote-Backends nutzen, eigene nicht teilen.

Alternative zu globalen Services: MCS-API (clustermesh.mcsapi.enabled=true, seit 1.20 stabil, CRDs v1beta1, braucht CoreDNS ab 1.12.2 für clusterset.local).

Gateway API

Eingebauter Controller

Cilium implementiert die Gateway API. CRDs vorab installieren, mindestens v1.6.1, die Doku zu 1.20.4 installiert v1.6.3. Cilium führt Gateway API v1.7.0 (29.10.2026) noch nicht. Pflicht: GatewayClass, Gateway, HTTPRoute, GRPCRoute, BackendTLSPolicy, ReferenceGrant, TLSRoute. Optional ListenerSet und TCPRoute/UDPRoute (seit Gateway API 1.6 GA, v1; Traffic läuft am Envoy vorbei direkt auf den Gateway-Service; im Host-Network-Modus nicht nutzbar). Fehlt eine optionale CRD, ist das Feature aus. Gateway-Service per Default vom Typ LoadBalancer.

Voraussetzungen: kubeProxyReplacement=true, l7Proxy=true (Default), gatewayAPI.enabled=true. Ohne bpf.tproxy=true (beta) läuft der Redirect zu Envoy über iptables-TPROXY: fehlen die Netfilter-Module, laufen Verbindungen in Timeouts. GatewayClass heißt cilium.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata: {name: web-gw, namespace: shop}
spec:
  gatewayClassName: cilium
  listeners:
  - {name: http, port: 80, protocol: HTTP}
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: {name: web, namespace: shop}
spec:
  parentRefs: [{name: web-gw}]
  rules:
  - matches:
    - path: {type: PathPrefix, value: /}
    backendRefs: [{name: web, port: 80}]

Diagnose (cilium-CLI)

Werkzeuge

cilium status --wait: Health aller Komponenten.
cilium connectivity test: E2E-Testsuite im Cluster.
cilium sysdump: Support-Archiv für Bug-Reports.

Im Agent-Pod: cilium-dbg (endpoint list, policy get, monitor).

cilium connectivity test
cilium sysdump --output-filename dump
kubectl -n kube-system exec ds/cilium -- \
  cilium-dbg endpoint list
kubectl -n kube-system exec ds/cilium -- \
  cilium-dbg monitor --type drop

Neu in v1.20

Highlights (29.07.2026)

ClusterNetworkPolicy (network-policy-api, v1alpha2): opt-in per k8sClusterNetworkPolicy.enabled=true, CRD separat installieren. Tier Admin schlägt auch CNP/CCNP, Baseline greift erst nach ihnen. Volle API-Konformität verlangt --policy-cidr-match-mode=pods,nodes (Helm policyCIDRMatchMode={pods,nodes}, Beta), weil die API CIDR-Regeln auch auf Pod- und Node-IPs anwendet; pods kostet je getroffenem Pod eine zusätzliche Identity.

Gateway API v1.6.1: TCPRoute/UDPRoute GA, delegierte Listener (ListenerSets), ExternalAuth-Filter, Backend-TLS.

netkit automatisch (Beta): bpf.datapathMode=auto nimmt netkit, wenn der Kernel es kann (ab 6.8, eBPF-Host-Routing nötig), sonst veth; Default bleibt veth. Kein In-place-Wechsel: erkennt auto netkit auf einem Node mit bestehenden veth-Pods, startet Cilium mit Fehler. Mit bpf.tproxy=true fällt auto immer auf veth zurück, explizites netkit plus tproxy startet nicht.

Traffic Distribution: PreferSameZone / PreferSameNode werden ausgewertet; Maglev beachtet service.cilium.io/weight auf EndpointSlices (Gewicht 0 = draining, Bestandsverbindungen laufen weiter).

MCS-API stabil für portable Service-Discovery über ClusterMesh.

Getestet gegen Kubernetes 1.33 bis 1.36 (ältere ohne Support, 1.37 erst im Entwicklungszweig für 1.21 gelistet), Envoy je nach Patch 1.37 bis 1.39. Das cilium-cni-Binary schrumpft von rund 77 auf 16 MB.

Upgrade-Reihenfolge

Nur eine Minor-Stufe je Upgrade oder Rollback, vorher auf den letzten Patch der laufenden Linie; upgradeCompatibility auf die Version der Erstinstallation setzen. Gateway-API-CRDs vor dem Cilium-Upgrade auf mindestens v1.6.1 heben und TLSRoute-Ressourcen vorher sichern. Kafka-/l7-/l7proto-Regeln vorher aus den Policies entfernen, CiliumNodeConfig auf cilium.io/v2 umstellen. Ziel mindestens 1.20.4: 1.20.0 und 1.20.1 scheitern auf Clustern, die mit 1.15 oder älter angelegt wurden, und ein HTTPRoute-ExternalAuth mit ungültiger Backend-Referenz schlägt erst ab 1.20.2 geschlossen fehl. 1.20.3 hat bei BackendTLSPolicy eine Regression (hohe CPU-Last, kein Forwarding), behoben in 1.20.4.

Mit kube-proxy-Replacement ohne Socket-LB oder mit socketLB.hostNamespaceOnly=true balanciert 1.20 Pod-Verbindungen auf NodePort-Services schon beim Verlassen des Client-Pods. Die Egress-Policy des Clients muss die Backends erlauben, die Ingress-Policy der Backends den Client.

Support-Fenster: drei Minor-Linien parallel (1.20.4, 1.19.10, 1.18.16 vom 23.10.2026). Die aktuelle Linie bekommt alle Bugfixes, die beiden Vorgänger nur Security- und schwere Korrektheits-Fixes. Die Releases vom 23.10. bauen mit Go 1.26.10 und verwerfen damit in den Go-Komponenten die eMudhra-Intermediates (CVE-2026-111943); zu cilium-envoy sagen die Release Notes nichts.

Anti-Patterns

Was du nicht tun solltest

toFQDNs ohne DNS-Rule: ohne rules.dns-Sichtbarkeit kein FQDN-zu-IP-Mapping, Policy blockt oder läuft leer.

kube-proxy-frei ohne k8sServiceHost/Port: Agent erreicht den API-Server nicht, Cluster-Bootstrap hängt.

CIDR-Regeln für Cluster-Traffic: toCIDR ist für externe Ziele gedacht; Pod-IPs sind flüchtig, Labels/Identities selektieren.

Default-Modus falsch verstanden: ohne selektierende Regel ist alles offen; die erste Regel schaltet den Endpoint auf default-deny (je Richtung), sofern sie nicht enableDefaultDeny abschaltet.

WireGuard ohne UDP 51871: Nodes müssen sich auf dem Port erreichen, sonst kein Tunnel.

--reuse-values beim Minor-Upgrade: neue Chart-Werte fehlen, das Template rendert falsch. Werte per helm get values sichern, auf umbenannte Keys prüfen, mit -f übergeben.

IPsec-Keys während des Upgrades rotieren: erst alle Nodes auf gleiche Cilium-Version, dann rotieren.

ClusterMesh mit überlappenden PodCIDRs oder doppelter cluster.id: Verbund kommt nicht sauber hoch, vorab planen.

kube-proxy-Replacement im laufenden Cluster umschalten: bestehende Verbindungen brechen ab. Wer kube-proxy unter kubeadm entfernt, löscht auch die ConfigMap kube-proxy, sonst kommt kube-proxy beim nächsten Upgrade zurück.