Kubernetes Pod Security in Production: PSA, Kyverno, and OPA Gatekeeper Compared

Manish Garg
Manish Garg Associate of (ISC)² · RingSafe
May 22, 2026
9 min read

Introduction

Kubernetes Pod Security in 2026 is dominated by three policy engines — Pod Security Admission (PSA, the built-in successor to PodSecurityPolicy), Kyverno, and OPA Gatekeeper. They overlap in scope but diverge in expressive power, operational model, and ecosystem maturity. Choosing among them — or running them together — defines the security posture of every workload on the cluster.

This guide is a comparative analysis for platform engineers and security architects responsible for the policy layer of a production Kubernetes platform. We cover what each engine does, how they differ, when to use which, the migration story from deprecated PSP, and a recommended composition pattern for enterprises that need both the simplicity of PSA and the expressiveness of Kyverno.

Background: From PSP to PSA

PodSecurityPolicy (PSP) was Kubernetes’ original cluster-level admission control for pod security. It was deprecated in v1.21 (April 2021) and removed in v1.25 (August 2022). The replacement strategy split the role into two parts: a built-in admission controller (PSA) for the common cases, and pluggable policy engines (Kyverno, OPA Gatekeeper) for everything else.

PSP was removed because it had three structural problems: it was a cluster-singleton resource that could not be namespaced easily, its policy model (allow/deny on capabilities) did not compose well, and it had no obvious extension point for non-pod resources. The replacement model addresses all three.

Theory: Pod Security Admission

PSA is a Kubernetes-native admission controller that applies one of three pre-defined Pod Security Standards to a namespace:

  • privileged — permissive; allows anything. Used for system namespaces.
  • baseline — blocks the most egregious misconfigurations: host network, host PID, privileged containers, hostPath, dangerous capabilities. Still permissive enough for most legacy apps to run.
  • restricted — the target posture: runAsNonRoot, runAsUser>0, no privilege escalation, seccomp default, capabilities all dropped.

Each profile can be applied at three enforcement levels: enforce (reject violating pods), audit (log violations), warn (return a warning to the user).

Configuration is via namespace labels:

apiVersion: v1
kind: Namespace
metadata:
  name: payments
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Strengths of PSA: built into Kubernetes (no extra controller to install), predictable, well-documented, the standard fallback when more sophisticated policy is unavailable.

Limitations: only three profiles; no policy composition; no resource types other than pods; no mutation; no parameterisation. Pod-escape classes that abuse capabilities the baseline profile still allows (CAP_SYS_PTRACE, CAP_NET_ADMIN under certain conditions) are a case in point — baseline does not block them, and PSA gives no way to add the missing check without escalating the entire namespace to restricted.

Theory: Kyverno

Kyverno is a CNCF-graduated policy engine designed specifically for Kubernetes. Policies are YAML, declarative, validate-or-mutate-or-generate-or-cleanup, namespaced or cluster-scoped, with native support for any Kubernetes resource type.

Kyverno’s policy philosophy: stay close to Kubernetes idioms. Policies look like the resources they govern, with selectors that match the standard Kubernetes label and annotation patterns.

Three categories of Kyverno policy:

  • Validate — pattern-match the resource; reject if it does not conform.
  • Mutate — modify the resource before admission (add default labels, inject sidecars, set resource limits).
  • Generate — create related resources (a NetworkPolicy when a namespace is created, a default ResourceQuota).

Kyverno also supports image verification (Cosign signature checks), policy reports, exception management, and a separate “cleanup” policy type for resource lifecycle.

Theory: OPA Gatekeeper

OPA Gatekeeper is the Kubernetes-specific integration of the Open Policy Agent project. Policies are written in Rego, a declarative policy language with set-theoretic semantics.

The Gatekeeper architecture has two parts:

  • ConstraintTemplates — define a parameterised policy in Rego.
  • Constraints — instantiate a ConstraintTemplate with specific parameters and scope.

This separation lets a platform team author ConstraintTemplates once (e.g., “containers must come from allowed registries”) and application teams instantiate Constraints with their own parameters (“our allowed registries are ecr.our-org/* and gcr.io/our-project/*“).

Gatekeeper’s strengths: Rego is more expressive than Kyverno’s pattern-matching for complex policies; OPA has reach beyond Kubernetes (Envoy, Terraform, CI/CD); the ConstraintTemplate model encourages reusable policy libraries.

Gatekeeper’s friction: Rego has a learning curve; debugging Rego is harder than debugging Kyverno YAML; policy violations report cryptic messages without effort to template good error text.

Technical Deep Dive: Kyverno vs Gatekeeper for a Specific Policy

Compare implementations of “all containers must drop ALL capabilities and explicitly add only what they need.”

Kyverno:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-drop-all
spec:
  validationFailureAction: Enforce
  rules:
  - name: must-drop-all
    match:
      any:
      - resources:
          kinds: [Pod]
    validate:
      message: "All containers must drop ALL capabilities"
      pattern:
        spec:
          containers:
          - securityContext:
              capabilities:
                drop: ["ALL"]

Gatekeeper:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8spsprequiredropall
spec:
  crd:
    spec:
      names:
        kind: K8sPSPRequireDropAll
  targets:
  - target: admission.k8s.gatekeeper.sh
    rego: |
      package k8spsprequiredropall

      violation[{"msg": msg}] {
        c := input.review.object.spec.containers[_]
        not has_drop_all(c)
        msg := sprintf("Container %v must drop ALL capabilities", [c.name])
      }

      has_drop_all(c) {
        c.securityContext.capabilities.drop[_] == "ALL"
      }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPRequireDropAll
metadata:
  name: require-drop-all
spec:
  match:
    kinds:
    - apiGroups: [""]
      kinds: [Pod]

Kyverno’s version is shorter and more readable. Gatekeeper’s version is more expressive — it could trivially be extended to per-container exception logic, complex parent-resource lookups, or cross-resource correlations that would be awkward in Kyverno.

Technical Deep Dive: Performance and Failure Modes

Policy engines sit on the critical path of every admission request. Their latency and availability directly affect cluster usability.

PSA: in-process with kube-apiserver, sub-millisecond, no failure mode beyond apiserver failure itself.

Kyverno: separate controller, 1-10 ms per evaluation in typical configurations. Failure mode: if the Kyverno webhook is unreachable, admission either fails closed (rejecting everything) or fails open (allowing everything), depending on failurePolicy. Fail-closed is the safer default but can cause cluster-wide deploy outages if Kyverno itself crashes.

Gatekeeper: similar latency profile. Same failurePolicy considerations.

Mitigation patterns:

  • Exempt critical system namespaces (kube-system, ingress-controllers, the policy engine’s own namespace) from policy.
  • Run the policy engine with replicas across multiple nodes.
  • Monitor webhook latency and policy-evaluation duration as SLOs.
  • Have a documented “policy bypass” runbook for incident scenarios.

Practical Implementation: A Layered Composition

A defensible 2026 stack composes the engines:

  • PSA enforce=restricted on all application namespaces. This is the safety net.
  • Kyverno for the majority of org-specific policies (signed images, allowed registries, required labels, automatic NetworkPolicy generation, sidecar injection).
  • Gatekeeper for the small set of policies that require Rego’s expressive power (cross-resource queries, complex parameterisation).
  • Falco / Tetragon at runtime to catch what admission missed.

PSA being the safety net matters because Kyverno and Gatekeeper, if misconfigured or unavailable, can fail open. PSA being in-process means it cannot fail open without a kube-apiserver failure.

Practical Implementation: The Migration Path

For a cluster still on baseline PSA or with no admission policy beyond defaults:

Phase 1: Audit. Set every namespace to pod-security.kubernetes.io/audit: restricted. The apiserver will log every workload that would violate restricted, without rejecting any. Run for 2-4 weeks; collect the report.

Phase 2: Per-namespace decision. For each namespace, decide: can we enforce restricted? Or do we need an exception? Document each exception with the business reason and a remediation plan.

Phase 3: Enforce. Flip namespaces from audit to enforce one at a time, starting with the lowest-risk (new namespaces, dev environments).

Phase 4: Compensating policies via Kyverno. For namespaces that cannot enforce restricted, write Kyverno policies that close the specific gaps.

Phase 5: Advanced policies. Image signing enforcement, required resource limits, mandatory liveness probes, namespace-level NetworkPolicy autogeneration.

Technical Deep Dive: Image Verification

Image verification is the highest-leverage admission policy beyond pod security. The pattern: containers may only run if the image is signed by an approved key, with provenance attached.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  webhookTimeoutSeconds: 30
  rules:
  - name: verify-cosign-signature
    match:
      any:
      - resources:
          kinds: [Pod]
    verifyImages:
    - imageReferences:
      - "registry.ringsafe.in/*"
      attestors:
      - entries:
        - keyless:
            subject: "https://github.com/ringsafe/*/.github/workflows/release.yml@refs/heads/main"
            issuer: "https://token.actions.githubusercontent.com"
            rekor:
              url: https://rekor.sigstore.dev

This policy enforces that any image from registry.ringsafe.in must have been signed by a GitHub Actions workflow on the main branch of an org-owned repository, with the signature recorded in Sigstore Rekor for transparency.

Enterprise Use Cases

Indian fintech platforms. Banking workloads on Kubernetes are increasingly running under PSA restricted + Kyverno image-signing policies + Cilium L7 NetworkPolicies. This baseline is what regulators are looking for in CSCRF maturity assessments.

SaaS multi-tenancy. Tenant workloads run with PSA restricted in their own namespaces; the SaaS provider’s control-plane uses Kyverno mutations to inject required tenant-isolation sidecars; Gatekeeper enforces complex tenant-quota policies.

Government and PSU platforms. Air-gapped clusters use Kyverno for image-signing against an internal Cosign root, ensuring no externally-pulled images can run.

Common Pitfalls

  • Permanent baseline. Baseline was a migration target, not an end state. Treating it as the cluster’s permanent posture leaves you exposed to CVEs like the recent containerd flaw.
  • Audit forever. Policies in audit mode catch nothing. They produce reports nobody reads. Enforce or remove.
  • Running Kyverno and Gatekeeper for the same policy. Operational redundancy with no benefit; pick one engine per policy domain.
  • Webhook fails open. A Kyverno webhook with failurePolicy: Ignore defeats its own purpose during the moments that matter (when the engine itself is under attack or stress).
  • Forgetting CronJobs and Jobs. Many security policies target Deployments and miss batch workloads.
  • System namespaces forgotten. Privileged pods in kube-system are normal; the same workloads in application namespaces are not.

Action Items

  • Run kubectl get ns -o jsonpath='{.items[*].metadata.labels.pod-security.kubernetes.io/enforce}' to inventory current PSA posture across namespaces.
  • Enable pod-security.kubernetes.io/audit: restricted on every namespace currently below restricted.
  • Install Kyverno in a non-production cluster, apply the OWASP Kubernetes policy set, observe.
  • Pick one image registry and require Cosign-signed images for it via Kyverno.
  • Define your policy-engine SLOs (webhook latency p99 < 50ms, availability >= 99.9%).
  • Document the policy bypass runbook for incident scenarios; rehearse it.

Kubernetes pod security in 2026 is no longer a “pick a tool” question — PSA, Kyverno, and Gatekeeper each have a role in a layered defence. The teams that compose them deliberately, audit honestly, and enforce decisively are the ones with cluster postures that survive both regulatory scrutiny and active attack.

AWS / Azure / GCP audit?

Get a cloud posture review

IAM hardening, public-exposure mapping, IaC review, K8s audit. We map your actual blast radius — not what a CSPM dashboard guesses at.

Book cloud scoping call Replies in 4 working hrs · India-only · Senior consultants