Skip to main content
RunBook Academy

KubernetesLXII · Pod Security StandardsPod Security Standards

PSA labels and modes — enforcing PSS at admission

Advanced⏱ ~12 minkubectl

What you'll learn

  • Configure the PSA labels on a namespace (enforce, audit, warn, version)
  • Choose the right mode for each phase of a migration
  • Use the version label to opt out of newer PSS rules
  • Diagnose why a Pod is rejected and remediate

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Pod Security Admission (PSA) is the controller that enforces PSS at admission. It reads namespace labels and applies the corresponding profile. This lesson covers the labels, the three modes (enforce, audit, warn), the version label for opt-out, and the operational patterns.

The four labels

A namespace can carry four labels that control PSA:

LabelEffect
pod-security.kubernetes.io/enforceReject Pods that violate the profile
pod-security.kubernetes.io/auditLog violations to the audit log
pod-security.kubernetes.io/warnProduce a warning event
pod-security.kubernetes.io/enforce-versionVersion of the enforce profile

The first three accept the same profile values: privileged, baseline, restricted. The fourth accepts a Kubernetes version (latest, 1.34).

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

The version label is the opt-out mechanism: a namespace that cannot meet the latest restricted profile pins to an older version.

The three modes

flowchart LR
    A[Pod admission] --> B{Pod violates?}
    B -->|yes| C{enforce mode}
    B -->|no| D[Allow]
    C -->|matches| E[Reject]
    C -->|no match| F{audit mode}
    F -->|matches| G[Log to audit]
    G --> H{warn mode}
    H -->|matches| I[Warning event]
    I --> D
    E --> J[Pod rejected]

Each mode is independent:

  • Enforce: rejects the Pod with a 403 error. The error message lists the violations.
  • Audit: logs the violation to the audit log at ResponseComplete stage. The Pod is admitted.
  • Warn: produces a Kubernetes warning event. The Pod is admitted.

The three modes are cumulative: a Pod that violates the audit profile but not the warn profile is logged but not warned. A Pod that violates the enforce profile is rejected.

The version label

The enforce-version label pins the enforce profile to a specific Kubernetes version:

labels:
  pod-security.kubernetes.io/enforce: restricted
  pod-security.kubernetes.io/enforce-version: "1.32"

A namespace pinned to 1.32 uses the restricted profile as defined in 1.32, even on a 1.34 cluster. This is the opt-out for workloads that cannot meet the latest restricted (because a new rule was added).

flowchart LR
    A[Newer rule] --> B{version pinned?}
    B -->|yes| C[Use pinned profile]
    B -->|no| D[Use latest profile]
    C --> E[Existing workload passes]
    D --> F[Existing workload fails]

The migration pattern

The migration from baseline (or no PSS) to restricted:

  1. warn: restricted. Surface violations as warnings. No Pods are rejected.
  2. audit: restricted. Capture violations in the audit log. SIEM rules can alert on the violations.
  3. Fix the violations. Address each violation: add runAsNonRoot, switch to emptyDir, add seccompProfile, etc.
  4. enforce: restricted. Once violations are fixed, switch to enforce. Future violations are rejected.
# 1. Set warn
kubectl label ns prod pod-security.kubernetes.io/warn=restricted --overwrite

# 2. Set audit
kubectl label ns prod pod-security.kubernetes.io/audit=restricted --overwrite

# 3. Identify violations
kubectl get events -n prod --field-selector reason=FailedCreate

# 4. Fix the violations in the manifests

# 5. Set enforce
kubectl label ns prod pod-security.kubernetes.io/enforce=restricted --overwrite

Common failure modes

  1. A workload is rejected. The error message lists the violated fields. The fix is to update the manifest to meet the profile.
  2. A namespace is implicitly privileged. The namespace has no PSA labels. The fix is to add enforce: restricted at creation.
  3. The version label is wrong. A namespace pinned to 1.32 on a 1.34 cluster does not enforce the new rules. The fix is to update the version (after fixing the violations) or to remove the label.
  4. The namespace label is changed. A misclick changes enforce: restricted to enforce: privileged. The fix is RBAC to restrict namespace updates to cluster-admin.

Diagnosing rejections

When a Pod is rejected, the API server returns a 403 with the violated fields:

Error from server (Forbidden): error when creating "pod.yaml":
pods "api" is forbidden: violates PodSecurity "restricted:latest":
allowPrivilegeEscalation != false (container "api" must set
securityContext.allowPrivilegeEscalation to false),
unrestricted capabilities (container "api" must set
securityContext.capabilities.drop to ["ALL"]),
seccompProfile (pod or container "api" must set
securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")

The error message lists every violation. The fix is to address each one in the manifest.

Production patterns

  1. restricted from creation. Every new namespace is created with enforce: restricted, audit: restricted, warn: restricted.
  2. Migration per-namespace. Existing namespaces migrate via warn → audit → enforce.
  3. Version pinning for exceptions. A namespace that genuinely cannot meet the latest restricted pins to an older version with a documented deadline.
  4. Quarterly audit. Review every namespace’s PSA labels; verify no namespace is implicitly privileged.

Cross-course references

  • The Observability course covers the audit log entries for PSS violations.
  • The Linux course covers the kernel features that PSS enforces.

Quiz

Knowledge check · 4 questions

  1. Q1. What is the difference between the `audit`, `warn`, and `enforce` modes?

  2. Q2. The `enforce-version` label can be used to opt a namespace out of newer PSS rules without removing the `enforce` label.

  3. Q3. Your team is migrating the `prod` namespace from `enforce: baseline` to `enforce: restricted`. Walk the migration steps.

    The `prod` namespace has been on `baseline` for a year. The workloads run as root and use `hostPath` for cache mounts. The migration to `restricted` requires removing root and replacing `hostPath`.

  4. Q4. Describe the three-step migration from no PSS to `enforce: restricted`.

Passing score: 75%. Answers are checked in this browser.

Production discipline

PSA labels are the operational control surface for PSS. A defensible PSS programme sets enforce: restricted on every production namespace, uses audit: restricted and warn: restricted for visibility, migrates per-namespace via warn → audit → enforce, and pins enforce-version only for documented exceptions. The audit log records every violation; the SIEM alerts on the violations. A cluster whose namespaces are mostly restricted with documented exceptions has a PSS programme that is auditable.