EKS Security Audit Checklist for Production Clusters

August 11, 2026 Updated AWS, CyberSecurity
EKS Security Audit Checklist: 20 Checks to Verify

I've reviewed EKS environments for SaaS and fintech companies ranging from small clusters running a handful of services to multi-environment platforms hosting dozens of applications. The size of the cluster turns out to matter very little. The findings that matter show up in the same places every time: at the boundaries between AWS IAM, Kubernetes RBAC, the network, and the CI/CD pipeline that decides what runs in production.

This checklist is organised around those boundaries. Every item comes with a way to verify it - a command or a query - because an audit built on "the team says it's configured" is not an audit.

Short answer: the three findings I see most often are over-permissive IAM and RBAC, network policies that don't actually restrict anything, and missing workload and supply-chain controls. Having IRSA or EKS Pod Identity configured does not mean workloads follow least privilege - you have to test the effective permissions from inside the pod.

The three findings I see most often

1. IAM and RBAC that grant more than the workload needs

Workloads with far more AWS permissions than they use, broad Kubernetes roles, and service accounts that can reach resources outside their intended scope. IRSA and EKS Pod Identity are the right mechanisms, but they only enforce least privilege if the IAM policies and the role trust relationships behind them are scoped correctly. Teams often check the first and never look at the second.

2. Network policies that don't restrict traffic

The common assumption is that anything inside a private VPC is isolated enough. It isn't. A private cluster is not a segmented cluster: without enforced NetworkPolicies, every pod can talk to every other pod and every internal service the security groups allow. Security groups, Kubernetes network policies and access to internal endpoints (databases, caches, internal APIs, the instance metadata service) have to be evaluated together, because each one assumes the others are doing the job.

3. Gaps in workload and supply-chain security

Privileged containers, unnecessary Linux capabilities, containers running as root, image references pinned to a mutable tag, and no control over what is allowed to run in production. None of these is a dramatic vulnerability on its own. They accumulate because delivery speed wins every individual trade-off, and nothing enforces a baseline.

A finding that surprised a client: the role was broader than the workload

One review turned up a workload that was meant to access a single AWS service but could, in practice, assume a much broader identity than the application needed. There was no bug in the application. The exposure came from the combination of three things: the IAM permissions on the role, the role's trust policy, and how workload identity was wired up in the cluster.

This class of problem is worth understanding because it passes a casual review - IRSA is "configured", the pod has a dedicated service account, everything looks right. The usual shapes are:

  • A trust policy that matches too many service accounts. A StringLike condition with a wildcard on the sub claim, or a condition on aud only, lets other service accounts in the cluster assume the role.
  • A permissions policy written for convenience. s3:* on * when the workload reads one bucket prefix.
  • A fallback to the node role. If pods can reach the instance metadata service, any pod on the node can pick up the node's IAM role credentials - whatever the service account says.

Here is what a correctly scoped IRSA trust policy looks like - exact match on one namespace and one service account:

{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::111122223333:oidc-provider/oidc.eks.eu-central-1.amazonaws.com/id/EXAMPLE"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "oidc.eks.eu-central-1.amazonaws.com/id/EXAMPLE:sub": "system:serviceaccount:payments:invoice-exporter",
      "oidc.eks.eu-central-1.amazonaws.com/id/EXAMPLE:aud": "sts.amazonaws.com"
    }
  }
}

The fix had three parts: restrict the trust relationship to the intended service account, cut the permissions policy down to the specific actions and resources the application uses, and then validate the effective access from inside the workload rather than reading the policies and assuming:

# Which identity does the pod actually get?
kubectl exec -n payments deploy/invoice-exporter -- aws sts get-caller-identity

# What can that role do? Test the actions you expect to be denied, not just the allowed ones.
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::111122223333:role/invoice-exporter \
  --action-names s3:GetObject s3:PutObject s3:DeleteBucket iam:PassRole

# Can a pod on this node reach the node role through IMDS? (should time out)
kubectl run imds-test --rm -it --image=curlimages/curl --restart=Never -- \
  curl -s -m 3 http://169.254.169.254/latest/meta-data/iam/security-credentials/

The lesson I now apply to every review: the existence of IRSA or Pod Identity tells you nothing. The effective permissions and the boundaries between workloads are what you audit.

The EKS security audit checklist

Work through it in this order. Identity first, because a gap there undermines everything after it.

Identity and access

  1. Every workload that calls AWS uses IRSA or Pod Identity, never the node role or static keys. Verify: search manifests and Secrets for AWS_ACCESS_KEY_ID; list service accounts with the eks.amazonaws.com/role-arn annotation and the cluster's pod identity associations.
  2. Each role's trust policy matches exactly one namespace and service account. Verify: read the Condition block of every IRSA role - StringEquals on sub, no wildcards.
  3. Permissions are scoped to actions and resources the workload uses. Verify: IAM Access Analyzer's unused-access findings or last-accessed data, then simulate-principal-policy as above.
  4. Pods cannot reach the node's instance metadata. Verify: IMDSv2 required with a hop limit of 1 on the node launch template, then the IMDS test above from an ordinary pod. AWS covers this in the EKS best practices guide.
  5. Human access goes through EKS access entries and SSO, not a hand-edited aws-auth ConfigMap. Verify: aws eks list-access-entries --cluster-name prod; anything still mapped in aws-auth is a migration item. See access entries.
  6. Nobody has cluster-admin who doesn't need it. Verify:
    kubectl get clusterrolebindings -o json | jq -r '
      .items[] | select(.roleRef.name=="cluster-admin")
      | .metadata.name + ": " + ([.subjects[]? | .kind + "/" + .name] | join(", "))'
    
    kubectl auth can-i --list --as=system:serviceaccount:payments:invoice-exporter

Network

  1. Every application namespace has a default-deny policy, and it is enforced. A NetworkPolicy object does nothing unless the CNI enforces it - with the VPC CNI that means enabling network policy support, otherwise Calico or Cilium.
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny
      namespace: payments
    spec:
      podSelector: {}
      policyTypes: [Ingress, Egress]
  2. Policies are tested, not just applied. Verify: from a throwaway pod in one namespace, try to reach a service in another that should be blocked:
    kubectl run nettest -n frontend --rm -it --image=nicolaka/netshoot --restart=Never -- \
      curl -s -m 3 http://ledger.payments.svc.cluster.local:8080/health
    If that returns a response, the segmentation exists on paper only.
  3. Databases and caches accept traffic only from the workloads that need them. Verify: security group rules on RDS and ElastiCache reference specific security groups (or security groups for pods), not the whole VPC CIDR.
  4. The cluster API endpoint is private, or public with a narrow CIDR allowlist. Verify: aws eks describe-cluster --name prod --query cluster.resourcesVpcConfig.

