TL;DR

Admission controllers intercept API requests before objects are persisted. Pod Security Admission (PSA) is built-in and enforces security standards by namespace label. OPA Gatekeeper and Kyverno provide custom policy-as-code. Start with PSA in warn mode, then graduate to enforce.

Pod Security Admission (Built-in)

PSA replaced PodSecurityPolicy (removed in 1.25); apply it per namespace with labels — use warn first to see what would fail before switching to enforce.

bashpsa.sh
# PSA profiles: privileged | baseline | restricted
# Modes:        enforce (reject) | audit (log) | warn (warn in API response)

# Apply restricted profile in warn+audit mode first
kubectl label namespace my-app \
  pod-security.kubernetes.io/warn=restricted \
  pod-security.kubernetes.io/warn-version=latest \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/audit-version=latest

# Once workloads are compliant, enforce:
kubectl label namespace my-app \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=latest

# Check which namespaces have PSA labels
kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}: {.metadata.labels}{"\n"}{end}' | grep "pod-security"

# Dry-run apply to see PSA warnings before deploying
kubectl apply -f deployment.yaml --dry-run=server
ProfileWhat it blocksWhen to use
privilegedNothing — full accessInfra namespaces (monitoring, CNI, CSI)
baselineKnown privilege escalations (hostPID, hostNetwork, privileged containers)Most workloads; good starting point
restrictedEverything in baseline + requires non-root, read-only rootfs, drop ALL capabilitiesProduction app namespaces

Kyverno Policies

Kyverno is a Kubernetes-native policy engine; policies are written in YAML (no Rego), making them accessible to engineers who are already comfortable with Kubernetes manifests.

yamlkyverno-policies.yaml
# Validate: require resource requests and limits on every container
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-resources
spec:
  validationFailureAction: Audit     # Audit | Enforce
  background: true                   # also check existing resources
  rules:
  - name: check-resources
    match:
      any:
      - resources:
          kinds: [Pod]
    validate:
      message: "Resource requests and limits are required on all containers."
      pattern:
        spec:
          containers:
          - (name): "*"
            resources:
              requests:
                memory: "?*"
                cpu: "?*"
              limits:
                memory: "?*"

---
# Mutate: add a default team label if missing
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-default-labels
spec:
  rules:
  - name: add-team-label
    match:
      any:
      - resources:
          kinds: [Pod]
    mutate:
      patchStrategicMerge:
        metadata:
          labels:
            +(team): unknown    # + prefix = only add if missing

---
# Generate: create a NetworkPolicy for every new namespace
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: default-deny-ingress
spec:
  rules:
  - name: generate-network-policy
    match:
      any:
      - resources:
          kinds: [Namespace]
    generate:
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      name: default-deny-ingress
      namespace: "{{request.object.metadata.name}}"
      data:
        spec:
          podSelector: {}
          policyTypes: [Ingress]
bashkyverno-cli.sh
# Test policies locally before applying to cluster
kyverno apply policy.yaml --resource deployment.yaml

# Check policy reports (audit violations without blocking)
kubectl get policyreport -A
kubectl get clusterpolicyreport

# View violations for a namespace
kubectl get policyreport -n my-app -o json | jq '.results[] | select(.result=="fail")'

OPA Gatekeeper (Overview)

Gatekeeper uses Rego (OPA policy language) for highly flexible policies; more powerful than Kyverno for complex logic, but Rego has a steeper learning curve — use it if you already have OPA expertise.

bashgatekeeper.sh
# Check Gatekeeper status
kubectl get constrainttemplates
kubectl get constraints -A         # all constraint instances

# View violations for a constraint
kubectl describe constraint require-resource-limits

# Audit mode: scan existing resources without blocking new ones
kubectl get constrainttemplate <name> -o yaml | grep enforcementAction
# dryrun = audit only; deny = enforce