Veröffentlicht am

Kubernetes CIS Benchmark Hardening: Guide + Checkliste

Teilen:
Authors

Kubernetes CIS Benchmark Hardening: kube-bench Anleitung & Checkliste 2026

TL;DR

Der CIS Kubernetes Benchmark umfasst je nach Version über 120 Prüfpunkte -- aber in der Praxis entscheiden rund 15 wiederkehrende Failed Checks über 80 Prozent Ihres Risikos. Dieser Guide zeigt, wie Sie kube-bench korrekt ausführen (als Job im Cluster oder als Binary auf dem Node), den Report nach Automated/Level-1-Findings priorisieren und die häufigsten FAILs mit konkreten Flags an kube-apiserver, etcd und Kubelet beheben. Dazu: ergänzendes Hardening mit Pod Security Standards und NetworkPolicies, eine Checkliste zum Abhaken und die Fallstricke bei EKS, AKS und GKE, wo die Control-Plane-Checks außerhalb Ihres Zugriffs liegen.

Das Problem: CIS-Benchmark gefordert, Report hat hunderte Zeilen

Der Auslöser ist fast immer extern: Ein Enterprise-Kunde schickt einen Security-Fragebogen, der Cyber-Versicherer fragt nach Hardening-Standards, oder die NIS2-Vorbereitung verlangt "Stand der Technik" mit Nachweis. Der CIS (Center for Internet Security) Kubernetes Benchmark ist dafür die Referenz -- Auditoren, Pentester und Compliance-Scanner beziehen sich darauf.

Dann läuft der erste kube-bench-Scan und produziert hunderte Zeilen Output mit Dutzenden FAILs und WARNs. Das ist normal: Die Standardkonfiguration von kubeadm und den meisten Distributionen ist nicht CIS-konform, und der Benchmark prüft bewusst streng. Das eigentliche Problem ist nicht der Scan, sondern die Priorisierung. Ein Team mit 10 bis 100 Mitarbeitern hat kein dediziertes Security-Team, das wochenlang Findings abarbeitet.

Die gute Nachricht: Die FAILs verteilen sich nicht gleichmäßig. In kubeadm-basierten Clustern tauchen immer wieder dieselben 15 bis 20 Checks auf, und ein Großteil davon ist mit einem einzelnen Flag oder einer Zeile in der Kubelet-Konfiguration behoben. Genau diese Checks behandelt dieser Artikel.

Grundlagen: Wie der CIS-Benchmark aufgebaut ist

Der Benchmark gliedert sich in fünf Abschnitte:

AbschnittUmfangWer ist verantwortlich
1. Control Plane Componentskube-apiserver, Controller-Manager, Scheduler, Manifeste, PKI-DateirechteSie (self-managed) / Provider (managed)
2. etcdTLS, Client-Zertifikate, Peer-KommunikationSie (self-managed) / Provider (managed)
3. Control Plane ConfigurationAuthentifizierung, Audit-Logginggeteilt
4. Worker NodesKubelet-Konfiguration, Dateirechte auf dem NodeSie -- immer
5. PoliciesRBAC, Pod Security, NetworkPolicies, Secrets-HandlingSie -- immer

Zwei Unterscheidungen sind für die Priorisierung entscheidend:

  • Automated vs. Manual (in älteren Benchmark-Versionen "Scored" vs. "Not Scored"): Automated-Checks prüft kube-bench maschinell und liefert PASS/FAIL. Manual-Checks (z. B. "Minimieren Sie den Einsatz von Wildcard-RBAC") erfordern eigene Bewertung und erscheinen als WARN.
  • Level 1 vs. Level 2: Level 1 ist Basis-Härtung ohne nennenswerte Funktionseinschränkung -- die sollte jeder Produktions-Cluster erfüllen. Level 2 ist strenger und kann Workloads brechen (etwa AlwaysPullImages).

kube-bench von Aqua Security ist das Standard-Tool, um den Benchmark automatisiert zu prüfen. Es erkennt Kubernetes-Version und Distribution und wählt die passende Benchmark-Variante (generisch, EKS, AKS, GKE, RKE2, K3s). Wichtig: Die Check-Nummern verschieben sich zwischen Benchmark-Versionen leicht. Die Nummern in diesem Artikel folgen dem generischen CIS Benchmark ab v1.9 -- prüfen Sie im Report immer die Beschreibung, nicht nur die Nummer.

