CKS — kube-bench Setup & Installation

11 minute read

Exam Context

Property Value
Domain Domain 1 — Cluster Setup
Weight 10%
Objective Use CIS benchmark to review the security configuration of Kubernetes components

kube-bench is the tool you’ll use on the CKS exam to run CIS benchmark checks. You need to know how to install it, run it, and interpret its output — quickly. This topic covers all three installation methods and the output format in detail.

What Is kube-bench?

kube-bench is an open-source Go application by Aqua Security that checks whether Kubernetes is deployed securely by running the checks documented in the CIS Kubernetes Benchmark.

Property Details
Maintainer Aqua Security
Language Go
License Apache 2.0
Repository github.com/aquasecurity/kube-bench
Runs on Linux (master and worker nodes)
Output formats Text (default), JSON, JUnit
Auto-detection Detects Kubernetes version and selects the correct CIS benchmark

How kube-bench Works

kube-bench operates by:

  1. Detecting the Kubernetes version running on the node
  2. Selecting the appropriate CIS benchmark configuration
  3. Checking running processes, configuration files, and file permissions
  4. Comparing actual values against expected values from the benchmark
  5. Producing a report categorized by PASS, FAIL, WARN, and INFO

kube-bench must run on the node itself (not remotely) because it inspects local processes and files. This is why running it as a Kubernetes Job is the most practical approach.

Installation Method 1: Binary Download

The simplest method — download the pre-compiled binary directly to the node.

Download and Install

# Set the version (check GitHub releases for latest)
KUBE_BENCH_VERSION=0.8.0

# Download the binary
curl -L https://github.com/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH_VERSION}/kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.tar.gz -o kube-bench.tar.gz

# Extract
tar -xvf kube-bench.tar.gz

# Move to PATH
sudo mv kube-bench /usr/local/bin/

# Verify installation
kube-bench version

Expected output:

v0.8.0

Run a Basic Scan

# Run all checks (must be run as root or with sudo)
sudo kube-bench run

# Run only master checks (on a control plane node)
sudo kube-bench run --targets master

# Run only node checks (on a worker node)
sudo kube-bench run --targets node

Configuration Files

When installed as a binary, kube-bench looks for configuration files in these locations:

# Default configuration directory
/etc/kube-bench/cfg/

# Alternative location
~/.kube-bench/cfg/

# Or specify explicitly
kube-bench run --config-dir /path/to/cfg

The configuration directory contains YAML files that define the checks:

cfg/
├── cis-1.9/
│   ├── config.yaml
│   ├── controlplane.yaml
│   ├── etcd.yaml
│   ├── master.yaml
│   ├── node.yaml
│   └── policies.yaml
└── cis-1.10/
    ├── config.yaml
    ├── controlplane.yaml
    ├── etcd.yaml
    ├── master.yaml
    ├── node.yaml
    └── policies.yaml

Installation Method 2: Container Image

Run kube-bench as a container directly on the node using Docker or containerd.

Using Docker

# Run against the host (mount host filesystem and PID namespace)
docker run --rm \
  --pid=host \
  -v /etc:/etc:ro \
  -v /var:/var:ro \
  -v $(which kubectl):/usr/local/bin/kubectl:ro \
  -v ~/.kube:/root/.kube:ro \
  aquasec/kube-bench:v0.8.0 run

# Run only master checks
docker run --rm \
  --pid=host \
  -v /etc:/etc:ro \
  -v /var:/var:ro \
  aquasec/kube-bench:v0.8.0 run --targets master

# Run only node checks
docker run --rm \
  --pid=host \
  -v /etc:/etc:ro \
  -v /var:/var:ro \
  aquasec/kube-bench:v0.8.0 run --targets node

Why these mounts are required:

Mount Purpose
--pid=host Access host process list to inspect running kube-apiserver, kubelet, etc.
/etc:/etc:ro Access Kubernetes manifests in /etc/kubernetes/manifests/
/var:/var:ro Access kubelet configuration in /var/lib/kubelet/

Using crictl (containerd)

On nodes using containerd (no Docker):

# Pull the image
crictl pull docker.io/aquasec/kube-bench:v0.8.0

# Run (crictl doesn't support --pid=host directly; prefer binary or Job method)

Recommendation: On containerd-only nodes (most modern clusters), prefer the binary or Kubernetes Job method over the container method.

Running kube-bench as a Kubernetes Job is the most exam-friendly and production-friendly approach. It leverages the cluster itself to distribute the scan across nodes.

