Harden EKS with Pod Security Policies

Harden EKS with Pod Security Policies: configure and enforce security controls for your Kubernetes pods. Hands-on tutorial with troubleshooting and next steps.

Focus: harden eks with pod security policies

Sponsored

Your EKS cluster is running, your workloads are deployed, but a single pod with privileged: true or a hostPath mount could be the open door an attacker needs to pivot from your application to your node, and from your node to your AWS account. In this lesson, you'll learn how to harden EKS with pod security policies — the admission control layer that keeps risky pods out of your cluster before they ever run. We'll move from theory to practice: you'll configure Pod Security Standards, apply them with a live exercise, and leave with the exact commands to bake security into your deployment pipeline.

The Problem: Why Default Pod Permissions Are Dangerous

Kubernetes pod specifications are powerful — and with that power comes risk. By default, your EKS control plane doesn't restrict what a pod can request:

  • Privileged containers can access all devices on the host node.
  • hostPath volumes let a pod read or write any path on the node's filesystem — including /etc/kubernetes or Docker sockets.
  • hostNetwork and hostPID expose node-level networking and process visibility.
  • Container users often run as root, and capabilities like CAP_SYS_ADMIN can lead to container escapes.

An attacker who compromises a single pod could use these misconfigurations to break out of the container boundary and take over the node — then pivot to your VPC, IAM roles, and beyond. This is why AWS EKS now enforces Pod Security Standards (PSS) and why the legacy PodSecurityPolicy (PSP) is deprecated in Kubernetes v1.21 and removed in v1.25.

Pain point: You need a policy that blocks these dangerous configurations before a pod is created — not a post-mortem audit after a security incident.

Core Concept / Mental Model

Think of pod security as a door security checkpoint for your cluster. Every pod that enters through the API server must pass through the checkpoint. The checkpoint checks the pod's "ID and luggage" — its security context, volumes, capabilities, and privileges — against a set of rules. If the pod doesn't comply, it's turned away.

That checkpoint is an admission controller. Two key mechanisms exist:

  • PodSecurity Admission (PSA) — the modern, built-in admission controller that applies Pod Security Standards (PSS) — a set of three policy levels: privileged, baseline, and restricted.
  • PodSecurityPolicy (PSP) — the old, cluster-wide policy resource that is now deprecated. It was complex and error-prone, which is why Kubernetes replaced it with the simpler PSA.

For EKS 1.25+, you should use PodSecurity Admission exclusively. The three levels are:

  • privileged: No restrictions — for system-level workloads (e.g., kube-proxy, CNI).
  • baseline: Prevents known privilege escalations — blocks privileged: true, host namespaces, and some volume types.
  • restricted: Follows pod hardening best practices — enforces non-root user, drops all capabilities, readonly root filesystem, and more.

You apply these standards to a namespace using labels. The control plane evaluates incoming pods against the selected level and enforces the mode you choose: enforce, audit, or warn.

How It Works Step by Step

  1. Choose a Standard for Each Namespace — Decide whether your workloads need privileged, baseline, or restricted. Default to restricted; only use privileged for system components that truly need it.
  2. Label the Namespace — Apply the standard and enforcement mode as labels on the namespace object.
  3. Admission Controller Evaluates — When a pod is created (via kubectl apply or a Helm chart), the API server checks its spec against the standard.
  4. Policy Mode Determines the Outcomeenforce blocks the pod with an error, audit records the violation in audit logs, and warn returns a warning to the user but allows the pod.
  5. Respond to Violations — For audit mode, monitor logs; for warn, fix manifests; for enforce, adjust the workload or the namespace label.

Pro tip: Start with audit mode for two weeks to see what your cluster would block, then switch to enforce. This avoids breaking running workloads.

Hands-On Walkthrough

Let's harden an EKS cluster using Pod Security Admission. You'll need a working kubectl connected to your EKS cluster.

1. Create Example Namespaces

kubectl create namespace secured
kubectl create namespace system-components

2. Apply Pod Security Standards via Labels

Label the secured namespace with restricted and enforce mode. Label system-components with privileged and enforce mode.

kubectl label namespace secured pod-security.kubernetes.io/enforce=restricted
kubectl label namespace secured pod-security.kubernetes.io/audit=restricted
kubectl label namespace secured pod-security.kubernetes.io/warn=restricted

kubectl label namespace system-components pod-security.kubernetes.io/enforce=privileged

3. Test with a Compliant Pod

Create a pod configured to meet the restricted standard:

# restricted-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
  namespace: secured
spec:
  containers:
  - name: app
    image: nginx:1.25
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]
      seccompProfile:
        type: RuntimeDefault

Apply it:

kubectl apply -f restricted-pod.yaml

Expected output: pod/secure-pod created

4. Test with a Violating Pod

Now try to run a privileged pod in the same namespace:

kubectl -n secured run bad-pod --image=nginx --privileged

Expected output:

Error from server (Forbidden): admission webhook "pod-security.admission.k8s.io" denied the request: pods "bad-pod" is forbidden: violates PodSecurity "restricted:latest": privileged (container "bad-pod" must not set securityContext.privileged=true)

The pod is blocked before it ever runs. Now check the enforcement is visible:

kubectl -n secured get events --field-selector type=Warning | grep "PodSecurity"

Compare Options / When to Choose What

Option Description Best For Pros Cons
PodSecurity Admission (PSA) Built-in admission controller with three standards Most EKS 1.25+ workloads Simple, clear, maintained by Kubernetes Only three granularity levels
PodSecurityPolicy (PSP) Legacy policy resource with custom rules None — avoid in new clusters Highly customizable Deprecated, complex, misleading security boundary
Kyverno Policy engine with custom rules Organizations needing fine-grained control Customizable, works with PSS Requires extra installation and maintenance
OPA Gatekeeper General-purpose policy engine using Rego Complex regulatory requirements Extremely flexible Steeper learning curve

Recommendation: For most teams, Pod Security Admission with restricted is the right default. It's free, built-in, and covers the 80% of security risks. Reach for Kyverno or Gatekeeper only when you need custom policies beyond the three standards.

Pro tip: Use the kubectl plugin kubectl-convert to update deprecated PSPs to PSA labels — or use the pod-security-admission tool to scan existing clusters.

Troubleshooting & Edge Cases

Pods are being blocked unexpectedly — Check which mode you're using: enforce blocks, audit only logs, warn only warns. Use kubectl get ns <namespace> --show-labels to verify labels. Common mistake: forgetting the warn label means users don't see why a future pod would be blocked.

Workload needs privileged access — For system components like the AWS Load Balancer Controller or cluster autoscaler, create a separate namespace labeled privileged and deploy them there. Never downgrade the restricted namespace label just to make a single pod pass.

Can't create pods after upgrading EKS — AWS automatically enables PSA in new clusters, but existing clusters may need you to add labels manually. Use kubectl label ns --all pod-security.kubernetes.io/enforce=baseline to start, then tighten later.

Helm charts fail — Helm templates often include privileged: true in development. Check the chart's values and set securityContext parameters to match the restricted standard, or deploy the chart in a dedicated baseline namespace temporarily.

kubectl shows no warning — Verify you're connected to the correct cluster (kubectl config current-context). The warning only appears for warn label, not audit.

What You Learned & What's Next

You've learned how to harden EKS with pod security policies — specifically, using the modern Pod Security Admission to enforce Pod Security Standards: privileged, baseline, and restricted. You understand the mental model of admission control as a checkpoint, and you've completed a hands-on exercise that proves how restricted blocks privileged pods while allowing compliant ones.

Key takeaways: - Pod Security Standards are the replacement for the deprecated PodSecurityPolicy. - Use namespace labels to apply standards with enforce, audit, or warn modes. - Start with audit mode, then switch to enforce. - Use privileged namespaces only for essential system components. - Check EKS version — PSA requires Kubernetes 1.25+.

Next lesson: Now that your pods are restricted, you'll learn to secure the control plane with AWS IAM authentication and Role-Based Access Control (RBAC). That will complete the perimeter around your EKS workloads.

Go ahead and try the practice exercise below — you'll cement these concepts in minutes.

Practice recap

Quick exercise: create a new namespace test-restricted and label it with pod-security.kubernetes.io/enforce=restricted. Try to run a pod with --privileged and observe the error. Then fix the pod spec to meet the standard and apply it successfully. You'll see the admission controller in action and get comfortable with the workflow.

Common mistakes

  • Labeling a namespace with enforce but forgetting warn and audit — you get no feedback when a developer tries to create a violating pod and sees a cryptic error.
  • Assuming PodSecurityPolicy is still a valid solution — it's removed in EKS 1.25+, so spend no time on it.
  • Applying a standard to all namespaces with a single command and breaking system components like kube-proxy — always exclude system namespaces or label them privileged.
  • Using restricted for a workload that legitimately needs host access without creating a dedicated privileged namespace — this leads to outages.

Variations

  1. Kyverno — a Kubernetes-native policy engine that can enforce the same Pod Security Standards with more granular controls and mutation capabilities.
  2. OPA Gatekeeper — a general-purpose policy manager that uses the Rego language for complex, custom admission policies.
  3. Manual manifest reviews — a low-tech fallback where you audit YAML files in CI/CD, but it doesn't catch runtime changes.

Real-world use cases

  • A fintech company enforces the restricted standard on all payment service namespaces to prevent container escapes and data breaches.
  • A SaaS platform uses audit mode for one release cycle to identify legacy workloads that need new security contexts before rolling out enforce.
  • A DevOps team isolates system components like the CloudWatch agent in a privileged namespace while keeping all customer workloads under restricted.

Key takeaways

  • Pod Security Admission is the modern, built-in way to harden EKS against privileged pods — up to three levels: privileged, baseline, restricted.
  • Namespace labels control the standard and mode: enforce blocks, audit logs, and warn warns — use them together.
  • Always start with audit mode to discover violations without breaking running workloads, then switch to enforce.
  • Create dedicated privileged namespaces only for essential system components; default everything else to restricted.
  • Legacy PodSecurityPolicy is deprecated — migrate or replace with Pod Security Admission.
  • Verify your cluster is EKS 1.25+ to use Pod Security Admission; older versions require alternative policy tools.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.