Praxis: kube-bench ausführen und den Report priorisiert lesen

Die sauberste Methode für einen self-managed Cluster ist ein Job direkt im Cluster, der die relevanten Host-Pfade read-only mountet:

apiVersion: batch/v1
kind: Job
metadata:
  name: kube-bench-master
  namespace: kube-system
spec:
  template:
    spec:
      hostPID: true
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""
      tolerations:
        - key: node-role.kubernetes.io/control-plane
          operator: Exists
          effect: NoSchedule
      containers:
        - name: kube-bench
          image: docker.io/aquasec/kube-bench:latest
          command: ["kube-bench", "run", "--targets", "master,etcd"]
          volumeMounts:
            - name: var-lib-kubelet
              mountPath: /var/lib/kubelet
              readOnly: true
            - name: etc-kubernetes
              mountPath: /etc/kubernetes
              readOnly: true
            - name: etc-systemd
              mountPath: /etc/systemd
              readOnly: true
      restartPolicy: Never
      volumes:
        - name: var-lib-kubelet
          hostPath:
            path: /var/lib/kubelet
        - name: etc-kubernetes
          hostPath:
            path: /etc/kubernetes
        - name: etc-systemd
          hostPath:
            path: /etc/systemd
  backoffLimit: 0

Für die Worker-Nodes derselbe Job ohne Control-Plane-nodeSelector und mit --targets node,policies. Alternativ als Binary direkt auf dem Node:

# Scan starten und Ergebnis lesen
kubectl apply -f kube-bench-job.yaml
kubectl logs -n kube-system job/kube-bench-master

# Oder als Binary auf dem Control-Plane-Node
sudo ./kube-bench run --targets master,etcd,node,policies

# JSON-Output zur Triage und Archivierung
sudo ./kube-bench run --json --outputfile cis-results.json

Den Report lesen Sie priorisiert, nicht linear. Mit jq filtern Sie zuerst die automatisierten FAILs -- das ist Ihre Arbeitsliste:

# Nur FAILs, sortiert nach Sektion
jq -r '.Controls[].tests[].results[] | select(.status=="FAIL") | "\(.test_number)  \(.test_desc)"' cis-results.json

# Zaehlung pro Status als Fortschritts-Metrik
jq '[.Controls[].tests[].results[].status] | group_by(.) | map({(.[0]): length}) | add' cis-results.json

WARNs (Manual-Checks) kommen in die zweite Runde: Dort bewerten Sie selbst, dokumentieren die Entscheidung und haken ab. Ein Cluster mit 0 FAILs und begründeten WARNs ist audit-fähig -- ein Cluster mit 40 unbearbeiteten FAILs nicht.

Die Top-15 Failed Checks und ihre Remediation

Die folgenden Checks scheitern in kubeadm-Standardinstallationen erfahrungsgemäß am häufigsten (Einschätzung aus Praxis-Audits, keine offizielle CIS-Statistik). Erst die Übersicht, dann die konkreten Konfigurationen.

#Check (CIS ab v1.9)KomponenteRemediation
11.2.1 anonymous-authkube-apiserver--anonymous-auth=false
21.2.5 kubelet-certificate-authoritykube-apiserver--kubelet-certificate-authority=/etc/kubernetes/pki/ca.crt
31.2.16 profilingkube-apiserver--profiling=false
41.2.17 audit-log-pathkube-apiserver--audit-log-path=/var/log/kubernetes/audit/audit.log
51.2.18-1.2.20 audit-log-Rotationkube-apiserver--audit-log-maxage=30 --audit-log-maxbackup=10 --audit-log-maxsize=100
61.2.22 service-account-lookupkube-apiserver--service-account-lookup=true
71.2.29 encryption-provider-configkube-apiserverSecrets-Verschlüsselung at rest (siehe unten)
81.3.2 profilingkube-controller-manager--profiling=false
91.3.6 RotateKubeletServerCertificatekube-controller-manager--feature-gates=RotateKubeletServerCertificate=true
101.4.1 profilingkube-scheduler--profiling=false
112.2 / 2.3 etcd Client-Authetcd--client-cert-auth=true --auto-tls=false
124.2.1 anonymous-authKubeletauthentication.anonymous.enabled: false
134.2.2 authorization-modeKubeletauthorization.mode: Webhook
144.2.4 read-only-portKubeletreadOnlyPort: 0
154.2.6 protect-kernel-defaultsKubeletprotectKernelDefaults: true (Vorsicht, siehe Fallstricke)

kube-apiserver (Checks 1-7)

Die Flags gehören in das statische Pod-Manifest /etc/kubernetes/manifests/kube-apiserver.yaml. Der Kubelet startet den API-Server nach dem Speichern automatisch neu -- planen Sie 30 bis 60 Sekunden API-Nichtverfügbarkeit pro Control-Plane-Node ein und ändern Sie bei HA-Setups einen Node nach dem anderen.

# /etc/kubernetes/manifests/kube-apiserver.yaml (Auszug: command)
spec:
  containers:
    - name: kube-apiserver
      command:
        - kube-apiserver
        - --anonymous-auth=false
        - --authorization-mode=Node,RBAC
        - --enable-admission-plugins=NodeRestriction
        - --kubelet-certificate-authority=/etc/kubernetes/pki/ca.crt
        - --profiling=false
        - --service-account-lookup=true
        - --audit-log-path=/var/log/kubernetes/audit/audit.log
        - --audit-log-maxage=30
        - --audit-log-maxbackup=10
        - --audit-log-maxsize=100
        - --encryption-provider-config=/etc/kubernetes/enc/encryption-config.yaml
        - --tls-min-version=VersionTLS12

Zwei Punkte, die kube-bench nicht erzwingt, aber ohne die die Flags wirkungslos sind: Der Audit-Log-Pfad muss als hostPath-Volume in das Manifest gemountet werden, sonst schreibt der API-Server ins Container-Dateisystem und die Logs verschwinden beim Neustart. Und die Encryption-Konfiguration braucht die Datei, auf die das Flag zeigt:

# /etc/kubernetes/enc/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-32-byte-key>
      - identity: {}

Nach dem Aktivieren sind nur neue Secrets verschlüsselt. Bestehende Secrets schreiben Sie einmalig um:

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

Controller-Manager, Scheduler, etcd (Checks 8-11)

Dieselbe Mechanik, andere Manifeste:

# /etc/kubernetes/manifests/kube-controller-manager.yaml (Auszug)
- --profiling=false
- --feature-gates=RotateKubeletServerCertificate=true
- --terminated-pod-gc-threshold=100

# /etc/kubernetes/manifests/kube-scheduler.yaml (Auszug)
- --profiling=false

# /etc/kubernetes/manifests/etcd.yaml (Auszug)
- --client-cert-auth=true
- --auto-tls=false
- --peer-client-cert-auth=true
- --peer-auto-tls=false

etcd verdient besondere Aufmerksamkeit: Dort liegt der komplette Cluster-State inklusive aller Secrets. Wer etcd ohne Client-Zertifikat erreicht, besitzt den Cluster -- unabhängig von jedem RBAC-Setup.

Kubelet (Checks 12-15)

Auf jedem Node gilt: Konfiguration über /var/lib/kubelet/config.yaml, nicht über deprecated Kommandozeilen-Flags. Nach der Änderung sudo systemctl restart kubelet.

# /var/lib/kubelet/config.yaml (Auszug)
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
readOnlyPort: 0
protectKernelDefaults: true
streamingConnectionIdleTimeout: 5m
makeIPTablesUtilChains: true
rotateCertificates: true
serverTLSBootstrap: true
tlsMinVersion: VersionTLS12

Der offene Read-Only-Port 10255 (Check 4.2.4) und anonyme Kubelet-API-Zugriffe (4.2.1) sind die beiden Findings mit dem direktesten Angriffspfad: Beide liefern einem Angreifer im Pod-Netz Pod-Listen, Umgebungsvariablen und teils Exec-Zugriff, ganz ohne Credentials.

Ergänzendes Hardening: Pod Security Standards und NetworkPolicy

Der Policies-Abschnitt (Sektion 5) besteht überwiegend aus Manual-Checks -- kube-bench sagt Ihnen also nicht automatisch, ob Sie hier gut dastehen. Zwei Maßnahmen decken den Großteil ab.

Pod Security Standards per Namespace-Label (Check 5.2.x). Das baseline-Profil blockiert privilegierte Container und Host-Namespaces, restricted erzwingt zusätzlich Non-Root und Capability-Drop:

kubectl label namespace produktion pod-security.kubernetes.io/enforce=baseline pod-security.kubernetes.io/warn=restricted pod-security.kubernetes.io/audit=restricted

Die Kombination aus enforce=baseline und warn=restricted ist der pragmatische Einstieg: Nichts Kritisches läuft mehr, und die Warnings zeigen, welche Workloads für restricted noch angepasst werden müssen. Details zu den Profilen und der Migration bestehender Workloads finden Sie in unserer Kubernetes-Security-Hardening-Checkliste.

Default-Deny-NetworkPolicy pro Namespace (Check 5.3.2). Ohne NetworkPolicies darf jeder Pod mit jedem reden -- ein kompromittierter Pod erreicht damit auch etcd-Ports, Kubelet-APIs und interne Dienste:

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

Danach geben Sie benötigte Verbindungen explizit frei (DNS zuerst, sonst löst nichts mehr auf). Für die RBAC-Seite von Sektion 5 -- Wildcard-Rollen, cluster-admin-Bindings, Service-Account-Token-Automount -- lohnt ein separater Durchgang mit rbac-lookup und kubeaudit; eine schnelle Standortbestimmung über alle Security-Ebenen liefert unser kostenloser Kubernetes Security Quick-Check.

Härtungs-Checkliste zum Abhaken

Control Plane (self-managed):

  • --anonymous-auth=false am API-Server
  • --authorization-mode=Node,RBAC (kein AlwaysAllow, kein ABAC)
  • --kubelet-certificate-authority gesetzt
  • Audit-Logging aktiv, Log-Pfad als hostPath gemountet, Rotation konfiguriert
  • --profiling=false an API-Server, Controller-Manager, Scheduler
  • EncryptionConfiguration für Secrets aktiv, Bestandssecrets umgeschrieben
  • etcd: --client-cert-auth=true, --auto-tls=false, Peer-TLS erzwungen

Worker Nodes (immer Ihre Verantwortung):

  • Kubelet: anonymous auth aus, authorization.mode: Webhook
  • readOnlyPort: 0
  • protectKernelDefaults: true nach Sysctl-Vorbereitung
  • Zertifikatsrotation aktiv (rotateCertificates, serverTLSBootstrap)
  • Dateirechte: Kubelet-config.yaml und kubeconfig maximal 600, Owner root:root

Policies:

  • Pod Security Standards: mindestens enforce=baseline auf allen Workload-Namespaces
  • Default-Deny-NetworkPolicy in jedem Namespace, Freigaben explizit
  • automountServiceAccountToken: false wo kein API-Zugriff nötig
  • Keine Wildcard-Verbs/-Resources in Rollen, cluster-admin-Bindings inventarisiert

Prozess:

  • kube-bench als wiederkehrender Job oder CI-Schritt, JSON-Ergebnisse archiviert
  • Manifeste vor dem Deploy gegen Best Practices geprüft (z. B. mit unserem YAML-Validator, Trivy oder Polaris)
  • Für akzeptierte WARNs/FAILs: dokumentierte Risikobewertung ("comply or explain")

Fallstricke

Managed Kubernetes: Die Control Plane gehört Ihnen nicht. Bei EKS, AKS und GKE laufen API-Server und etcd beim Provider -- die Sektionen 1 bis 3 des generischen Benchmarks können Sie weder prüfen noch ändern. Ein generischer kube-bench-Lauf liefert dort irreführende Ergebnisse. Nutzen Sie die plattformspezifischen Benchmarks (kube-bench run --benchmark eks-1.5.0, analog für AKS und GKE; die verfügbaren Versionen listet das kube-bench-Repository). Ihre Verantwortung bleibt: Node-Konfiguration (Sektion 4) und komplett Sektion 5.

protectKernelDefaults kann den Kubelet-Start verhindern. Das Flag lässt den Kubelet abbrechen, wenn Kernel-Parameter nicht seinen Erwartungen entsprechen. Setzen Sie vorher auf jedem Node die Sysctls (vm.overcommit_memory=1, kernel.panic=10, kernel.panic_on_oops=1) und testen Sie auf einem Node, bevor Sie die Änderung ausrollen.

