← Back to DevBytes

Kube-bench Security Auditing: Complete Implementation Guide

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:

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:

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:

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


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.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles