k8s-security.pro
kubernetes security audit compliance soc2

How to Pass a Kubernetes Security Audit (2026 Guide)

A practical, do-this checklist to pass your kubernetes security audit. Self-check RBAC, NetworkPolicies, privileged pods, secrets, and audit logs with kubectl before the auditor arrives.

K8s Security Pro Team | | 13 min read

How to Pass a Kubernetes Security Audit

Your cluster is about to be audited. Maybe it’s a SOC2 Type II examination, an internal security review, or a customer sent you a 200-line security questionnaire before they’ll sign. Either way, someone with a checklist is about to look at your Kubernetes environment, and you want to walk in knowing exactly what they’ll find.

The good news: a kubernetes security audit is predictable. Auditors check the same high-risk controls almost every time, and every one of them is something you can self-check with kubectl in a few minutes. This guide walks through what auditors actually look for, how to verify each control before they do, how to remediate the gaps, and what evidence to have ready.

What Auditors Actually Check

Whether the framework is CIS Kubernetes Benchmark, SOC2, or a bespoke customer questionnaire, the findings cluster around the same eight areas:

Risk areaWhat they look forCIS / SOC2 reference
Privileged containersprivileged: true, root UID, host namespacesCIS 5.2.x
NetworkPoliciesNamespaces with no default-denyCIS 5.3.2 / CC6.6
Over-broad RBACcluster-admin bindings, wildcard verbsCIS 5.1.x / CC6.1
Image hygiene:latest tags, unsigned imagesCIS 5.5.1
Resource limitsPods with no CPU/memory limitsCIS 5.2 / A1.1
SA token automountautomountServiceAccountToken: true by defaultCIS 5.1.5/5.1.6
Audit loggingAPI server audit policy enabledCIS 1.2.x / CC7.1
Secret protectionPlaintext secrets, no encryption at restCIS 1.2.x / CC6.1

The rest of this guide is one section per area: self-check, remediate, and the evidence to keep.

The 30-second first pass

Before you go control by control, run one automated scan to see the shape of the problem. The open-source k8s-audit tool gives you a fast, read-only first pass across all of these areas in about 30 seconds — it’s the quickest way to know where you actually stand before you start fixing things. kube-bench and kubescape are also excellent for CIS-mapped scans:

# CIS Benchmark scan as a Job
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs job/kube-bench

# Broader framework scan
kubescape scan framework cis-v1.23-t1.0.1

Automated scans find the obvious gaps. The manual checks below catch the ones scanners miss and produce the evidence auditors want.

1. Privileged and Root Containers

Privileged containers are the number one breakout risk. A privileged: true pod can access host devices and effectively owns the node.

Self-check:

# Find privileged containers
kubectl get pods -A -o json | jq -r '
  .items[] | select(.spec.containers[].securityContext.privileged==true)
  | "\(.metadata.namespace)/\(.metadata.name)"'

# Find pods that can run as root
kubectl get pods -A -o json | jq -r '
  .items[] | select(
    (.spec.securityContext.runAsNonRoot != true) and
    (.spec.containers[].securityContext.runAsNonRoot != true)
  ) | "\(.metadata.namespace)/\(.metadata.name)"'

# Find host namespace sharing
kubectl get pods -A -o json | jq -r '
  .items[] | select(.spec.hostNetwork==true or .spec.hostPID==true or .spec.hostIPC==true)
  | "\(.metadata.namespace)/\(.metadata.name)"'

Remediate with a hardened securityContext and enforce it cluster-wide with Pod Security Standards. Apply the restricted profile at namespace level:

kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted

A compliant pod:

securityContext:
  runAsNonRoot: true
  runAsUser: 10001
  seccompProfile:
    type: RuntimeDefault
containers:
  - name: app
    securityContext:
      allowPrivilegeEscalation: false
      privileged: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]

Evidence: the namespace labels (kubectl get ns -L pod-security.kubernetes.io/enforce) and a Kyverno/Gatekeeper policy proving privileged pods are blocked at admission, not just absent today.

2. Missing NetworkPolicies

By default, every pod can talk to every other pod in the cluster. Auditors want to see a default-deny posture and explicit allow rules — this is CIS 5.3.2 and SOC2 CC6.6 (logical segmentation).

Self-check — find namespaces with no NetworkPolicy at all:

for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
  count=$(kubectl get netpol -n "$ns" --no-headers 2>/dev/null | wc -l)
  if [ "$count" -eq 0 ]; then echo "NO NetworkPolicy: $ns"; fi
done

Remediate with a default-deny-all policy in each workload namespace, then add explicit allows:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Remember: a default-deny egress policy also blocks DNS, so pair it with an allow rule to kube-system on UDP/TCP 53. Confirm your CNI actually enforces NetworkPolicy — Calico, Cilium, and Antrea do; the default kubenet on some managed clusters does not.