kube-bench Job for Master Node

Create a file kube-bench-master-job.yaml:

apiVersion: batch/v1
kind: Job
metadata:
  name: kube-bench-master
  namespace: default
spec:
  template:
    metadata:
      labels:
        app: kube-bench
    spec:
      hostPID: true
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""
      tolerations:
        - key: node-role.kubernetes.io/control-plane
          operator: Exists
          effect: NoSchedule
        - key: node-role.kubernetes.io/master
          operator: Exists
          effect: NoSchedule
      containers:
        - name: kube-bench
          image: aquasec/kube-bench:v0.8.0
          command: ["kube-bench", "run", "--targets", "master"]
          volumeMounts:
            - name: var-lib-etcd
              mountPath: /var/lib/etcd
              readOnly: true
            - name: var-lib-kubelet
              mountPath: /var/lib/kubelet
              readOnly: true
            - name: var-lib-kube-scheduler
              mountPath: /var/lib/kube-scheduler
              readOnly: true
            - name: var-lib-kube-controller-manager
              mountPath: /var/lib/kube-controller-manager
              readOnly: true
            - name: etc-systemd
              mountPath: /etc/systemd
              readOnly: true
            - name: lib-systemd
              mountPath: /lib/systemd/
              readOnly: true
            - name: srv-kubernetes
              mountPath: /srv/kubernetes/
              readOnly: true
            - name: etc-kubernetes
              mountPath: /etc/kubernetes
              readOnly: true
            - name: usr-bin
              mountPath: /usr/local/mount-from-host/bin
              readOnly: true
            - name: etc-cni-netd
              mountPath: /etc/cni/net.d/
              readOnly: true
            - name: opt-cni-bin
              mountPath: /opt/cni/bin/
              readOnly: true
      restartPolicy: Never
      volumes:
        - name: var-lib-etcd
          hostPath:
            path: /var/lib/etcd
        - name: var-lib-kubelet
          hostPath:
            path: /var/lib/kubelet
        - name: var-lib-kube-scheduler
          hostPath:
            path: /var/lib/kube-scheduler
        - name: var-lib-kube-controller-manager
          hostPath:
            path: /var/lib/kube-controller-manager
        - name: etc-systemd
          hostPath:
            path: /etc/systemd
        - name: lib-systemd
          hostPath:
            path: /lib/systemd
        - name: srv-kubernetes
          hostPath:
            path: /srv/kubernetes
        - name: etc-kubernetes
          hostPath:
            path: /etc/kubernetes
        - name: usr-bin
          hostPath:
            path: /usr/bin
        - name: etc-cni-netd
          hostPath:
            path: /etc/cni/net.d/
        - name: opt-cni-bin
          hostPath:
            path: /opt/cni/bin/
  backoffLimit: 0

kube-bench Job for Worker Node

Create a file kube-bench-node-job.yaml:

apiVersion: batch/v1
kind: Job
metadata:
  name: kube-bench-node
  namespace: default
spec:
  template:
    metadata:
      labels:
        app: kube-bench
    spec:
      hostPID: true
      nodeSelector:
        node-role.kubernetes.io/worker: ""
      containers:
        - name: kube-bench
          image: aquasec/kube-bench:v0.8.0
          command: ["kube-bench", "run", "--targets", "node"]
          volumeMounts:
            - name: var-lib-kubelet
              mountPath: /var/lib/kubelet
              readOnly: true
            - name: etc-systemd
              mountPath: /etc/systemd
              readOnly: true
            - name: lib-systemd
              mountPath: /lib/systemd/
              readOnly: true
            - name: etc-kubernetes
              mountPath: /etc/kubernetes
              readOnly: true
            - name: usr-bin
              mountPath: /usr/local/mount-from-host/bin
              readOnly: true
      restartPolicy: Never
      volumes:
        - name: var-lib-kubelet
          hostPath:
            path: /var/lib/kubelet
        - name: etc-systemd
          hostPath:
            path: /etc/systemd
        - name: lib-systemd
          hostPath:
            path: /lib/systemd
        - name: etc-kubernetes
          hostPath:
            path: /etc/kubernetes
        - name: usr-bin
          hostPath:
            path: /usr/bin
  backoffLimit: 0

Deploy and View Results

# Apply the master scan Job
kubectl apply -f kube-bench-master-job.yaml

# Wait for completion
kubectl wait --for=condition=complete job/kube-bench-master --timeout=120s

