← Back to Guide
Security & Hardening L3 · ADVANCED ~120 min

CIS Benchmark Remediation with kube-bench

Run kube-bench against a cluster to assess CIS Kubernetes Benchmark compliance. Build a prioritised remediation plan, implement 10 failing checks, re-run to measure the delta, and target a pass rate of 95% or above.

Objective

The CIS Kubernetes Benchmark defines security best practices for cluster configuration. kube-bench automates the assessment. This exercise runs a full benchmark scan, parses the results into a prioritised backlog using a risk matrix, implements remediation for the 10 most impactful failing checks, and re-validates to measure the improvement in compliance score.

Prerequisites

Some remediations require restarting the API server or kubelet. Do not run this on production clusters without a maintenance window.

Steps

01

Run kube-bench as a Kubernetes Job

Running kube-bench as a Job on the master node gives it access to the actual configuration files at the paths the CIS Benchmark expects.

# Run kube-bench Job on the control plane
cat << 'EOF' | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
  name: kube-bench
spec:
  template:
    spec:
      hostPID: true
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""
      tolerations:
      - key: node-role.kubernetes.io/control-plane
        effect: NoSchedule
      containers:
      - name: kube-bench
        image: aquasec/kube-bench:latest
        command: ["kube-bench", "--json", "run", "--targets",
                  "master,etcd,policies"]
        volumeMounts:
        - name: var-lib-etcd
          mountPath: /var/lib/etcd
          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-etcd
        hostPath: { path: /var/lib/etcd }
      - name: etc-kubernetes
        hostPath: { path: /etc/kubernetes }
      - name: usr-bin
        hostPath: { path: /usr/bin }
EOF

# Wait for completion and save output
kubectl wait job/kube-bench --for=condition=Complete --timeout=120s
kubectl logs job/kube-bench > kube-bench-results.json

# Run worker node checks separately
kubectl delete job kube-bench
# Modify job above to targets: "node" and run on worker nodes
02

Parse results and calculate initial score

# Parse JSON output with Python
python3 << 'EOF'
import json, sys

with open('kube-bench-results.json') as f:
    data = json.load(f)

total_pass = total_fail = total_warn = 0
failures = []

for control in data.get('Controls', []):
    for group in control.get('tests', []):
        for test in group.get('results', []):
            status = test.get('status', '')
            if status == 'PASS':
                total_pass += 1
            elif status == 'FAIL':
                total_fail += 1
                failures.append({
                    'id': test.get('test_number'),
                    'desc': test.get('test_desc'),
                    'remediation': test.get('remediation', '')[:100],
                    'scored': test.get('scored', True)
                })
            elif status == 'WARN':
                total_warn += 1

total = total_pass + total_fail
score = (total_pass / total * 100) if total > 0 else 0

print(f"=== CIS Benchmark Results ===")
print(f"PASS:  {total_pass}")
print(f"FAIL:  {total_fail}")
print(f"WARN:  {total_warn}")
print(f"Score: {score:.1f}%")
print(f"\nTop 10 Failures:")
for i, f in enumerate(failures[:10], 1):
    print(f"  {i}. [{f['id']}] {f['desc'][:80]}")
EOF
03

Prioritise remediations with risk matrix

Not all failures are equal. Use impact (what an attacker gains) × exploitability (how easy it is) to prioritise. Focus on these common high-impact failures first:

CIS IDCheckImpactEffortPriority
1.2.1API server anonymous-auth=falseCriticalLowP1
1.2.6API server audit logging enabledHighMediumP1
1.2.22Admission plugins include NodeRestrictionHighLowP1
2.1etcd TLS for client connectionsCriticalMediumP1
3.2.1 Logging verbosity <= 2MediumLowP2
4.2.1kubelet anonymous-auth=falseCriticalLowP1
4.2.6kubelet protectKernelDefaults=trueMediumLowP2
5.1.3No wildcards in ClusterRolesHighHighP2
04

Implement common remediations

# Remediation 1.2.1: Disable anonymous auth on API server
# Edit /etc/kubernetes/manifests/kube-apiserver.yaml
# Add: --anonymous-auth=false

sudo sed -i 's/--anonymous-auth=true/--anonymous-auth=false/' \
  /etc/kubernetes/manifests/kube-apiserver.yaml
# API server pod will restart automatically (static pod)

# Remediation 1.2.6: Enable audit logging
cat > /etc/kubernetes/audit-policy.yaml << 'POLICY'
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
  resources:
  - group: ""
    resources: ["secrets"]
- level: Request
  resources:
  - group: "rbac.authorization.k8s.io"
    resources: ["clusterroles", "clusterrolebindings"]
- level: Metadata
  omitStages: ["RequestReceived"]
POLICY

# Add to kube-apiserver.yaml:
# --audit-policy-file=/etc/kubernetes/audit-policy.yaml
# --audit-log-path=/var/log/kubernetes/audit.log
# --audit-log-maxage=30
# --audit-log-maxbackup=10
# --audit-log-maxsize=100

# Remediation 4.2.1: Disable anonymous auth on kubelet
# Edit /var/lib/kubelet/config.yaml
cat >> /var/lib/kubelet/config.yaml << 'KUBELET'
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
authorization:
  mode: Webhook
KUBELET
sudo systemctl restart kubelet
05

Re-run kube-bench and measure delta

# Re-run kube-bench after remediations
kubectl apply -f kube-bench-job.yaml
kubectl wait job/kube-bench --for=condition=Complete --timeout=120s
kubectl logs job/kube-bench > kube-bench-results-v2.json

# Compare pass rates
python3 << 'EOF'
import json

def get_score(filename):
    with open(filename) as f:
        data = json.load(f)
    p = f = 0
    for control in data.get('Controls', []):
        for group in control.get('tests', []):
            for test in group.get('results', []):
                s = test.get('status', '')
                if s == 'PASS': p += 1
                elif s == 'FAIL': f += 1
    total = p + f
    return p, f, (p/total*100) if total > 0 else 0

p1, f1, s1 = get_score('kube-bench-results.json')
p2, f2, s2 = get_score('kube-bench-results-v2.json')

print(f"Before: {s1:.1f}% ({p1} pass, {f1} fail)")
print(f"After:  {s2:.1f}% ({p2} pass, {f2} fail)")
print(f"Delta:  +{s2-s1:.1f}% (+{p2-p1} checks fixed)")
EOF

Success Criteria

Further Reading