Understanding Kube-bench
Kube-bench is an open-source security auditing tool developed by Aqua Security that automates the process of checking a Kubernetes cluster against the CIS (Center for Internet Security) Kubernetes Benchmark. It runs a series of checks against your Kubernetes environment—whether it's a managed service like EKS, AKS, or GKE, or a self-managed cluster—and produces a detailed report highlighting security misconfigurations, missing controls, and remediation steps.
The tool is written in Go and is distributed as a container image, a standalone binary, or a Kubernetes job that runs within the cluster itself. Each check corresponds to a specific recommendation in the CIS benchmark, which covers control plane components (API server, kubelet, etcd), worker node configurations, RBAC policies, network policies, and more.
Why Security Auditing Matters for Kubernetes
Kubernetes ships with a vast array of configuration options, and many defaults are not security-hardened. A single misconfigured component—such as an API server with anonymous authentication enabled, or a kubelet with read-write access to its configuration—can lead to full cluster compromise. Regular auditing with kube-bench provides:
- Baseline hardening validation – Confirm that your cluster meets industry-standard security benchmarks before deploying workloads
- Compliance evidence – Generate reports for auditors, regulators, or internal security reviews
- Drift detection – Identify when configuration changes introduce new risks over time
- Multi-cloud consistency – Apply the same benchmark logic across EKS, AKS, GKE, and on-premises clusters
- Shift-left security – Catch misconfigurations early in the development lifecycle rather than waiting for a penetration test
The CIS Kubernetes Benchmark is regularly updated (currently at version 1.9.0 as of early 2025), and kube-bench tracks these revisions so you can audit against the latest guidance.
Installation and Setup
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Prerequisites
Before running kube-bench, you need:
- A running Kubernetes cluster (any version 1.19+ is supported for recent CIS benchmarks)
kubectlconfigured with cluster-admin access for the most thorough audit (some checks require node-level access)- Docker or a container runtime if running kube-bench as a container
- Alternatively, direct node access if running the binary on control plane or worker nodes
Option 1: Running as a Kubernetes Job
This is the most common approach for auditing managed Kubernetes services where you may not have direct access to the control plane nodes. The job runs on a worker node and checks what it can access—some control plane checks will be marked as "WARN" or "FAIL" on managed services because those components are not directly accessible.
Create a file named kube-bench-job.yaml:
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
namespace: kube-bench
spec:
template:
metadata:
labels:
app: kube-bench
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:v0.9.0
command: ["kube-bench"]
args: ["--benchmark", "cis-1.9"]
volumeMounts:
- name: var-lib-etcd
mountPath: /var/lib/etcd
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
- name: etc-systemd
mountPath: /etc/systemd
readOnly: true
- name: etc-cni
mountPath: /etc/cni/net.d
readOnly: true
- name: usr-bin
mountPath: /usr/local/mounts/bin
readOnly: true
- name: kubelet-lib
mountPath: /var/lib/kubelet
readOnly: true
- name: kubelet-conf
mountPath: /etc/kubernetes/kubelet
readOnly: true
- name: conf
mountPath: /etc/kubernetes/kubelet/conf
readOnly: true
restartPolicy: Never
volumes:
- name: var-lib-etcd
hostPath:
path: /var/lib/etcd
- name: etc-kubernetes
hostPath:
path: /etc/kubernetes
- name: etc-systemd
hostPath:
path: /etc/systemd
- name: etc-cni
hostPath:
path: /etc/cni/net.d
- name: usr-bin
hostPath:
path: /usr/bin
- name: kubelet-lib
hostPath:
path: /var/lib/kubelet
- name: kubelet-conf
hostPath:
path: /etc/kubernetes/kubelet
- name: conf
hostPath:
path: /etc/kubernetes/kubelet/conf
---
apiVersion: v1
kind: Namespace
metadata:
name: kube-bench
Apply the job and monitor its completion:
kubectl create -f kube-bench-job.yaml
kubectl wait --for=condition=complete job/kube-bench -n kube-bench --timeout=300s
kubectl logs job/kube-bench -n kube-bench
The logs will contain the full audit report. For persistent storage of results, you can redirect logs to a file or forward them to a log aggregation system.
Option 2: Running as a Binary on the Node
If you have SSH access to control plane and worker nodes, you can run kube-bench directly. This gives the most accurate results because the tool can inspect configuration files on disk and running processes.
# Download the latest binary
curl -LO https://github.com/aquasecurity/kube-bench/releases/download/v0.9.0/kube-bench_0.9.0_linux_amd64.tar.gz
tar -xzf kube-bench_0.9.0_linux_amd64.tar.gz
# Run on a control plane node
./kube-bench node --benchmark cis-1.9 --targets master
# Run on a worker node
./kube-bench node --benchmark cis-1.9 --targets node
The --targets flag accepts master, node, controlplane, etcd, policies, or managedservices. Use the appropriate target based on the node's role.
Option 3: Running via Docker
This method is useful for ad-hoc audits without modifying the cluster. It requires access to the Docker socket and relevant host paths:
docker run --rm -it \
--pid=host \
-v /etc/kubernetes:/etc/kubernetes:ro \
-v /var/lib/etcd:/var/lib/etcd:ro \
-v /etc/systemd:/etc/systemd:ro \
-v /etc/cni/net.d:/etc/cni/net.d:ro \
-v /usr/bin:/usr/local/mounts/bin:ro \
-v /var/lib/kubelet:/var/lib/kubelet:ro \
aquasec/kube-bench:v0.9.0 \
kube-bench --benchmark cis-1.9
Understanding the Output
Kube-bench produces results organized by CIS benchmark sections. Each check has a status and, if applicable, a remediation message. Here's a sample output excerpt:
[INFO] 1 Master Node Security Configuration
[INFO] 1.1 Master Node Configuration Files
[PASS] 1.1.1 Ensure that the API server pod specification file permissions are set to 644 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.3 Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive (Automated)
Remediation: Run the following command (using the appropriate file location):
chmod 644 /etc/kubernetes/manifests/kube-controller-manager.yaml
[WARN] 1.1.4 Ensure that the controller manager pod specification file ownership is set to root:root (Manual)
[INFO] 1.2 API Server
[FAIL] 1.2.1 Ensure that the --anonymous-auth argument is set to false (Automated)
Remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
and set the parameter: --anonymous-auth=false
[PASS] 1.2.2 Ensure that the --basic-auth-file parameter is not set (Automated)
Each check carries one of four statuses:
- PASS – The configuration meets the CIS recommendation
- FAIL – The configuration does not meet the recommendation and immediate remediation is advised
- WARN – The check is manual and requires human review (e.g., checking a file's ownership on a managed service where access is restricted)
- INFO – Section header or informational note, not a check itself
The output also includes a summary at the end:
== Summary ==
15 checks PASS
6 checks FAIL
3 checks WARN
0 checks INFO
Exporting Results as JSON
For integration with CI/CD pipelines or security dashboards, kube-bench can output results in JSON format:
kube-bench --benchmark cis-1.9 --json | tee kube-bench-results.json
This produces a structured JSON document with each check's ID, status, description, and remediation text, suitable for parsing by downstream tools like Falco, custom scripts, or compliance platforms.
Customizing Checks and Configurations
Using Custom YAML Check Definitions
Kube-bench uses YAML configuration files to define which checks to run. You can create a custom configuration that extends or overrides the built-in CIS benchmark. This is essential when you have organizational security policies that differ from CIS defaults.
Create a directory for custom checks and populate it with YAML files:
mkdir -p ./custom-checks
cat > ./custom-checks/my-checks.yaml << 'EOF'
---
controls:
- id: 1
text: "Master Node Security Configuration - Custom Overrides"
type: "master"
groups:
- id: 1.1
text: "Master Node Configuration Files"
checks:
- id: 1.1.1
text: "Ensure API server pod spec file permissions are 600 or more restrictive"
audit: "stat -c %a /etc/kubernetes/manifests/kube-apiserver.yaml"
tests:
test_op: eq
test_var: "600"
bin_op: or
test_op: eq
test_var: "400"
remediation: |
Run: chmod 600 /etc/kubernetes/manifests/kube-apiserver.yaml
scored: true
- id: 1.1.99
text: "Custom check: Ensure audit log maxage is at least 30 days"
audit: "grep -E '--audit-log-maxage' /etc/kubernetes/manifests/kube-apiserver.yaml | awk -F= '{print $2}' | tr -d ' '"
tests:
bin_op: or
test_op: gt
test_var: "29"
remediation: |
Set --audit-log-maxage=30 or higher in the API server manifest
scored: true
EOF
Run kube-bench with your custom checks directory:
kube-bench --benchmark cis-1.9 --cfg ./custom-checks
The --cfg flag points to a directory containing check definition files. Kube-bench merges these with the built-in checks, allowing you to override existing checks or add entirely new ones.
Filtering Checks by Group or ID
You can run a subset of checks using the --group or --check flags:
# Run only checks from group 1.1 (Configuration Files)
kube-bench --benchmark cis-1.9 --group 1.1
# Run only specific checks
kube-bench --benchmark cis-1.9 --check 1.2.1,1.2.2,2.1.1
# Run checks targeting only the control plane
kube-bench --benchmark cis-1.9 --targets master
This granularity is useful for iterative remediation—you fix a set of issues and then re-run only those checks to confirm resolution without waiting for the full audit.
Integrating Kube-bench into CI/CD Pipelines
GitHub Actions Example
A powerful pattern is running kube-bench against ephemeral test clusters during CI. This catches configuration drift before it reaches production. Below is a complete GitHub Actions workflow that provisions a kind cluster, runs kube-bench, and fails the build if critical checks fail.
name: Kubernetes Security Audit
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
kube-bench-audit:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Create kind cluster
uses: helm/kind-action@v1
with:
cluster_name: audit-test
wait: 120s
- name: Install kube-bench as a job
run: |
kubectl create namespace kube-bench --dry-run=client -o yaml | kubectl apply -f -
cat < kube-bench-output.json
# Extract FAIL count from JSON output
FAIL_COUNT=$(jq '[.results[].checks[] | select(.status == "FAIL")] | length' kube-bench-output.json)
echo "Failed checks: $FAIL_COUNT"
if [ "$FAIL_COUNT" -gt 0 ]; then
echo "::error::Security audit failed with $FAIL_COUNT failing checks"
exit 1
fi
echo "All automated checks passed"
- name: Upload audit report
uses: actions/upload-artifact@v4
with:
name: kube-bench-report
path: kube-bench-output.json
This workflow creates a clean cluster, runs the audit, parses the JSON output with jq, and fails if any FAIL-level checks are detected. The report is uploaded as an artifact for later review. You can adjust the threshold—for example, only failing on "scored" checks or excluding known exceptions for managed services.
GitLab CI Example
For teams using GitLab, here's an equivalent pipeline snippet that runs kube-bench against a cluster referenced via KUBECONFIG:
kube-bench-audit:
stage: security
image:
name: aquasec/kube-bench:v0.9.0
entrypoint: [""]
script:
- kube-bench --benchmark cis-1.9 --json --output-file /tmp/report.json || true
- |
FAIL_COUNT=$(jq '[.results[].checks[] | select(.status == "FAIL")] | length' /tmp/report.json)
echo "FAIL_COUNT=$FAIL_COUNT"
if [ "$FAIL_COUNT" -gt 5 ]; then
echo "Too many failing checks: $FAIL_COUNT"
exit 1
fi
artifacts:
paths:
- /tmp/report.json
expire_in: 30 days
Note the || true after the kube-bench command—this allows the audit to complete even if kube-bench exits with a non-zero code (which it does when failures are found). The conditional logic then decides whether the pipeline should actually fail based on your tolerance threshold.
Handling Managed Kubernetes Services
When auditing managed services like AWS EKS, Azure AKS, or Google GKE, you cannot access the control plane components directly. Kube-bench adapts by running only worker-node checks and flagging control plane checks as WARN or FAIL with a note that they require manual verification through the cloud provider's compliance reports.
To get meaningful results on managed services, use the appropriate benchmark target:
# For GKE
kube-bench --benchmark cis-1.9 --targets managedservices
# For EKS (runs node and managed-specific checks)
kube-bench --benchmark cis-1.9 --targets node,managedservices
# For AKS
kube-bench --benchmark cis-1.9 --targets managedservices
Many cloud providers publish their own compliance attestations (e.g., AWS Artifact, Azure Compliance Manager). Cross-reference kube-bench's findings with these provider attestations to build a complete picture. For control plane checks that kube-bench cannot verify directly, document them in your risk register and request evidence from your cloud provider.
Scheduling Regular Audits with CronJob
One-time audits are valuable, but continuous auditing catches drift. Deploy kube-bench as a CronJob that runs weekly and pushes results to your observability stack:
apiVersion: batch/v1
kind: CronJob
metadata:
name: kube-bench-weekly
namespace: kube-bench
spec:
schedule: "0 2 * * 1" # Every Monday at 2 AM
jobTemplate:
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:v0.9.0
command: ["kube-bench"]
args: ["--benchmark", "cis-1.9", "--json", "--output-file", "/tmp/report.json"]
- name: results-publisher
image: curlimages/curl:latest
command: ["/bin/sh"]
args:
- "-c"
- |
curl -X POST -H "Content-Type: application/json" \
--data-binary @/tmp/report.json \
https://your-observability-platform.example.com/api/kube-bench-results
volumeMounts:
- name: shared-data
mountPath: /tmp
restartPolicy: Never
volumes:
- name: shared-data
emptyDir: {}
# Include host path volumes as needed for your cluster configuration
backoffLimit: 2
This CronJob runs the audit, stores the JSON report in a shared volume, and then a sidecar container posts it to an observability endpoint. You could alternatively push results to an S3 bucket, a Slack webhook, or a Prometheus pushgateway for alerting integration.
Remediation Automation
While kube-bench focuses on detection, you can combine it with remediation tooling. Here are several practical approaches:
Automated Fix Script
Parse kube-bench JSON output and apply fixes programmatically. This example uses jq to iterate over failing checks and execute their remediation commands (use with extreme caution and only in non-production environments):
#!/bin/bash
# remediation-script.sh - Parse kube-bench JSON and apply remediations
# WARNING: Review all remediations before running in production
REPORT="/tmp/kube-bench-report.json"
jq -r '.results[].checks[] | select(.status == "FAIL") | .remediation' "$REPORT" | while read -r remediation; do
echo "Applying remediation: $remediation"
# In a real implementation, you would map each remediation to a specific action
# rather than blindly executing arbitrary text
done
Policy-as-Code Integration
Feed kube-bench findings into admission controllers or policy engines like OPA Gatekeeper or Kyverno. For example, if kube-bench detects that the API server allows anonymous authentication, you can create a Gatekeeper constraint that prevents deploying workloads until the issue is resolved:
# Gatekeeper constraint template triggered by kube-bench findings
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: kubeapiserveranonymousauth
spec:
crd:
spec:
names:
kind: KubeAPIServerAnonymousAuth
validation:
message: "API server anonymous authentication must be disabled per CIS 1.2.1"
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8s.kubeapiserver
violation[{"msg": msg}] {
input.review.kind.kind == "Pod"
input.review.object.metadata.namespace == "kube-system"
# Logic to detect API server configuration issues
msg := "CIS 1.2.1 violation detected"
}
This approach shifts remediation left—developers get immediate feedback when their configurations would cause audit failures downstream.
Best Practices for Production Deployments
- Run audits on a schedule, not just once – Kubernetes configurations change over time. A cluster that passes today might fail next month after an upgrade, a new operator installation, or a manual tweak. Weekly audits are a good starting cadence.
- Treat WARN as actionable – WARN statuses indicate manual checks. Assign these to a human reviewer or create a ticket in your ticketing system. Do not let them accumulate as noise.
-
Use versioned benchmarks – Always specify
--benchmark cis-1.9(or the latest version) explicitly rather than relying on defaults. This ensures reproducibility across environments and time. - Combine with other tools – Kube-bench audits the cluster infrastructure. Complement it with workload-level scanning (Trivy, Falco) for container vulnerabilities, runtime anomalies, and compliance gaps inside pods.
- Store historical results – Keep JSON reports in a time-series database or cloud storage bucket. Trends are more valuable than point-in-time snapshots—you can track whether your security posture is improving or degrading over months.
- Customize checks for your environment – Not every CIS recommendation applies universally. If your cluster uses a different authentication mechanism, override the relevant check rather than perpetually accepting a FAIL.
- Test remediations in staging first – Some CIS recommendations (like restricting the kubelet's permissions) can break legitimate workloads. Validate changes in a non-production cluster before applying them broadly.
- Integrate with alerting – Pipe critical FAIL results to PagerDuty, Slack, or your incident management system. A failing check on the API server's anonymous authentication should trigger the same urgency as a production outage.
- Document exceptions formally – If you accept a specific FAIL (e.g., a check that doesn't apply to your architecture), document it in a risk acceptance register with a justification, approver, and review date. This keeps audit trails clean and auditable.
- Keep kube-bench updated – New releases track new CIS benchmark versions and add support for newer Kubernetes features. Subscribe to the Aqua Security GitHub repository for release notifications.
Troubleshooting Common Issues
Issue: "Unable to access config file" on managed services
On EKS, AKS, or GKE, many control plane checks will fail with "unable to access" errors. This is expected behavior—the control plane is managed by the provider and not directly accessible. Use --targets managedservices to run only the applicable subset of checks.
Issue: Kube-bench job stays in "Pending" state
Ensure the hostPID: true field is set in the pod spec and that the node has the necessary host paths available. Some clusters restrict host path mounts via PodSecurityPolicy or PodSecurity admission—you may need to create an appropriate policy or use a privileged pod.
Issue: JSON output is empty or malformed
When using --json, ensure you're capturing stdout (not stderr) and that the kube-bench process completes fully. If running as a Kubernetes job, use kubectl logs and redirect to a file; do not assume the job's exit code indicates data completeness.
Issue: Checks pass on one node but fail on another
Kube-bench runs checks specific to the node it's executing on. If your cluster has heterogeneous node configurations (different OS versions, different kubelet versions), run kube-bench on a representative sample of each node type and aggregate the results. A DaemonSet-based approach can help achieve this:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-bench-daemon
namespace: kube-bench
spec:
selector:
matchLabels:
app: kube-bench-node
template:
metadata:
labels:
app: kube-bench-node
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:v0.9.0
command: ["kube-bench"]
args: ["--benchmark", "cis-1.9", "--targets", "node", "--json"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
volumes:
- name: var-lib-kubelet
hostPath:
path: /var/lib/kubelet
- name: etc-kubernetes
hostPath:
path: /etc/kubernetes
tolerations:
- operator: Exists
effect: NoSchedule
This DaemonSet runs kube-bench on every node in the cluster, giving you per-node audit results that you can collect and compare.
Conclusion
Kube-bench transforms the CIS Kubernetes Benchmark from a static PDF document into an executable, automatable security audit. By integrating it into your cluster provisioning workflow, CI/CD pipelines, and scheduled operations, you establish a continuous feedback loop that catches misconfigurations before they become incidents. The tool's extensibility—custom check definitions, JSON output, and flexible deployment models—means it can adapt to managed services, air-gapped environments, and organization-specific policies. Start with a one-time audit to establish your baseline, then embed kube-bench into your operational rhythm. Combined with runtime security tools, policy-as-code admission controllers, and a disciplined remediation process, kube-bench becomes a cornerstone of a defense-in-depth strategy for Kubernetes security.