Evidence: exported NetworkPolicy YAML per namespace, plus a kubectl exec connectivity test showing a blocked cross-namespace connection.

3. Over-Broad RBAC and cluster-admin

The most common serious finding. A ServiceAccount bound to cluster-admin turns any pod breakout into full cluster compromise.

Self-check — enumerate every cluster-admin subject:

# Everyone bound to cluster-admin
kubectl get clusterrolebindings -o json | jq -r '
  .items[] | select(.roleRef.name=="cluster-admin")
  | .subjects[]? | "\(.kind)/\(.namespace // "-")/\(.name)"'

# Roles granting wildcard verbs or secret access
kubectl get clusterroles,roles -A -o json | jq -r '
  .items[] | select(.rules[]? |
    (.verbs[]? == "*") or
    ((.resources[]? == "secrets") and (.verbs[]? | test("get|list|watch"))))
  | "\(.kind)/\(.metadata.namespace // "-")/\(.metadata.name)"' | sort -u

kubectl auth can-i --list --as=system:serviceaccount:production:my-sa is invaluable for checking what a specific identity can actually do.

Remediate: replace cluster-admin with scoped Roles granting only the verbs and resources a workload needs. Prefer namespaced Role/RoleBinding over cluster-scoped. Remove RBAC for default ServiceAccounts and give each workload its own.

Evidence: exported ClusterRoleBindings, a documented justification for any remaining cluster-admin subject (ideally zero for workloads), and least-privilege Role definitions.

4. Mutable Image Tags (:latest)

:latest is non-deterministic — you can’t prove what code is running, and a rollback pulls something different. CIS 5.5.1 and every supply-chain reviewer flag it.

Self-check:

kubectl get pods -A -o json | jq -r '
  .items[].spec.containers[] |
  select(.image | test(":latest$") or (contains(":") | not))
  | .image' | sort -u

Remediate: pin images by digest (image@sha256:...) or immutable semver tags, and enforce it at admission. A Kyverno policy that blocks :latest:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-latest-tag
spec:
  validationFailureAction: Enforce
  rules:
    - name: require-image-tag
      match:
        any:
          - resources: { kinds: ["Pod"] }
      validate:
        message: "Using ':latest' or an untagged image is not allowed."
        pattern:
          spec:
            containers:
              - image: "!*:latest"

Evidence: the enforcing Kyverno/Gatekeeper policy plus a screenshot of a rejected deployment.

5. Missing Resource Limits

Missing CPU/memory limits enable noisy-neighbor DoS and are an availability finding (SOC2 A1.1).

Self-check:

kubectl get pods -A -o json | jq -r '
  .items[] | select(.spec.containers[] | .resources.limits == null)
  | "\(.metadata.namespace)/\(.metadata.name)"'

Remediate: set requests and limits on every container, and enforce a floor with a LimitRange per namespace plus a ResourceQuota to cap the namespace total:

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:        { cpu: "500m", memory: "512Mi" }
      defaultRequest: { cpu: "100m", memory: "128Mi" }

Evidence: LimitRange and ResourceQuota exports per namespace.

6. Service Account Token Automount

By default Kubernetes mounts a ServiceAccount token into every pod. If the pod doesn’t call the API, that token is pure attack surface — a breakout hands the attacker cluster credentials (CIS 5.1.5/5.1.6).

Self-check:

# ServiceAccounts that still automount by default
kubectl get sa -A -o json | jq -r '
  .items[] | select(.automountServiceAccountToken != false)
  | "\(.metadata.namespace)/\(.metadata.name)"'

Remediate: set automountServiceAccountToken: false on the ServiceAccount (or the pod spec) wherever the API isn’t used:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: web-frontend
  namespace: production
automountServiceAccountToken: false

Evidence: ServiceAccount manifests showing automount disabled for non-API workloads.

7. Audit Logging Disabled

If the API server audit log isn’t enabled, you can’t answer “who accessed this secret?” — a hard requirement for CIS 1.2.x and SOC2 CC7.1.

Self-check (self-managed clusters):

# Confirm the API server was started with an audit policy
ps -ef | grep kube-apiserver | grep -o 'audit-policy-file=[^ ]*'
ps -ef | grep kube-apiserver | grep -o 'audit-log-path=[^ ]*'

On EKS, audit events land in CloudWatch; GKE and AKS route them to Cloud Logging / Azure Monitor — confirm the log stream exists and is retained.

Remediate: deploy an audit policy that captures secrets, RBAC changes, exec sessions, and authentication at Metadata or RequestResponse level while dropping high-volume noise, and retain logs at least 90 days for SOC2.

Evidence: the audit policy file, the API server flags, and a sample query showing you can search by user, verb, and resource.

8. Unprotected Secrets

Auditors check that secrets aren’t hard-coded in manifests, aren’t world-readable via RBAC, and are encrypted at rest.

Self-check:

# etcd encryption config present? (self-managed)
ps -ef | grep kube-apiserver | grep -o 'encryption-provider-config=[^ ]*'

# Who can read secrets cluster-wide?
kubectl get clusterrolebindings -o json | jq -r '.items[].subjects[]? | .name' | sort -u
# then: kubectl auth can-i get secrets --as=<subject> -A

Remediate: enable an EncryptionConfiguration (AES-CBC or KMS provider) on the API server, restrict secrets verbs in RBAC to the few workloads that need them, and prefer an external secret store (External Secrets Operator + AWS Secrets Manager / Vault) so secrets never live plaintext in Git.

Evidence: encryption provider config, external secrets configuration, and the RBAC export from section 3 showing scoped secret access.

Building Your Evidence Pack

Passing the audit is half technical, half paperwork. Auditors ask for proof, so collect it into a timestamped folder before the meeting:

mkdir -p audit-evidence/$(date +%F) && cd audit-evidence/$(date +%F)
kubectl get clusterrolebindings,rolebindings -A -o yaml > rbac-bindings.yaml
kubectl get netpol -A -o yaml            > network-policies.yaml
kubectl get ns -L pod-security.kubernetes.io/enforce > pod-security-labels.txt
kubectl get clusterpolicies -o yaml      > kyverno-policies.yaml
kubescape scan framework cis-v1.23-t1.0.1 --format json > cis-scan.json

The strongest evidence isn’t “no privileged pods exist today” — it’s an admission controller (Kyverno or Gatekeeper) proving non-compliant workloads are rejected. Point-in-time cleanliness plus enforced policy is what turns a WARN into a PASS.

Working through all eight areas, mapping each to CIS controls and SOC2 criteria, and writing the remediation YAML from scratch is a lot of work under deadline pressure. The full kit at k8s-security.pro packages this as a 50-point checklist with CIS and SOC2 mappings, 25 production-ready YAML templates, and Helm/Kustomize deployments — so the evidence pack above is mostly kubectl apply and export.

Your Pre-Audit Checklist

Run this the day before the audit:

  • Zero privileged containers; Pod Security restricted enforced on prod namespaces
  • Default-deny NetworkPolicy in every workload namespace (DNS allowed)
  • No workload ServiceAccount bound to cluster-admin
  • No :latest images; admission policy blocks them
  • CPU/memory limits on every container; LimitRange in place
  • Token automount disabled for non-API workloads
  • API server audit logging enabled, 90-day retention
  • Secrets encrypted at rest; secret RBAC scoped narrowly
  • Evidence pack exported with today’s timestamp

Start with the k8s-audit 30-second scan to see which boxes are already ticked, then remediate top-down by risk.

FAQ

What do auditors actually check in a Kubernetes security audit?

The same eight high-risk areas nearly every time: privileged/root containers, missing NetworkPolicies, over-broad RBAC (especially cluster-admin), mutable :latest image tags, missing resource limits, ServiceAccount token automount, audit logging, and secret protection. For SOC2 these map to CC6.1, CC6.6, and CC7.1; for CIS they map to numbered controls in sections 1, 4, and 5.

How do I quickly find my biggest gaps before the audit?

Run an automated first pass. The open-source k8s-audit tool gives a read-only 30-second overview across all eight areas, and kube-bench / kubescape produce CIS-mapped PASS/FAIL/WARN reports. Fix the highest-severity findings — privileged pods, cluster-admin bindings, missing NetworkPolicies — first.

Is a point-in-time clean scan enough to pass?

Usually not. Mature auditors want proof that non-compliant workloads are prevented, not just absent right now. Deploy an admission controller (Kyverno or OPA/Gatekeeper) enforcing your policies and include a rejected-deployment example in your evidence. That converts “clean today” into “continuously enforced.”

Do managed clusters like EKS or GKE pass audits automatically?

No. The managed control plane covers some control-plane CIS controls (API server flags, etcd encryption), but every workload-level control — Pod Security, NetworkPolicies, RBAC, image policy, resource limits — is your responsibility, and that’s where almost all findings land.


Ready to self-check in 30 seconds? Run the free k8s-audit scan, then grab the full 50-point checklist and 25 hardening templates at k8s-security.pro.

Get the Complete 50-Point Security Checklist

5 production-ready templates + audit checklist highlights, free for K8s engineers.

Get Free Kit

Get the Free K8s Security Quick-Start Kit

Get 5 essential templates + audit checklist highlights delivered to your inbox.

No spam. Unsubscribe anytime.

Secure Your Kubernetes Clusters

Get the complete 50-point audit checklist and 20+ production-ready YAML templates.

View Pricing Plans