Lesson 14 of 15 · Detect & Harden
CIS benchmark hardening
Measure your cluster against the CIS Kubernetes Benchmark with kube-bench, fix the findings that matter on the control plane, kubelets and nodes, and document the exceptions honestly.
Benchmarks turn "is it secure?" into a checklist
The CIS Kubernetes Benchmark lists hundreds of concrete recommendations for the control plane, etcd, kubelets, node files and policies. kube-bench checks a node against them automatically. It won't make a cluster secure on its own, but it catches dangerous defaults and drift reliably.
A building inspector doesn't guess whether a house is safe. They go through a printed checklist: smoke alarms, fire doors, wiring. kube-bench is that inspector for Kubernetes, and the CIS Benchmark is the checklist. You fix what's red, and write down why anything left red is acceptable.
Run kube-bench
$ kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
$ kubectl logs job/kube-bench | head -30
[INFO] 4 Worker Node Security Configuration
[INFO] 4.1 Worker Node Configuration Files
[PASS] 4.1.1 Ensure that the kubelet service file permissions are set to 600 or more restrictive
...
[INFO] 4.2 Kubelet
[PASS] 4.2.1 Ensure that the --anonymous-auth argument is set to false
[FAIL] 4.2.6 Ensure that the --protect-kernel-defaults argument is set to true
...
== Summary node ==
18 checks PASS
3 checks FAIL
2 checks WARN
(Check numbers and wording depend on the benchmark version.) Run it on control-plane nodes too (the repository has job manifests with the right node selectors and host mounts), and on a schedule, since configurations drift.
What typically needs fixing
API server (static pod flags on kubeadm):
| Check | Setting |
|---|---|
| Anonymous access restricted | --anonymous-auth=false, or allow it only for health endpoints where supported |
| Authorization | --authorization-mode=Node,RBAC |
| Audit logging on | --audit-policy-file, --audit-log-* (lesson 13) |
| Encryption at rest | --encryption-provider-config (lesson 11) |
| Profiling off | --profiling=false (also for scheduler and controller manager) |
| Admission plugins | NodeRestriction enabled (limits what kubelets may modify) |
Kubelet (/var/lib/kubelet/config.yaml):
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
anonymous:
enabled: false
webhook:
enabled: true
authorization:
mode: Webhook
readOnlyPort: 0
protectKernelDefaults: true # set the required sysctls on the node first, or the kubelet won't start
rotateCertificates: true
serverTLSBootstrap: true # kubelet serving certs signed by the cluster (approve CSRs, lesson 01)
eventRecordQPS: 5
Nodes: file permissions on kubelet and CNI configs, SSH hardening, a minimal OS, SELinux/AppArmor enforcing (see Linux, Level 3). An immutable OS removes many node findings entirely.
Test hardening changes on one node first
Settings such as protectKernelDefaults: true stop the kubelet from starting if the node's sysctls don't match what it expects. Roll changes out like an upgrade: one node, verify, then the rest.
Managed Kubernetes
On EKS, GKE or AKS you can't see or change the control plane. CIS publishes provider-specific benchmarks covering what you do control: worker nodes, RBAC, policies, networking and secrets. kube-bench supports them (e.g. --benchmark eks-1.x). Some distributions ship a CIS mode (for example, RKE2 has a CIS profile setting), which applies many recommendations for you.
Exceptions are fine, if they're written down
Not every recommendation fits every environment. Record each exception with the check ID, reason, compensating control, owner and review date, and keep it next to the configuration in Git. "We accept 1.2.x because… mitigated by…, reviewed 2026-Q4" is a finding closed; silence is a finding hidden.
Try it: benchmark, fix, re-run (kubeadm lab)
- Run kube-bench on a worker and on the control-plane node; save both reports.
- Pick three FAILs on the worker. Fix them in
/var/lib/kubelet/config.yaml, restart the kubelet (systemctl restart kubelet), and confirm the node stays Ready. - Fix one control-plane finding (e.g.
--profiling=falseon the scheduler) by editing its static pod manifest. - Re-run kube-bench and compare the summaries.
- Write two exception entries for findings you decide not to fix, with reasons and compensating controls.
Going deeper: continuous compliance
- Run kube-bench as a CronJob and export results (e.g. via a compliance operator or your SIEM), so drift is detected automatically.
- Combine with policy engines (lesson 10) for workload-level controls, and image scanning (lesson 12). The benchmark covers the platform, not your apps.
- Bake node hardening into golden images and cluster templates (kubeadm config, Cluster API, RKE2 config), so new clusters start compliant.
- Map benchmark controls to your compliance frameworks (ISO 27001, SOC 2, PCI DSS) once, and reuse the evidence.
Recap
- CIS Kubernetes Benchmark = the checklist; kube-bench = the automated inspector.
- Typical fixes: API server audit, encryption, profiling off,
NodeRestriction; kubelet anonymous off, Webhook authz, readOnlyPort 0, certificate rotation. - Managed clusters: use the provider-specific benchmark.
- Roll hardening out node by node; document exceptions with reasons and owners.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.