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.
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.
systemctl: sudo systemctl disable bluetooth.service
sudo systemctl disable cups.service
sudo systemctl disable avahi-daemon.service
kubelet configuration via /etc/kubernetes/kubelet.conf with exact key-value pairs: # /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
PodSecurity admission controller via kube-apiserver flags: --enable-admission-plugins=PodSecurity,NodeRestriction,ServiceAccount,DefaultStorageClass,ResourceQuota
--admission-control-config-file=/etc/kubernetes/admission-config.yaml
PodSecurity policy in 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.
Ensure admission controllers are actively enforcing policies.
PodSecurity is enabled and active: kubectl get mutatingwebhookconfigurations pod-security-admission -o yaml
webhook section with matchPolicy: Equivalent, failurePolicy: Fail, and rules that target pods in * API groups: 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.
Implement fine-grained access using the K8s SB’s RBAC controls.
ClusterRole for developers: # 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 to validate access: kubectl auth can-i create pods --as=alice --namespace=prod
kubectl auth can-i update deployments --as=alice --namespace=prod
RoleBinding status: 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
The control plane must be hardened and monitored.
kube-apiserver: --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 to inspect audit events: 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
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'
PodSecurity annotations: kubectl get pods -A -o json | \
jq -r '.items[] | select(.metadata.annotations | has("pod-security.kubernetes.io/enforce")) | .metadata.namespace + "/" + .metadata.name'
PodSecurity level via kubectl describe: kubectl describe pod test-pod | grep -A 5 "Pod Security"
curl to query the PodSecurity admission webhook directly: 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.
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.
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.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy