☁️ Cloud Security July 27, 2026 · 15 min read

Container Security Audit Checklist: 50 Checks That Matter

Pragmatic 50-point container security audit checklist — build, registry, runtime, RBAC, network, detection. What attackers exploit and the fixes.

CS
☁️ Cloud Security
CS

Container security audits get done at one of two depths. The shallow version scans images for CVEs, reports a four-digit count of “vulnerabilities,” and produces a finding list nobody reads. The deep version inspects the actual blast radius of a compromised container — who can the workload assume, what data can it reach, what does the cluster’s RBAC actually permit, and which detection signal would fire if any of it went wrong. The shallow version satisfies compliance; the deep version closes incidents. After running container audits as part of Kubernetes and container security engagements across many environments, a consistent picture emerges: the security-relevant findings concentrate in a few well-known categories, and a focused 50-check audit catches more than 95% of the real risk. This is the field-tested checklist — what to inspect, why it matters, what attackers exploit when each check fails, and how to sequence the fixes.

Why most container audits stop at CVE scanning

CVE scanning produces output. Output produces compliance documentation. Compliance documentation closes audit cycles. The combination has trained the industry to optimise for finding count rather than blast-radius reduction.

What CVE scanning actually tells you

A CVE scan tells you “this package version has a known vulnerability.” It does not tell you whether the vulnerable code path is reachable, whether the container has the privilege to do anything dangerous when the vulnerability is exploited, or whether your detection stack would catch the exploitation if it happened. Three containers with the same CVE list can have radically different real-world risk profiles depending on these three questions.

What container audits actually need to cover

The full picture requires inspecting eight layers, each with its own attack patterns:

  1. Image build pipeline — what gets into the image and how is it verified
  2. Registry and supply chain — who can push, who can pull, what is verified
  3. Runtime configuration — privileges, capabilities, resources, mounts
  4. Cluster security — control plane hardening, etcd, API server
  5. Identity, RBAC, and secrets — who can do what, how secrets are exposed
  6. Network policy — what can talk to what
  7. Observability and detection — would you see an attack
  8. Incident response readiness — could you respond to one

The 50 checks below distribute across these layers in roughly the proportions that matter — runtime config and RBAC dominate, build pipeline and registry follow, network and detection round it out.

How to use this checklist

Each check below has three components: what to verify, what attacker exploits when the check fails, and the specific remediation. The recommended audit sequence:

  1. Phase 1 (Day 1-3): checks marked Critical. Run through these against every cluster in scope. Document findings.
  2. Phase 2 (Week 1-2): checks marked High. Surface the patterns that show up across multiple workloads.
  3. Phase 3 (Week 2-4): checks marked Medium. Use these to build the longer-term hardening backlog.
  4. Phase 4 (Month 2): continuous controls that prevent recurrence.

Score each check as Pass, Fail, or N/A per workload/cluster. Aggregate to a heat map. Fix highest-blast-radius failures first.

Image build pipeline (Checks 1-10)

This is where supply-chain attacks land. Most production breaches involving containers begin with a malicious or vulnerable image entering the registry through a permissive build pipeline.

# Check Severity What attackers exploit when this fails
1 Base images pinned by digest, not tag (@sha256:... not :latest) High Image pull-time substitution; supply-chain compromise via tag re-pointing
2 Base images from a curated approved list, not arbitrary registries High Malicious image upload to public registries; typosquatting
3 Multi-stage builds used to strip build dependencies from final image Medium Larger attack surface; build tools available at runtime
4 No secrets in image layers (verified via layer inspection, not source) Critical Credential extraction from leaked images; old layer secret recovery
5 No interactive credentials in build args High CI log leak; build cache leak
6 Dockerfile uses non-root USER directive High Privilege escalation if container is compromised
7 Image signed via cosign / Notation / Sigstore High Image tampering between registry and cluster
8 SBOM generated and attached to image Medium Inability to respond to disclosed CVEs without manual inventory
9 Image scan integrated into CI with severity gate High Vulnerable images shipped to production
10 Build pipeline runs in isolated environment (not on developer machines) Medium Build-host compromise contaminates production images

The single highest-leverage check in this category is #4 — secrets in image layers. Even when a developer removes a secret in a later layer, the secret remains in the earlier layer and can be extracted by anyone with image pull access. Common offenders: API keys baked into early COPY directives, SSH keys used during build, npm/pip credentials.

