PodSecurityPolicy Deprecation: Migration to Pod Security Admission

PodSecurityPolicy Deprecation: Migration to Pod Security Admission

Introduction

In April 2021, with Kubernetes 1.21, PodSecurityPolicy (PSP) was officially deprecated, marking the end of an era and the beginning of a new approach to pod security enforcement. The deprecation announcement gave teams until Kubernetes 1.25 (2022) to migrate to alternatives: Pod Security Admission (introduced in 1.23) or policy engines like Gatekeeper and Kyverno.

This mattered because PSP had been the primary pod security enforcement mechanism since Kubernetes 1.3, but its complexity, RBAC requirements, and performance issues made it difficult to use in production. The deprecation forced teams to evaluate alternatives and migrate their security policies, representing a significant operational change for many organizations.

Historical note: PSP had been in beta since Kubernetes 1.3 (2016) and never reached GA. Its deprecation in 1.21 and removal in 1.25 marked the end of a 5+ year beta feature that had become a security standard despite its limitations.

Why PodSecurityPolicy Was Deprecated

Complexity Issues

  • RBAC Complexity: PSP required complex RBAC bindings that were difficult to understand and maintain.
  • Confusing Semantics: Policy application and precedence rules were unclear.
  • Performance Problems: PSP evaluation had performance overhead, especially with many policies.
  • Limited Scope: PSP only applied to pods, not other Kubernetes resources.

Usability Problems

  • Hard to Use Correctly: Most teams used PSP incorrectly or not at all.
  • Namespace Limitations: PSP couldn’t be easily scoped to namespaces.
  • Migration Challenges: Difficult to migrate existing policies to PSP.
  • Documentation Gaps: Limited documentation and examples.

Migration Options

Option 1: Pod Security Admission (Kubernetes 1.23+)

Pod Security Admission provides namespace-scoped security profiles:

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

Advantages:

  • Simple, namespace-scoped
  • Built into Kubernetes
  • Standard security profiles
  • Better performance

Limitations:

  • Only three profiles (Privileged, Baseline, Restricted)
  • Less flexible than PSP
  • Requires Kubernetes 1.23+

Option 2: Gatekeeper

Gatekeeper provides flexible policy enforcement using OPA:

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8spspsecuritycontext
spec:
  crd:
    spec:
      names:
        kind: K8sPSPSecurityContext
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8spspsecuritycontext
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.securityContext.runAsNonRoot
          msg := "Container must run as non-root"
        }

Advantages:

  • Flexible policy language (Rego)
  • Can enforce policies on any resource
  • Mutation support
  • Audit mode

Limitations:

  • Requires learning Rego
  • Additional infrastructure
  • More complex than Pod Security Admission

Option 3: Kyverno

Kyverno provides Kubernetes-native policy enforcement:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-non-root
spec:
  validationFailureAction: enforce
  rules:
  - name: check-security-context
    match:
      resources:
        kinds:
        - Pod
    validate:
      message: "Container must run as non-root"
      pattern:
        spec:
          containers:
          - name: "*"
            securityContext:
              runAsNonRoot: true

Advantages:

  • YAML-based policies (no Rego)
  • Kubernetes-native
  • Policy generation and mutation
  • Easy to learn

Limitations:

  • Less mature than Gatekeeper
  • May have performance limitations

Comparison: PSP vs Alternatives

CapabilityPodSecurityPolicyPod Security AdmissionGatekeeperKyverno
ScopePods onlyPods onlyAny resourceAny resource
ComplexityHigh (RBAC)Low (labels)Medium (Rego)Low (YAML)
PerformancePoorGoodGoodGood
FlexibilityMediumLow (3 profiles)HighHigh
MutationNoNoYesYes
Kubernetes NativeYes (deprecated)YesNo (add-on)No (add-on)
Best ForLegacy clustersSimple use casesComplex policiesKubernetes-native policies

Migration Strategy

Phase 1: Assessment

  1. Inventory PSP Usage: Identify all PSP policies and their usage.
  2. Map to Alternatives: Map PSP policies to Pod Security Admission profiles or policy engine rules.
  3. Test in Staging: Test migration in staging environment.
  4. Plan Rollout: Plan gradual rollout to production.

Phase 2: Implementation

  1. Deploy Alternative: Deploy Pod Security Admission or policy engine.
  2. Create Policies: Create equivalent policies in new system.
  3. Enable Audit Mode: Enable audit mode to identify violations.
  4. Remediate Violations: Fix workloads that violate policies.

Phase 3: Migration

  1. Enable Enforcement: Enable enforcement in non-critical namespaces.
  2. Monitor: Monitor for issues and adjust policies.
  3. Gradual Rollout: Gradually enable enforcement in all namespaces.
  4. Disable PSP: Disable PSP once migration is complete.

Practical Migration Examples

PSP Policy

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
spec:
  privileged: false
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
    - ALL
  runAsUser:
    rule: MustRunAsNonRoot
  seLinux:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    - 'persistentVolumeClaim'

Pod Security Admission Equivalent

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

Gatekeeper Equivalent

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8spspsecuritycontext
spec:
  crd:
    spec:
      names:
        kind: K8sPSPSecurityContext
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8spspsecuritycontext
        violation[{"msg": msg}] {
          input.review.object.spec.securityContext.privileged == true
          msg := "Pod must not be privileged"
        }
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          container.securityContext.allowPrivilegeEscalation == true
          msg := "Container must not allow privilege escalation"
        }

Getting Started with Migration

# 1. Identify PSP usage
kubectl get psp
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name | contains("psp"))'

# 2. Deploy Pod Security Admission (Kubernetes 1.23+)
# Enable feature gate
kube-apiserver --feature-gates=PodSecurity=true

# 3. Create namespace with Pod Security Admission
kubectl create namespace production
kubectl label namespace production pod-security.kubernetes.io/enforce=restricted

# 4. Test workloads
kubectl apply -f test-pod.yaml --dry-run=server

Caveats & Lessons Learned

  • Timeline: PSP removal in 1.25 gives teams ~1 year to migrate; start early.
  • Policy Mapping: Not all PSP policies map directly to alternatives; some require custom policies.
  • Workload Updates: Some workloads may need updates to comply with new policies.
  • Testing: Thoroughly test migration in staging before production.

Common Failure Modes

  • “Workloads blocked”: New policies block existing workloads; use audit mode first.
  • “Policy gaps”: Some PSP policies don’t have direct equivalents; create custom policies.
  • “Migration delays”: Teams delay migration until 1.25; start early to avoid rush.

Conclusion

PodSecurityPolicy’s deprecation in 2021 marked a significant shift in Kubernetes pod security. While PSP had served the community for years, its complexity and limitations made it unsuitable for modern Kubernetes deployments. The deprecation forced teams to evaluate alternatives and adopt more modern, flexible policy enforcement mechanisms.

For organizations using PSP, the migration represented a necessary evolution toward better security practices. Pod Security Admission, Gatekeeper, and Kyverno offered different approaches to pod security, each with distinct advantages. Teams could now choose the right tool based on their complexity needs, flexibility requirements, and operational preferences.

The migration from PSP to modern policy engines demonstrated that Kubernetes security was evolving. What started as a simple pod security mechanism had become a comprehensive policy enforcement ecosystem. PSP’s deprecation wasn’t the end of pod security—it was the beginning of a more flexible, powerful approach to Kubernetes policy enforcement.