Check-Nummern wandern. CIS 1.2.16 in Benchmark v1.9 ist nicht zwingend 1.2.16 in der nächsten Version. Verlassen Sie sich in Skripten und Ausnahme-Dokumentation auf die Beschreibung plus Benchmark-Version, nie auf die Nummer allein.

Nicht jeder FAIL ist ein Fehler. AlwaysPullImages (Level 2) erhöht Registry-Last und Startzeiten erheblich; EventRateLimit ist ein Alpha-Admission-Plugin. Bewusstes Abweichen mit dokumentierter Begründung ist legitim -- stilles Ignorieren nicht.

kube-bench prüft Konfiguration, nicht Laufzeit. Ein perfekter CIS-Score sagt nichts über verwundbare Images, exponierte Ingresses oder kompromittierte Workloads. Für Runtime-Erkennung brauchen Sie Falco, für Image-Scanning Trivy. Wie die Bausteine zusammenspielen, zeigt unser Überblick zum Cluster-Absichern mit Security-Check.

FAQ

Wie oft sollte ich kube-bench ausführen?

Mindestens wöchentlich automatisiert plus nach jeder Änderung an Control-Plane- oder Kubelet-Konfiguration und nach jedem Kubernetes-Upgrade (Upgrades setzen gern Manifest-Änderungen zurück). In regulierten Umgebungen (NIS2, KRITIS, ISO 27001) ist ein täglicher Lauf mit Alerting auf neue FAILs sinnvoll.

Wie integriere ich kube-bench in die CI/CD-Pipeline?

Als Job gegen den Zielcluster mit JSON-Output, dann per jq die FAIL-Anzahl gegen eine Baseline vergleichen: Pipeline rot, wenn neue FAILs gegenüber dem letzten akzeptierten Stand dazukommen. Absolute Null-FAIL-Gates scheitern in der Praxis an bewusst akzeptierten Abweichungen -- der Baseline-Vergleich ist robuster.

Was ist bei EKS, AKS und GKE anders?

Die Control-Plane-Checks (Sektionen 1-3) liegen beim Provider; Sie prüfen mit den plattformspezifischen Benchmarks nur Nodes und Policies. Zusätzlich verschieben sich Kubelet-Defaults: Managed Node Groups liefern teils bereits readOnlyPort: 0 und Webhook-Authorization. Prüfen statt annehmen -- gerade selbstverwaltete Node-Pools und Custom-Images weichen oft ab.

Muss ich alle Checks bestehen?

Nein. Ziel ist: alle Level-1-Automated-Checks auf PASS, alle Manual-Checks bewertet und dokumentiert, Level-2-Checks selektiv nach Risikoabwägung. Auditoren akzeptieren begründete Abweichungen ("comply or explain") -- was sie nicht akzeptieren, sind unbearbeitete Reports.

Was ist der Unterschied zwischen kube-bench und Trivy oder kubescape?

kube-bench prüft ausschließlich den CIS-Benchmark, dafür am gründlichsten. Trivy scannt zusätzlich Images, Dependencies und Manifeste; kubescape deckt neben CIS auch NSA/CISA-Guidelines und MITRE ATT&CK ab. Für den CIS-Nachweis gegenüber Auditoren ist kube-bench das Referenz-Tool, die anderen ergänzen es.

Fazit

CIS-Hardening ist kein Projekt für ein Quartal, sondern zwei fokussierte Arbeitstage plus Automatisierung: kube-bench laufen lassen, die Top-15-FAILs mit den gezeigten Flags beheben, Pod Security Standards und Default-Deny-NetworkPolicies setzen, Scan in die Pipeline. Damit sind Sie besser aufgestellt als die Mehrheit der Produktions-Cluster -- und haben für Kunden-Fragebögen und Audits belastbare Nachweise statt Absichtserklärungen.

Die Cluster-Konfiguration ist die eine Hälfte, Ihre Workload-Manifeste die andere: Privilegierte Container, fehlende Resource-Limits und automatisch gemountete Service-Account-Tokens macht kein API-Server-Flag ungeschehen.

Prüfen Sie Ihre Deployments jetzt im kostenlosen Kubernetes-YAML-Validator -- er lintet Ihre Manifeste direkt im Browser gegen Security-Best-Practices sowie DSGVO-/BSI-relevante Anforderungen, ohne dass Daten Ihren Rechner verlassen.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen