| Tested on | Rocky Linux 10.2 (Red Quartz) — kubeadm control plane and worker nodes |
|---|---|
| Package | kube-bench 0.15.6kubectl 1.36.3kubeadm 1.36.3 |
| Applies to | kubeadm clusters on Linux x86_64; other architectures require the matching kube-bench release archive |
| Cert prep | CKS |
| Lab environment | Multi-node kubeadm cluster with containerd and SSH node access — install Kubernetes with kubeadm |
| Privilege | root on cluster nodes (kube-bench reads host configuration and remediation changes file permissions) |
| Scope | Install kube-bench, select a CIS profile, run master, etcd, controlplane, node, and policies targets, interpret PASS, WARN, and FAIL, map findings to Kubernetes paths, remediate one file-permission issue, and rerun checks. Does not cover full CIS transcription, automatic remediation of every failure, general Linux hardening, runtime threat detection, workload manifest scanning, or compliance certification claims. |
The CIS Kubernetes Benchmark contains many configuration recommendations. kube-bench automates the checks that can be verified from the node filesystem and, for some sections, from the API. This walkthrough installs kube-bench on a kubeadm lab cluster, records the environment, runs control-plane and worker scans against cis-1.12 as the nearest profile bundled with kube-bench 0.15.6, tightens one kubelet file permission, and reruns only the affected check.
What kube-bench does
kube-bench is an open-source scanner from Aqua Security. It executes tests defined in YAML benchmark bundles shipped under /etc/kube-bench/cfg/ and prints a line per control with PASS, FAIL, WARN, or INFO.
Keep these limits in mind before you treat a green summary as "done":
- Results depend on Kubernetes version, distribution layout, node role, and which benchmark directory you select.
- Controls labelled Manual may produce PASS or FAIL when kube-bench includes an audit test, or WARN when the control requires human review.
- Policy checks query the API with
kubectland can be slow on large clusters. - Passing node checks does not prove your application Pods, images, or network paths are safe.
Record the cluster environment
Write down the cluster shape before you interpret failures. Future you needs to know whether a control applied to kubeadm, a managed control plane, or a single-node lab.
On a workstation or control-plane node with kubectl configured, capture versions and node layout:
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl versionClient Version: v1.36.3
Kustomize Version: v5.8.1
Server Version: v1.36.3Record the kubeadm and kube-bench versions on the node where you install the scanner:
kubeadm version -o short
kube-bench versionv1.36.3
0.15.6List nodes and roles so you know where to run master, etcd, controlplane, and node targets:
kubectl get nodes -o wideNAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-cp Ready control-plane 4h43m v1.36.3 192.168.56.108 <none> Rocky Linux 10.2 (Red Quartz) 6.12.0-211.16.1.el10_2.0.1.x86_64 (amd64) containerd://2.2.5
worker01 Ready <none> 4h41m v1.36.3 192.168.56.109 <none> Rocky Linux 10.2 (Red Quartz) 6.12.0-211.34.1.el10_2.x86_64 (amd64) containerd://2.2.5This lab is a multi-node kubeadm cluster with one control-plane node and one worker. The displayed kubectl get nodes output does not show whether the control-plane node is schedulable.
On managed Kubernetes, run the node target on worker nodes you can access. Run the policies target once from any host with working kubectl credentials; master and etcd targets normally cannot inspect a provider-managed control plane.
Install kube-bench
This guide uses the release tarball for kube-bench 0.15.6 on each node you scan. Install the same version on the control plane and every worker.
On each node, download and install the binary plus benchmark data:
curl -sL https://github.com/aquasecurity/kube-bench/releases/download/v0.15.6/kube-bench_0.15.6_linux_amd64.tar.gz -o /tmp/kube-bench.tar.gz
tar -xzf /tmp/kube-bench.tar.gz -C /tmp
install -m 755 /tmp/kube-bench /usr/local/bin/kube-bench
mkdir -p /etc/kube-bench
cp -a /tmp/cfg /etc/kube-bench/Confirm the binary runs:
kube-bench version0.15.6Alternatives you may see in other environments:
- Container —
docker run --rm -v /etc/kubernetes:/etc/kubernetes ... aquasec/kube-bench:latest runmounts host paths into the container. - Kubernetes Job — useful for recurring scans; the Job still needs hostPath mounts to read static Pod manifests and kubelet files.
Pin the version in production. Floating latest tags make before-and-after comparisons meaningless.
Select the benchmark profile
Benchmark definitions live under /etc/kube-bench/cfg/. List CIS bundles:
ls /etc/kube-bench/cfg/ | grep '^cis-' | sort -Vcis-1.5
cis-1.6
...
cis-1.11
cis-1.12
cis-1.20
...kube-bench 0.15.6 has no benchmark explicitly mapped to Kubernetes 1.36. When you pass --version 1.36, its fallback logic selects the Kubernetes 1.34 mapping and therefore runs cis-1.12, whose documented support range is Kubernetes 1.32–1.34. This lab uses cis-1.12 as the nearest bundled baseline, not as an officially matched Kubernetes 1.36 or current CIS 2.0.1 assessment.
Two safe ways to select it — use one flag, not both (--version and --benchmark together returns an error):
export KUBECONFIG=/etc/kubernetes/admin.conf
kube-bench run --targets=master --version 1.36 2>&1 | head -3Or pin the benchmark explicitly:
kube-bench run --targets=master --benchmark cis-1.12 2>&1 | head -3| Method | Example | When to use |
|---|---|---|
| Auto-map from Kubernetes version | kube-bench run --targets=master --version 1.36 |
kubeadm or upstream layouts with working kubectl |
| Explicit benchmark | kube-bench run --targets=master --benchmark cis-1.12 |
You already know the CIS revision |
Set KUBECONFIG before auto-detection. Without API access, kube-bench may assume an old default (for example 1.18) and choose the wrong profile.
Do not select an older benchmark only because it prints fewer FAIL lines — the control IDs and thresholds change between versions.
Run control-plane checks
SSH to the control-plane node. Export kubeconfig when policy targets need the API later:
Cluster node access in this lab assumes SSH from your workstation.
export KUBECONFIG=/etc/kubernetes/admin.conf
kube-bench run --targets=master --benchmark cis-1.12The master target covers API server, scheduler, and controller manager settings plus control-plane file permissions. Sample lines from this lab:
[INFO] 1 Control Plane Security Configuration
[INFO] 1.1 Control Plane Node Configuration Files
[PASS] 1.1.1 Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)
[PASS] 1.1.2 Ensure that the API server pod specification file ownership is set to root:root (Automated)
...
[FAIL] 1.1.7 Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Automated)
[WARN] 1.1.9 Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)
[INFO] 1.2 API Server
[FAIL] 1.2.15 Ensure that the --profiling argument is set to false (Automated)
...
== Summary master ==
37 checks PASS
12 checks FAIL
11 checks WARN
0 checks INFOTypical finding areas in section 1:
- Static Pod manifests under
/etc/kubernetes/manifests/ - PKI and kubeconfig permissions under
/etc/kubernetes/pki/and/etc/kubernetes/*.conf - API server authorization and admission flags in
kube-apiserver.yaml
For certificate layout and renewal, see Kubernetes PKI and certificates. For API hardening beyond file permissions, plan separate work on audit logging and admission — those show up as FAIL or WARN rows in section 1.2 on a default kubeadm cluster.
Run etcd checks
Still on the control-plane node, run the etcd target:
kube-bench run --targets=etcd --benchmark cis-1.12[INFO] 2 Etcd Node Configuration
[PASS] 2.1 Ensure that the --cert-file and --key-file arguments are set as appropriate (Automated)
[PASS] 2.2 Ensure that the --client-cert-auth argument is set to true (Automated)
...
[PASS] 2.7 Ensure that a unique Certificate Authority is used for etcd (Manual)
== Summary etcd ==
7 checks PASS
0 checks FAIL
0 checks WARN
0 checks INFOetcd runs as a static Pod in kubeadm (/etc/kubernetes/manifests/etcd.yaml). Data lives under /var/lib/etcd/. Backup, restore, and encryption at rest are out of scope here — this scan only reports CIS-style configuration and file checks.
Run control-plane configuration checks
The master target runs section 1 checks, but it does not include the separate section 3 control-plane checks. Run the controlplane target on the control-plane node:
export KUBECONFIG=/etc/kubernetes/admin.conf
kube-bench run --targets=controlplane --benchmark cis-1.12This target reviews authentication and authorization practices plus audit-policy configuration. Several controls require manual review, so WARN results are expected.
[INFO] 3 Control Plane Configuration
[INFO] 3.1 Authentication and Authorization
[WARN] 3.1.1 Client certificate authentication should not be used for users (Manual)
[WARN] 3.1.2 Service account token authentication should not be used for users (Manual)
[WARN] 3.1.3 Bootstrap token authentication should not be used for users (Manual)
[INFO] 3.2 Logging
[WARN] 3.2.1 Ensure that a minimal audit policy is created (Manual)
[WARN] 3.2.2 Ensure that the audit policy covers key security concerns (Manual)
== Summary controlplane ==
0 checks PASS
0 checks FAIL
5 checks WARN
0 checks INFORun worker-node checks
SSH to each worker (or run the same install steps there) and execute the node target:
kube-bench run --targets=node --benchmark cis-1.12Worker results focus on kubelet configuration, service unit permissions, certificate paths, and kube-proxy where present:
[INFO] 4 Worker Node Security Configuration
[FAIL] 4.1.1 Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)
[PASS] 4.1.5 Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive (Automated)
[FAIL] 4.1.9 If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)
[WARN] 4.2.9 Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)
...
== Summary node ==
19 checks PASS
2 checks FAIL
5 checks WARN
0 checks INFONode compromise expands blast radius to every Pod on that host — align kube-bench worker results with kubelet hardening lessons later in the CKS tutorial.
Run policy or control checks
The policies target evaluates RBAC, ServiceAccounts, namespaces, and related API-visible settings. It requires a working kubectl configuration and issues many API calls, so a full run can take noticeably longer than master or node on the same host.
The full cis-1.12 policies bundle uses jq in multiple RBAC, ServiceAccount, and Pod checks. Install it on the host from which you run the policies target—not on every worker unless you execute policies there:
# RHEL, Rocky Linux, AlmaLinux, Fedora
dnf install -y jq
# Ubuntu and Debian
apt-get update && apt-get install -y jqRun one policy check when you want a quick sample:
export KUBECONFIG=/etc/kubernetes/admin.conf
kube-bench run --targets=policies --benchmark cis-1.12 -c 5.1.2[INFO] 5 Kubernetes Policies
[INFO] 5.1 RBAC and Service Accounts
[PASS] 5.1.2 Minimize access to secrets (Manual)
== Summary policies ==
1 checks PASS
0 checks FAIL
0 checks WARN
0 checks INFORun the full policies target outside peak periods on large clusters because it performs many read-only API queries:
kube-bench run --targets=policies --benchmark cis-1.12Policy rows are often manual or API-heavy — expect more WARN than on filesystem checks. Implement RBAC, NetworkPolicy, and Pod Security admission in their dedicated guides; kube-bench tells you whether the cluster still fails a CIS policy probe, not how to write every manifest.
Understand result categories
| Result | Meaning | Typical next step |
|---|---|---|
PASS |
The configured audit or test met its expected condition | Record the result and confirm whether the control is scored or manual |
FAIL |
The audit or test ran but did not meet its expected condition | Review applicability and remediation; check whether it is scored |
WARN |
Manual review is required, or the audit could not complete reliably | Read the control details and inspect any command error |
INFO |
The check was skipped or reported for information | No direct pass/fail effect |
A WARN on a manual control is not automatically a production blocker. A FAIL on a scored control should have a fix or a formal exception before you call the audit complete.
Run selected checks
Use flags to narrow scope during remediation or CI.
One section (group 1.1 — control-plane configuration files):
kube-bench run --targets=master --benchmark cis-1.12 -g 1.1One check ID with failing process output:
kube-bench run --targets=master --benchmark cis-1.12 -c 1.2.15 --include-test-output[FAIL] 1.2.15 Ensure that the --profiling argument is set to false (Automated)
root ... kube-apiserver --advertise-address=192.168.56.108 ... --secure-port=6443 ...Skip a justified deviation while you document an exception:
kube-bench run --targets=master --benchmark cis-1.12 --skip 1.2.15 -c 1.2.15Skipped checks appear as INFO instead of FAIL for that run.
JSON for pipelines (written to a file):
kube-bench run --targets=master --benchmark cis-1.12 --json --outputfile /tmp/kube-bench-master.jsonCI exit code when any selected check fails:
kube-bench run --targets=master --benchmark cis-1.12 -c 1.2.15 --exit-code 1
echo $?1Map findings to files
When kube-bench prints a remediation, map it to the file kubeadm actually uses:
| Finding area | Common source on kubeadm |
|---|---|
| API server flag | /etc/kubernetes/manifests/kube-apiserver.yaml |
| etcd flag | /etc/kubernetes/manifests/etcd.yaml |
| Scheduler flag | /etc/kubernetes/manifests/kube-scheduler.yaml |
| Controller-manager flag | /etc/kubernetes/manifests/kube-controller-manager.yaml |
| Kubelet setting | /var/lib/kubelet/config.yaml and /etc/kubernetes/kubelet.conf |
| Kubelet service environment | /usr/lib/systemd/system/kubelet.service and /etc/sysconfig/kubelet (RPM) |
| Admin credentials | /etc/kubernetes/admin.conf |
| PKI material | /etc/kubernetes/pki/ |
Static Pod changes reload automatically when the kubelet watches /etc/kubernetes/manifests/. Copy files to /root/ or another directory outside the watch path before you edit.
Remediate and rerun
Walk through one safe fix on the worker: check 4.1.9 failed because /var/lib/kubelet/config.yaml was mode 644.
1. Confirm the recommendation applies. kubeadm uses config.yaml; tightening permissions does not change kubelet semantics.
2. Back up outside watched directories:
cp -a /var/lib/kubelet/config.yaml /root/kubelet-config.yaml.bak3. Change one setting:
chmod 600 /var/lib/kubelet/config.yaml
ls -la /var/lib/kubelet/config.yaml-rw-------. 1 root root 1118 Jul 27 22:31 /var/lib/kubelet/config.yaml4. Verify the component is still healthy. kubelet does not need a restart for permission-only changes:
systemctl is-active kubeletactive5. Confirm cluster health from the control plane:
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl get nodesNAME STATUS ROLES AGE VERSION
k8s-cp Ready control-plane 4h43m v1.36.3
worker01 Ready <none> 4h41m v1.36.36. Rerun the specific check on the worker:
kube-bench run --targets=node --benchmark cis-1.12 -c 4.1.9[PASS] 4.1.9 If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive (Automated)
== Summary node ==
1 checks PASS
0 checks FAIL7. Rerun the whole file-permission group:
kube-bench run --targets=node --benchmark cis-1.12 -g 4.1The group summary should show one fewer FAIL than the first baseline (remaining failures such as 4.1.1 may still need separate work).
8. Record the check ID, file path, and new result in your change log or ticket.
Larger remediations — enabling audit logs on the API server, etcd ownership changes, or admission plugins — need change windows and rollback plans. Fix one control at a time and rerun targeted checks instead of editing many manifests at once.
Handle exceptions
Not every FAIL should be "fixed immediately." Document accepted risk when a control does not fit the environment.
| Field | Example |
|---|---|
| Check ID | 1.2.15 |
| Reason for deviation | Lab cluster; profiling required for short-term API latency troubleshooting |
| Risk | Unauthenticated profiling endpoint exposure if anonymous auth were misconfigured |
| Compensating control | API server reachable only on lab network; RBAC for admin access |
| Owner | Platform team |
| Review date | 2026-10-01 |
| Evidence | kube-bench output with --skip 1.2.15; change ticket link |
Revisit exceptions on upgrade — a new Kubernetes minor release may map to a different CIS profile with changed control text.
What's Next
- Kubernetes Ingress Rules with Host, Path and TLS Examples
- Protect Kubernetes Node Metadata and Kubelet Endpoints
- Verify Kubernetes Release Binaries and Container Images
References
- kube-bench repository — installation, flags, and benchmark bundles
- CIS Kubernetes Benchmark — authoritative control definitions
- Kubernetes security overview — upstream security documentation
- RBAC good practices — policy context for section 5 checks
Summary
kube-bench turns the CIS Kubernetes Benchmark into runnable PASS, FAIL, and WARN lines on your nodes. Install a pinned release on each host you measure, select cis-1.12 as the nearest profile bundled with kube-bench 0.15.6 for this lab cluster. Run master, etcd, and controlplane on the control-plane node, node on every worker, and policies once from a host with working kubectl credentials.
Read summaries as a prioritized backlog, not a grade. Filesystem checks map directly to /etc/kubernetes/manifests/, /var/lib/kubelet/, and PKI paths; policy checks point you toward RBAC and namespace design without replacing those tutorials. When you remediate, back up one file, change one control, confirm kubelet and kubectl get nodes stay healthy, then rerun -c and -g before you kick off a full scan again.
For CKS study, pair this audit with Kubernetes security architecture so you know which failures matter at the API boundary versus the node and workload layers. Schedule kube-bench after upgrades, before exams, and whenever you harden kubelet or control-plane manifests — and keep a short exception register for WARN and accepted FAIL rows so the next run stays comparable.