Verification command for the most common secret patterns:

docker save image:tag | tar -xO --wildcards '*/layer.tar' | \
  tar -tO 2>/dev/null | grep -aE '(AWS|API_KEY|SECRET|PASSWORD|BEGIN.*PRIVATE)'

For CI integration of image signing and SBOM, cosign + syft is the current de facto stack:

syft my-registry/my-app:1.2.3 -o spdx-json > sbom.json
cosign sign --key cosign.key my-registry/my-app:1.2.3
cosign attest --predicate sbom.json --key cosign.key my-registry/my-app:1.2.3

Registry and supply chain (Checks 11-16)

The registry is the bridge between build and runtime. A compromised registry, or a registry with weak access control, defeats the security model that depends on trusting the images it serves.

# Check Severity What attackers exploit when this fails
11 Registry requires authentication for both push and pull Critical Anonymous image upload; supply-chain injection
12 Push credentials issued per-pipeline, not shared High Credential leak from one pipeline compromises all
13 Image pull policy enforced via admission controller (only signed images) Critical Unsigned or unauthorised images deployed
14 Vulnerability rescanning enabled on new CVE disclosures High Long-running images with newly-disclosed CVEs invisible
15 Image retention policy in place, old images removed Low Forgotten old images with credentials or vulnerabilities
16 Cross-region replication keys rotated and access-logged Medium Replication compromise; stale credential abuse

Check #13 is the most underutilised registry control. Admission controllers like Kyverno or OPA Gatekeeper can enforce “only deploy images signed by our cosign key” — preventing both supply-chain attacks and accidental deployment of unintended images.

# Kyverno policy: require cosign-signed images
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: enforce
  rules:
    - name: verify-signature
      match:
        resources:
          kinds: [Pod]
      verifyImages:
        - imageReferences: ["my-registry/*"]
          attestors:
            - entries:
                - keys:
                    publicKeys: |
                      -----BEGIN PUBLIC KEY-----
                      <cosign public key>
                      -----END PUBLIC KEY-----

Runtime configuration (Checks 17-26)

Runtime configuration determines what a compromised container can actually do. This is the densest category in the audit because the failures here turn small vulnerabilities into large incidents.

# Check Severity What attackers exploit when this fails
17 privileged: false on all production pods Critical Direct host compromise from container escape
18 runAsNonRoot: true enforced via Pod Security Admission High Container processes running as root; easier privilege escalation
19 allowPrivilegeEscalation: false on all production pods High Setuid binary escalation inside container
20 readOnlyRootFilesystem: true where workload supports it Medium Filesystem-based persistence; binary modification
21 Linux capabilities dropped to least required (drop ALL, add specific) High Capability abuse for network/filesystem operations
22 No mount of host paths (hostPath), or restricted to approved list Critical Host filesystem read/write from container
23 No hostNetwork, hostPID, hostIPC on production pods Critical Cluster-wide visibility from a single pod
24 Resource limits (CPU/memory) set on every container Medium DoS; noisy-neighbour; OOM-kill cascade
25 Liveness and readiness probes do not expose sensitive endpoints Low Information disclosure via probe responses
26 Pod Security Admission set to restricted or equivalent enforcement High Workloads can opt out of all the above with no central enforcement

Pod Security Admission (PSA) is the central enforcer for most of this category. The restricted profile enforces #17, #18, #19, #20, #21, parts of #22, and #23 — but only if the namespace label is set. Audit every namespace for the PSA label:

kubectl get namespaces -L pod-security.kubernetes.io/enforce,pod-security.kubernetes.io/audit,pod-security.kubernetes.io/warn

Any production namespace without enforce=restricted (or baseline with documented justification) fails check #26.

For deeper context on the hardening config that backs these checks, see our Kubernetes security hardening for containers post.

Cluster security (Checks 27-36)

The cluster control plane is the highest-blast-radius surface. A compromise of the API server, etcd, or kubelet typically means cluster takeover.

# Check Severity What attackers exploit when this fails
27 API server bound to private network or restricted CIDR Critical Internet-exposed API server; brute-force kubeconfig recovery
28 API server audit logging enabled with appropriate policy High Lack of forensic evidence; missed detection signal
29 etcd encryption at rest enabled with KMS-backed key Critical Secret extraction from etcd snapshots
30 etcd access restricted to control plane nodes only Critical Direct etcd read/write bypasses API authorisation
31 Kubelet authentication/authorisation set to webhook mode Critical Anonymous kubelet API access; pod exec from outside
32 Kubelet port not exposed beyond node High Lateral movement via kubelet API
33 Control plane TLS certificates rotated automatically Medium Stale certificates; manual rotation drift
34 Anonymous auth disabled on API server High Probing API surface without credentials
35 API server TLS uses strong cipher suite, no TLS < 1.2 Medium Downgrade attacks; protocol-level weaknesses
36 CIS Kubernetes Benchmark compliance scan run and remediated Medium Systematic miss of well-known weak settings

For managed Kubernetes (EKS, GKE, AKS), checks #29, #30, #31, #33, #34 are partially handled by the cloud provider — but only partially. Each provider has knobs the customer owns. EKS, for example, requires the customer to explicitly enable secrets encryption with KMS; the default cluster has no envelope encryption.

CIS Benchmark scanning is fastest via kube-bench:

kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs job/kube-bench

Identity, RBAC, and secrets (Checks 37-44)

The container’s identity surface — service accounts, RBAC roles, secrets — is what determines blast radius. Most container compromises become cluster compromises through identity misconfiguration. The patterns mirror the broader cloud identity problems documented in our AWS IAM audit walkthrough.

# Check Severity What attackers exploit when this fails
37 Default service account has no RBAC permissions Critical Pods using default SA have unintended cluster access
38 Service account tokens not automounted unless explicitly needed High Pod-to-API-server access from compromised workloads
39 ClusterRoleBindings to cluster-admin audited and minimised Critical Service account compromise = cluster admin
40 Service account-to-IAM mapping (IRSA/Workload Identity) scoped to least privilege Critical Pod-to-cloud-account lateral movement
41 Secrets stored in dedicated secret store (Vault/Secrets Manager), not as plain k8s secrets High Plain k8s secrets are base64-encoded, not encrypted, easily exfiltrated
42 Secrets injected via projected volumes or external secrets operator High Secrets in environment variables are visible in process listings and crash dumps
43 Image pull secrets per-namespace, not cluster-wide Medium Compromise of one workload exposes pull credentials for all images
44 Service account audit log review quarterly Medium Stale or unused service accounts retain dangerous permissions

Check #40 is the highest-leverage cross-system control. A pod’s service account often maps to a cloud IAM role (via IRSA on EKS, Workload Identity on GKE, Pod Identity on AKS). Overly broad role permissions turn a single pod compromise into a cloud-account compromise.

A common production pattern showing the failure mode:

# Bad: pod's IRSA role has s3:* on *
apiVersion: v1
kind: ServiceAccount
metadata:
  name: analytics-pod
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/analytics-pod-role
# analytics-pod-role policy:
# Action: s3:*
# Resource: "*"

The correct version scopes both the action and the resource:

# Good: scoped to specific S3 bucket and specific actions
# analytics-pod-role policy:
# Action: ["s3:GetObject", "s3:PutObject"]
# Resource: ["arn:aws:s3:::analytics-data-bucket/*"]

Network policy and segmentation (Checks 45-49)

Network policy is the layer that limits lateral movement within the cluster. Without it, a compromised pod can reach every other pod, every internal service, and often the cloud metadata endpoint.

# Check Severity What attackers exploit when this fails
45 Default-deny network policy in every production namespace Critical Unrestricted lateral movement on cluster compromise
46 Egress restricted (no arbitrary outbound from pods) High Data exfiltration; C2 callbacks; cryptocurrency mining
47 Cloud metadata endpoint blocked at network policy level (169.254.169.254) Critical IMDS abuse for credential theft (especially on EKS without IMDSv2 only)
48 Service mesh mTLS enabled if mesh is deployed Medium Cleartext inter-service traffic; spoofing
49 Network policies tested via automated policy verification tool Medium Policy drift; rules that don’t actually do what they claim

Check #47 is a top-3 finding by frequency in production audits. The instance metadata endpoint accessible from pods, combined with IMDSv1 enabled on the node, allows credential theft via SSRF in any pod-hosted application — a single XSS payload becomes cloud credential theft. Block at the network policy level:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-imds
  namespace: production
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except: [169.254.169.254/32]

Combined with enforcing IMDSv2-only at the node level, this closes one of the most common cloud-to-container escalation paths.

Observability and detection (Check 50)

The single most underutilised check, treated as one because the detection coverage need is unitary — either you’d see an attack on a container or you wouldn’t.

# Check Severity What attackers exploit when this fails
50 Runtime detection deployed (Falco, Tetragon, or equivalent) with alerts routed to SOC High Container compromise proceeds without detection; long dwell time

Runtime detection at the container layer is meaningfully different from log-based detection. Falco watches kernel-level syscalls; Tetragon watches eBPF-level events. They catch behaviours like:

  • Shell spawned inside a container that should never have an interactive shell
  • Outbound connection to an unexpected IP from a workload pod
  • Process executing from a writable filesystem location
  • Sensitive file read (e.g., /etc/shadow from inside container)
  • Privileged operation by a container that has no business doing it

Without runtime detection, the dwell time between container compromise and discovery is typically measured in weeks. With it, the same compromise is caught in minutes.

For organisations layering on broader detection, the runtime telemetry plugs into managed SOC monitoring and continuous vulnerability management. The AWS-native detection layer documented in our Security Hub vs GuardDuty vs Inspector comparison covers complementary categories.

What to fix first — severity matrix

After running the 50 checks, the question is sequencing. The matrix that works in practice:

Finding category Fix priority Why
Privileged containers in production (#17) Day 1 Direct host compromise risk
Host path mounts (#22) and host network (#23) Day 1 Cluster-wide blast radius
Cluster admin RBAC over-grants (#39) Day 1-3 Service account compromise = cluster takeover
IRSA / Workload Identity over-permissioned (#40) Day 1-3 Pod-to-cloud lateral movement
Metadata endpoint not blocked (#47) Day 1-3 SSRF-to-credential-theft pipeline
Default-deny network policy missing (#45) Week 1 Lateral movement enabler
Pod Security Admission not enforced (#26) Week 1-2 Central enforcer for half the runtime checks
Image signing not enforced (#13) Week 2-3 Supply-chain attack surface
Runtime detection not deployed (#50) Week 2-4 Closes the dwell-time gap
Secrets stored as plain k8s secrets (#41) Week 3-4 Easier than it sounds with External Secrets Operator
CIS Benchmark remediations (#36) Month 2 Long tail; lower individual blast radius
SBOM generation and retention policy (#8, #15) Month 2-3 Supports incident response when CVE disclosures land

The pattern across mature container security programmes: the day-1 fixes drop risk by 50-60%; the week-1+ fixes drop it another 20-30%; the month-2+ fixes pick up the long tail. Front-load the high-blast-radius work.

Continuous controls after the audit

The 50 checks produce a point-in-time view. The view ages out fast — new workloads, new images, new RBAC bindings land every day. Sustaining the audit’s improvements requires continuous controls.

The minimum continuous controls

  • Admission control via Kyverno or OPA Gatekeeper enforcing the most critical of the 50 checks (no privileged, no host mounts, signed images, scoped service accounts) as Deny rules
  • Pod Security Admission set to restricted in production namespaces, baseline elsewhere
  • Image scan + signature verification gate in the CI pipeline, failing the build on critical findings
  • Runtime detection (Falco/Tetragon) with alerts routed to a SIEM and triaged
  • Quarterly RBAC review — list every cluster-admin and high-permission RoleBinding, justify or remove
  • Quarterly cluster CIS benchmark scan with delta tracking against last quarter
  • Periodic image inventory cleanup — registry retention, unused image removal

For broader context on how these controls fit into a complete cloud security program, the cloud security assessment service walks through the operational sequencing. Layered together, the controls turn the audit from an annual exercise into an ongoing programme that keeps the risk reduction durable.

The argument was never whether containers can be secured. They can be. The argument is whether the secured state survives contact with production engineering velocity — and the difference between programmes that hold the line and programmes that drift is whether the continuous controls listed above are actually running, not just documented. The 50 checks are the snapshot; the continuous controls are the system. Most teams that audit once and then drift end up with the same audit findings the next year, just with different workload names attached.

#Containers #Kubernetes #Cloud Security #Audit #DevSecOps

Related articles

Need expert help with Cloud Security?

Our certified security team is ready to assess your environment and recommend the right solutions.

Book a Free Consultation