Kubernetes Security Benchmark Compliance Guide

Production-ready guide covering kubernetes security benchmark compliance guide with implementation patterns, code examples, and anti-patterns for enterprise engineering teams.

Kubernetes Security Benchmark compliance is essential during cluster onboarding, security audits, CI/CD integration, and incident response. This guide maps the Kubernetes Security Benchmark (K8s SB) controls directly to concrete actions, configurations, and workflows using standard tools. Use it when validating a cluster against the 100+ controls from the Kubernetes Security Foundation, or when building a security automation pipeline that checks compliance at every deployment.

Preparing the Cluster for Compliance

Begin with a baseline configuration that aligns with the K8s SB’s foundational controls. Ensure every node runs a hardened OS with minimal attack surface.

  sudo systemctl disable bluetooth.service
  sudo systemctl disable cups.service
  sudo systemctl disable avahi-daemon.service
  # /etc/kubernetes/kubelet.conf
  apiVersion: kubelet.config.k8s.io/v1beta1
  kind: KubeletConfiguration
  authentication:
    x509:
      clientCAFile: /etc/kubernetes/pki/ca.crt
    webhook:
      enabled: true
      cacheTTL: 2m0s
  authorization:
    mode: Webhook
    webhook:
      cacheAuthorizedTTL: 5m0s
      cacheUnathorizedTTL: 30s
  cgroupDriver: systemd
  clusterDNS:
    - 10.96.0.10
  clusterDomain: cluster.local
  featureGates:
    RotateKubeletServerCertificate: true
    PodSecurity: true
  kubeAPIQPS: 100
  kubeAPIBurst: 200
  --enable-admission-plugins=PodSecurity,NodeRestriction,ServiceAccount,DefaultStorageClass,ResourceQuota
  --admission-control-config-file=/etc/kubernetes/admission-config.yaml
  kind: PodSecurityConfiguration
  apiVersion: apiserver.config.k8s.io/v1
  defaults:
    enforce: "restricted"
    audit: "restricted"
    warn: "restricted"
  exemptions:
    usernames:
      - "system:node:*"
      - "system:apiserver"
    runtimeClasses:
      - "host"

> Common failure: The enforce level is applied, but the audit and warn levels are misconfigured or missing. Without audit and warn, the cluster produces no logs during rollout, and violations go unnoticed.

Validating Admission Control Configuration

Ensure admission controllers are actively enforcing policies.

  kubectl get mutatingwebhookconfigurations pod-security-admission -o yaml
  apiVersion: admissionregistration.k8s.io/v1
  kind: MutatingWebhookConfiguration
  metadata:
    name: pod-security-admission
  webhooks:
    - name: pod-security.admission.k8s.io
      admissionReviewVersions:
        - v1
        - v1beta1
      matchPolicy: Equivalent
      rules:
        - apiGroups: [""]
          apiVersions: ["v1"]
          resources: ["pods"]
          scope: "*"
      failurePolicy: Fail
      sideEffects: NoneOnDryRun
      clientConfig:
        service:
          name: pod-security-webhook
          namespace: kube-system
          path: /mutate
        caBundle: <base64-encoded-ca-cert>
  kubectl get service -n kube-system pod-security-webhook
  kubectl get pod -n kube-system -l app=pod-security-webhook
  # test-pod.yaml
  apiVersion: v1
  kind: Pod
  metadata:
    name: test-pod
  spec:
    containers:
      - name: nginx
        image: nginx:alpine
        securityContext:
          runAsNonRoot: true
          runAsUser: 1000
          fsGroup: 2000
          allowPrivilegeEscalation: false
    securityContext:
      runAsNonRoot: false
      runAsUser: 100
      supplementalGroups: [100, 200]
      seLinuxOptions:
        level: "s0:c123,c456"
  kubectl apply -f test-pod.yaml
  kubectl get pod test-pod -o jsonpath='{.status.conditions[*].type}' | grep -i "podsecurity"
  kubectl describe pod test-pod

> Sharp edge: The PodSecurity admission controller only applies the policy to pods at creation. If a pod is patched after creation, the policy is not rechecked. Use kubectl patch pod test-pod --type=merge -p '{"spec":{"securityContext":{"runAsNonRoot":true}}}' and observe the PodSecurity condition remains unchanged.

Enforcing Role-Based Access Control (RBAC)

Implement fine-grained access using the K8s SB’s RBAC controls.

  # developer-role.yaml
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRole
  metadata:
    name: developer
  rules:
    - apiGroups: [""]
      resources: ["pods", "services", "configmaps", "secrets"]
      verbs: ["get", "list", "create", "update", "delete", "patch"]
    - apiGroups: [""]
      resources: ["pods/exec"]
      verbs: ["create"]
    - apiGroups: ["apps"]
      resources: ["deployments", "statefulsets"]
      verbs: ["get", "list", "create", "update", "delete"]
    - apiGroups: ["batch"]
      resources: ["jobs"]
      verbs: ["get", "list", "create"]
    - apiGroups: ["networking.k8s.io"]
      resources: ["ingresses"]
      verbs: ["get", "list", "create"]
  kubectl apply -f developer-role.yaml
  kubectl create rolebinding dev-team --clusterrole=developer --group=developers --namespace=prod
  kubectl auth can-i create pods --as=alice --namespace=prod
  kubectl auth can-i update deployments --as=alice --namespace=prod
  kubectl get rolebinding dev-team -o yaml
  kubectl describe rolebinding dev-team

> Silent failure: A RoleBinding is created but subjects are missing. The subjects field is a list of kind, name, and apiGroup, but if kind: User is misspelled as kind: User, the binding is ignored. Correct:

  subjects:
    - kind: User
      name: [email protected]
      apiGroup: rbac.authorization.k8s.io

Incorrect:

  subjects:
    - kind: User
      name: [email protected]
      apiGroup: rbac.authorization.k8s.io
  # missing 'apiGroup' field — binding fails silently

Securing the Control Plane

The control plane must be hardened and monitored.

  --audit-policy-file=/etc/kubernetes/audit-policy.yaml
  --audit-log-path=/var/log/kubernetes/audit.log
  --audit-log-maxsize=104857600
  --audit-log-maxage=30
  --audit-log-maxbackup=10
  # /etc/kubernetes/audit-policy.yaml
  apiVersion: audit.k8s.io/v1
  kind: Policy
  rules:
    - level: Metadata
      resources:
        - group: ""
          resources: ["pods"]
      namespaces:
        - "prod"
        - "staging"
    - level: RequestResponse
      resources:
        - group: "apps"
          resources: ["deployments"]
      verbs: ["create", "update", "delete"]
    - level: Request
      resources:
        - group: "rbac.authorization.k8s.io"
          resources: ["rolebindings"]
        - group: "authentication.k8s.io"
          resources: ["tokenreviews"]
    - level: Metadata
      resources:
        - group: "batch"
          resources: ["jobs"]
  ls /var/log/kubernetes/audit.log*
  kubectl logs -n kube-system kube-apiserver-<pod-name> | grep -i "audit"
  kubectl get auditlogs -n kube-system
  kubectl get events -n kube-system --field-selector=type=Normal | grep -i audit

> Failure mode: The audit-log-path is set but the directory is not mounted or writable. The API server starts but logs are not written. Check journalctl -u kube-apiserver for:

  Failed to create audit log file: /var/log/kubernetes/audit.log: No such file or directory

Monitoring and Validating Cluster Security

Use kubectl and curl to validate compliance continuously.

  kubectl get pods -A -o json | \
    jq -r '.items[] | select(.spec.securityContext.runAsNonRoot == false) | .metadata.namespace + "/" + .metadata.name'
  kubectl get pods -A -o json | \
    jq -r '.items[] | select(.metadata.annotations | has("pod-security.kubernetes.io/enforce")) | .metadata.namespace + "/" + .metadata.name'
  kubectl describe pod test-pod | grep -A 5 "Pod Security"
  curl -X POST \
    -H "Content-Type: application/json" \
    -d @test-pod.json \
    http://pod-security-webhook.kube-system.svc.cluster.local:80/mutate \
    -v

> Edge case: The MutatingWebhookConfiguration uses matchPolicy: Equivalent, but a new Pod resource is created with apiVersion: v1, and the webhook expects apiVersion: v1/pods. The matchPolicy: Equivalent ensures the match is exact, so a Pod with apiVersion: v1 is not matched unless resources: ["pods"] includes apiVersion: v1.

Automating Compliance Checks

Build a continuous compliance pipeline using kubectl, jq, and sh.

#!/bin/sh


set -euo pipefail

echo "Starting Kubernetes Security Benchmark Compliance Check"

# Check if PodSecurity is applied
echo "Checking PodSecurity enforcement..."
kubectl get pods -A -o jsonpath='{.items[*].metadata.annotations.pod-security\.kubernetes\.io/enforce}' | \
  grep -v "restricted" || {
    echo "ERROR: Not all pods use 'restricted' PodSecurity enforcement"
    exit 1
  }

# Check RBAC for critical namespaces
echo "Validating RBAC in prod and staging..."
for ns in prod staging; do
  kubectl auth can-i get pods --as=alice --namespace=$ns || {
    echo "ERROR: Alice lacks 'get' access to pods in $ns"
    exit 1
  }
done

# Check audit logging
echo "Verifying audit log availability..."
if [ ! -f /var/log/kubernetes/audit.log ]; then
  echo "ERROR: Audit log file missing"
  exit 1
fi

# Check control plane components
for comp in kube-apiserver kube-scheduler kube-controller-manager; do
  if ! kubectl get pod -n kube-system -l component=$comp | grep Running; then
    echo "ERROR: $comp not running"
    exit 1
  fi
done

echo "All compliance checks passed."

> Silent failure: The check-k8s-compliance.sh script runs but the jq command is not installed. The jsonpath query returns nothing, but no error is logged. Install jq in the CI/CD runner with apt install jq or apk add jq.

Summary

This guide maps Kubernetes Security Benchmark compliance to real, repeatable actions. Use the provided kubectl, systemctl, curl, and sh workflows to ensure every cluster meets the standard, and to catch subtle failures before they break production. The patterns described are universal: they work across all Kubernetes implementations, from bare metal to managed services, without reliance on any vendor’s tooling.

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.