Workloads and supply chain

  1. Pod Security Admission enforces restricted (or at least baseline) on application namespaces. Verify: kubectl get ns -L pod-security.kubernetes.io/enforce. The levels are defined in the Pod Security Standards.
  2. No privileged containers, no added capabilities, no root without a documented reason. Verify:
    kubectl get pods -A -o json | jq -r '
      .items[] | (.metadata.namespace + "/" + .metadata.name) as $p
      | (.spec.securityContext.runAsNonRoot // false) as $podNonRoot
      | .spec.containers[]
      | select(.securityContext.privileged == true
               or ((.securityContext.capabilities.add // []) | length > 0)
               or ((.securityContext.runAsNonRoot // $podNonRoot) != true))
      | $p + " " + .name'
  3. Production images are referenced by digest, or by immutable tags in ECR. A :latest or :v2 tag can point at different code tomorrow. Verify: ECR tag immutability on production repositories, and no :latest in production manifests.
  4. Images are scanned, and something blocks critical findings from reaching production. Scanning without enforcement is a report nobody reads. Verify: ECR enhanced scanning or Trivy in CI with a failing exit code, plus an admission policy (Kyverno or Gatekeeper) restricting which registries can run in production.
  5. Secrets live in AWS Secrets Manager or Parameter Store and sync in (External Secrets Operator or the Secrets Store CSI driver), rather than as base64 Kubernetes Secrets committed to Git.

Detection and hygiene

  1. Control plane logging is on for api, audit and authenticator, with retention long enough to investigate an incident months later. Verify: aws eks describe-cluster --name prod --query cluster.logging. See control plane logs.
  2. GuardDuty EKS protection is enabled for audit-log and runtime threat detection.
  3. Nodes run current AMIs on a schedule, and the cluster version is inside standard support. Bottlerocket is worth considering if node patching has been inconsistent.
  4. Secrets encryption uses a customer-managed KMS key where auditors need you to control and rotate the key.
  5. A CIS benchmark scan runs on a schedule, with findings tracked to owners. kube-bench runs the CIS Amazon EKS Benchmark; a one-off report filed away changes nothing.

How the checklist maps to SOC 2 controls

If the audit is driven by SOC 2, hand your auditor this mapping instead of explaining Kubernetes from scratch. For the evidence auditors ask for, see EKS SOC 2 readiness: what auditors actually check.

Checklist areaSOC 2 criteriaWhat it demonstrates
IRSA / Pod Identity, trust policies, RBAC (1-6)CC6.1, CC6.3 - logical accessAccess is limited to authorised identities, scoped per workload
Network policies, security groups, API endpoint (7-10)CC6.6 - boundary protectionWorkloads cannot reach what they have no reason to reach
Pod security, image controls (11-14)CC8.1 - change management; CC7.1 - vulnerability managementOnly reviewed, scanned artefacts run in production
Secrets, KMS (15, 19)CC6.1, CC6.7 - data protectionCredentials and sensitive data are protected at rest
Logging, GuardDuty (16-17)CC7.2 - monitoringThere is an evidence trail for detection and investigation
Patching, CIS scans (18, 20)CC7.1 - vulnerability managementKnown weaknesses are found and fixed on a schedule

Mistakes that make a good configuration fail the audit

  • Treating "IRSA is enabled" as the finding. It is the start of the check, not the result. Audit trust policies and effective permissions.
  • Trusting a private cluster to be segmented. Private endpoints reduce exposure from outside; they do nothing about lateral movement inside.
  • Writing NetworkPolicies on a CNI that doesn't enforce them. The objects apply cleanly and block nothing.
  • Leaving temporary access in place. The cluster-admin binding created for a migration is still there a year later.
  • Enabling audit logging after the incident. Logs you didn't collect cannot be produced for an investigation or an auditor.
  • Running a CIS scan once. Findings without owners and deadlines don't change your posture.

FAQ

How often should an EKS cluster be audited?

A full review at least once a year and after major changes - a new compliance requirement, a large customer's security questionnaire, a platform migration. In between, automate the checks above (CIS scans, IAM unused-access findings, admission policies) so drift is caught continuously.

Is an automated CIS scan enough?

No. Scanners catch configuration drift, but they can't tell you whether a role's trust policy is broader than intended, or whether a network policy actually blocks traffic. Those need someone to test effective access, as in the IRSA example above.

IRSA or EKS Pod Identity?

Both give pods their own AWS identity. Pod Identity is simpler to operate - no per-cluster OIDC provider, and associations are managed through the EKS API - so it's the better default for new workloads. Either way, the audit question is the same: what can this pod actually do?

Do we need a policy engine like Kyverno or Gatekeeper?

Pod Security Admission covers the container-hardening baseline for free. Add Kyverno or Gatekeeper when you need organisation-specific rules - allowed registries, required labels, digest-pinned images.

Want a second pair of eyes on your cluster?

This checklist is the core of the EKS review in my AWS architecture review and Kubernetes and EKS consulting. The output is a prioritised list of findings, each with the command that proves it and the fix.

Need help with this?

I offer hands-on consulting on exactly this — kubernetes & aws eks consulting .

☁️ AWS Cloud That Saves and Scales

Helping SaaS teams cut costs, speed up releases, and scale securely with DevOps done right

Uncover Bottlenecks & Savings - Free 30-Min Review

Not ready for a call? Get the free AWS Survival Guide instead.

Related Service
Kubernetes & AWS EKS Consulting
Get Kubernetes working the way it was supposed to - without your team spending months figuring it out.