Audit Kubernetes Against the CIS Benchmark with kube-bench

Audit a kubeadm cluster with kube-bench, interpret CIS PASS, WARN, and FAIL results, map findings to files, remediate one check, and verify the fix.

Published

Updated

Read time 13 min read

Reviewed byDeepak Prasad

kube-bench CIS Kubernetes benchmark audit on a kubeadm control plane and worker node
Tested on Rocky Linux 10.2 (Red Quartz) — kubeadm control plane and worker nodes
Package kube-bench 0.15.6
kubectl 1.36.3
kubeadm 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.

IMPORTANT
kube-bench audits node and control-plane configuration against a CIS profile. It does not replace workload scanning, image signing, Falco, or a full compliance program. For where those controls fit, see Kubernetes security architecture.

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 kubectl and 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:

bash
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl version
output
Client Version: v1.36.3
Kustomize Version: v5.8.1
Server Version: v1.36.3

Record the kubeadm and kube-bench versions on the node where you install the scanner:

bash
kubeadm version -o short
kube-bench version
output
v1.36.3
0.15.6

List nodes and roles so you know where to run master, etcd, controlplane, and node targets:

bash
kubectl get nodes -o wide
output
NAME       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.5

This 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:

bash
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:

bash
kube-bench version
output
0.15.6

Alternatives you may see in other environments:

  • Containerdocker run --rm -v /etc/kubernetes:/etc/kubernetes ... aquasec/kube-bench:latest run mounts 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:

bash
ls /etc/kube-bench/cfg/ | grep '^cis-' | sort -V
output
cis-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):

bash
export KUBECONFIG=/etc/kubernetes/admin.conf
kube-bench run --targets=master --version 1.36 2>&1 | head -3

Or pin the benchmark explicitly:

bash
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.

bash
export KUBECONFIG=/etc/kubernetes/admin.conf
kube-bench run --targets=master --benchmark cis-1.12

The master target covers API server, scheduler, and controller manager settings plus control-plane file permissions. Sample lines from this lab:

output
[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 INFO

Typical 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:

bash
kube-bench run --targets=etcd --benchmark cis-1.12
output
[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 INFO

etcd 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:

bash
export KUBECONFIG=/etc/kubernetes/admin.conf
kube-bench run --targets=controlplane --benchmark cis-1.12

This target reviews authentication and authorization practices plus audit-policy configuration. Several controls require manual review, so WARN results are expected.

output
[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 INFO

Run worker-node checks

SSH to each worker (or run the same install steps there) and execute the node target:

bash
kube-bench run --targets=node --benchmark cis-1.12

Worker results focus on kubelet configuration, service unit permissions, certificate paths, and kube-proxy where present:

output
[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 INFO

Node 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:

bash
# RHEL, Rocky Linux, AlmaLinux, Fedora
dnf install -y jq

# Ubuntu and Debian
apt-get update && apt-get install -y jq

Run one policy check when you want a quick sample:

bash
export KUBECONFIG=/etc/kubernetes/admin.conf
kube-bench run --targets=policies --benchmark cis-1.12 -c 5.1.2
output
[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 INFO

Run the full policies target outside peak periods on large clusters because it performs many read-only API queries:

bash
kube-bench run --targets=policies --benchmark cis-1.12

Policy 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):

bash
kube-bench run --targets=master --benchmark cis-1.12 -g 1.1

One check ID with failing process output:

bash
kube-bench run --targets=master --benchmark cis-1.12 -c 1.2.15 --include-test-output
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:

bash
kube-bench run --targets=master --benchmark cis-1.12 --skip 1.2.15 -c 1.2.15

Skipped checks appear as INFO instead of FAIL for that run.

JSON for pipelines (written to a file):

bash
kube-bench run --targets=master --benchmark cis-1.12 --json --outputfile /tmp/kube-bench-master.json

CI exit code when any selected check fails:

bash
kube-bench run --targets=master --benchmark cis-1.12 -c 1.2.15 --exit-code 1
echo $?
output
1

Map 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:

bash
cp -a /var/lib/kubelet/config.yaml /root/kubelet-config.yaml.bak

3. Change one setting:

bash
chmod 600 /var/lib/kubelet/config.yaml
ls -la /var/lib/kubelet/config.yaml
output
-rw-------. 1 root root 1118 Jul 27 22:31 /var/lib/kubelet/config.yaml

4. Verify the component is still healthy. kubelet does not need a restart for permission-only changes:

bash
systemctl is-active kubelet
output
active

5. Confirm cluster health from the control plane:

bash
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl get nodes
output
NAME       STATUS   ROLES           AGE     VERSION
k8s-cp     Ready    control-plane   4h43m   v1.36.3
worker01   Ready    <none>          4h41m   v1.36.3

6. Rerun the specific check on the worker:

bash
kube-bench run --targets=node --benchmark cis-1.12 -c 4.1.9
output
[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 FAIL

7. Rerun the whole file-permission group:

bash
kube-bench run --targets=node --benchmark cis-1.12 -g 4.1

The 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


References


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.


Frequently Asked Questions

1. What does kube-bench check in Kubernetes?

kube-bench runs automated and manual tests from a CIS Kubernetes Benchmark profile against control plane components, etcd, worker nodes, and cluster policies. It inspects static Pod manifests, kubelet configuration, PKI file permissions, API server flags, and RBAC-related policy items where the profile defines them. It does not scan application container images or workload YAML unless a policy check queries the API for cluster configuration.

2. What is the difference between PASS, WARN, and FAIL in kube-bench?

PASS means the configured audit or test met its expected condition. FAIL means the audit or test ran but did not meet its expected condition—review applicability and whether the control is scored. WARN means manual review is required, or the audit could not complete reliably. INFO appears for skipped or informational lines with no direct pass/fail effect.

3. Which CIS benchmark profile should I use for Kubernetes 1.36?

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. Do not pick an older profile only because it reports fewer failures—the controls differ between benchmark versions.

4. Does a clean kube-bench report mean my cluster is fully secure?

No. kube-bench validates selected CIS configuration controls on nodes and some API-visible policy settings. It does not prove workload security, supply-chain integrity, network exposure for every Service, or detective controls such as runtime threat detection. Treat the report as a structured baseline audit, then layer workload hardening, image scanning, and monitoring on top.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)