# View results
kubectl logs job/kube-bench-master

# Apply the node scan Job
kubectl apply -f kube-bench-node-job.yaml

# Wait for completion
kubectl wait --for=condition=complete job/kube-bench-node --timeout=120s

# View results
kubectl logs job/kube-bench-node

Cleanup

# Delete Jobs when done
kubectl delete job kube-bench-master kube-bench-node

CKS exam tip: Running kube-bench as a Job is the most common exam approach. Memorize the key elements: hostPID: true, the correct nodeSelector and tolerations for the control plane, and the hostPath volume mounts. The exam may provide partial YAML that you need to complete.

Output Format Interpretation

Understanding kube-bench output is essential for identifying and prioritizing fixes.

Result Categories

Status Meaning Action Required
PASS Check passed — configuration meets the benchmark None
FAIL Check failed — configuration does not meet the benchmark Fix required
WARN Cannot be automatically verified — manual review needed Review and decide
INFO Informational — provides context but no pass/fail determination Note for reference

Sample Output (Text Format)

[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
[PASS] 1.1.2 Ensure that the API server pod specification file ownership is set to root:root
[PASS] 1.1.3 Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive
[PASS] 1.1.4 Ensure that the controller manager pod specification file ownership is set to root:root
[FAIL] 1.1.5 Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive
[PASS] 1.1.6 Ensure that the scheduler pod specification file ownership is set to root:root

[INFO] 1.2 API Server
[FAIL] 1.2.1 Ensure that the --anonymous-auth argument is set to false
[PASS] 1.2.2 Ensure that the --token-auth-file parameter is not set
[PASS] 1.2.6 Ensure that the --authorization-mode argument is not set to AlwaysAllow
[PASS] 1.2.7 Ensure that the --authorization-mode argument includes Node
[PASS] 1.2.8 Ensure that the --authorization-mode argument includes RBAC
[WARN] 1.2.10 Ensure that the admission control plugin EventRateLimit is set
[FAIL] 1.2.16 Ensure that the --profiling argument is set to false
[FAIL] 1.2.19 Ensure that the --audit-log-path argument is set

== Remediations master ==
1.1.5 Run the below command (based on the file location on your system) on the control plane node.
chmod 600 /etc/kubernetes/manifests/kube-scheduler.yaml

1.2.1 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
on the control plane node and set the below parameter.
--anonymous-auth=false

1.2.16 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
on the control plane node and set the below parameter.
--profiling=false

1.2.19 Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
on the control plane node and set the below parameter.
--audit-log-path=/var/log/kubernetes/audit/audit.log

== Summary master ==
43 checks PASS
9 checks FAIL
11 checks WARN
0 checks INFO

Summary Section Breakdown

The summary at the bottom gives you the overall picture:

== Summary master ==
43 checks PASS    ← These are fine
9 checks FAIL     ← These must be fixed
11 checks WARN    ← Review manually
0 checks INFO     ← Informational only

Priority: Fix all FAIL results first, then review WARN results.

JSON Output

For automation and compliance reporting:

kube-bench run --json | jq .

Sample JSON structure:

{
  "Controls": [
    {
      "id": "1",
      "version": "cis-1.9",
      "detected_version": "1.29",
      "text": "Control Plane Security Configuration",
      "tests": [
        {
          "section": "1.1",
          "desc": "Control Plane Node Configuration Files",
          "results": [
            {
              "test_number": "1.1.1",
              "test_desc": "Ensure that the API server pod specification file permissions are set to 600 or more restrictive",
              "status": "PASS",
              "scored": true
            },
            {
              "test_number": "1.2.1",
              "test_desc": "Ensure that the --anonymous-auth argument is set to false",
              "status": "FAIL",
              "scored": true,
              "remediation": "Edit the API server pod specification file..."
            }
          ]
        }
      ]
    }
  ],
  "Totals": {
    "total_pass": 43,
    "total_fail": 9,
    "total_warn": 11,
    "total_info": 0
  }
}

Filtering JSON Output

# Get only FAIL results
kube-bench run --json | jq '.Controls[].tests[].results[] | select(.status == "FAIL")'

# Count FAILs per section
kube-bench run --json | jq '[.Controls[].tests[].results[] | select(.status == "FAIL")] | length'

# Get remediation for all FAILs
kube-bench run --json | jq '.Controls[].tests[].results[] | select(.status == "FAIL") | {test: .test_number, desc: .test_desc, fix: .remediation}'

Key Flags Reference

Flag Description Example
--targets Run checks for specific targets: master, node, etcd, controlplane, policies kube-bench run --targets master
--check Run a specific check by ID kube-bench run --check 1.2.1
--group Run all checks in a specific group kube-bench run --group 1.2
--json Output results in JSON format kube-bench run --json
--junit Output results in JUnit XML format kube-bench run --junit
--benchmark Specify the CIS benchmark version kube-bench run --benchmark cis-1.9
--config-dir Specify the configuration directory kube-bench run --config-dir /etc/kube-bench/cfg
--config Specify the configuration file kube-bench run --config /path/to/config.yaml
--noremediations Suppress remediation text in output kube-bench run --noremediations
--nosummary Suppress summary in output kube-bench run --nosummary
--nototals Suppress totals in output kube-bench run --nototals

Practical Flag Combinations

# Run only API server checks
kube-bench run --group 1.2

# Run a single check to verify a fix
kube-bench run --check 1.2.1

# Run master checks with JSON output
kube-bench run --targets master --json

# Run checks and only show the summary
kube-bench run --targets master --noremediations

Choosing the Right Installation Method

Method Best For CKS Exam Production
Binary Quick one-off scans, SSH access to nodes Sometimes Ad-hoc checks
Container Docker-based environments Rarely CI/CD integration
Kubernetes Job Standard approach, no SSH needed Most common Automated scanning

CKS exam recommendation: Practice the Job method until you can create the YAML from memory. Also know the binary method as a fallback — the exam may provide kube-bench pre-installed on nodes.


Practice Questions

Q1: What is kube-bench and who maintains it?

Show Answer

kube-bench is an open-source Go application maintained by Aqua Security. It automates CIS Kubernetes Benchmark checks by inspecting running processes, configuration files, and file permissions on Kubernetes nodes. It compares actual configurations against CIS recommendations and produces a PASS/FAIL/WARN/INFO report.


Q2: Why must kube-bench run on the node itself rather than remotely?

Show Answer

kube-bench inspects local processes (via /proc or ps), local configuration files (in /etc/kubernetes/manifests/, /var/lib/kubelet/), and local file permissions. These are not accessible remotely through the Kubernetes API. The tool needs hostPID access and filesystem mounts to check the actual state of the node’s configuration.


Q3: Write the key spec fields needed in a kube-bench Job to scan a control plane node.

Show Answer

The critical fields are:

spec:
  template:
    spec:
      hostPID: true                                    # Access host process list
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""      # Schedule on control plane
      tolerations:
        - key: node-role.kubernetes.io/control-plane   # Tolerate control plane taint
          operator: Exists
          effect: NoSchedule
      containers:
        - name: kube-bench
          image: aquasec/kube-bench:v0.8.0
          command: ["kube-bench", "run", "--targets", "master"]
          volumeMounts:
            - name: etc-kubernetes
              mountPath: /etc/kubernetes
              readOnly: true
            - name: var-lib-kubelet
              mountPath: /var/lib/kubelet
              readOnly: true
      restartPolicy: Never
      volumes:
        - name: etc-kubernetes
          hostPath:
            path: /etc/kubernetes
        - name: var-lib-kubelet
          hostPath:
            path: /var/lib/kubelet

Key points: hostPID: true, correct nodeSelector, tolerations for the control plane taint, hostPath volume mounts, and restartPolicy: Never.


Q4: A kube-bench scan shows 9 FAIL, 11 WARN, and 43 PASS results. What do you address first and why?

Show Answer

Address FAIL results first. FAIL means the configuration explicitly does not meet the CIS benchmark — these represent known security gaps. WARN results require manual review because kube-bench couldn’t automatically determine the status (the check may pass or fail depending on context).

After fixing all 9 FAIL results, review the 11 WARN results to determine which are actual risks vs. acceptable configurations. PASS results require no action.


Q5: What kube-bench command would you run to check only the API server section and output the results in JSON format?

Show Answer
kube-bench run --group 1.2 --json
  • --group 1.2 selects only Section 1.2 (API Server checks)
  • --json outputs results in JSON format

To filter for only FAIL results:

kube-bench run --group 1.2 --json | jq '.Controls[].tests[].results[] | select(.status == "FAIL")'

Next: Learn what kube-bench checks on the control plane with Scanning Control Plane →


← Understanding CIS Benchmarks Scanning Control Plane →

Updated: