Hedronite · Ops Lesson · 01-Earth-DevOps / AKS · Sat 2026-09-12

AKS Pod Security Admission — namespace labels, PSS profiles, Azure Policy

Labels decide what may be born. Privileged pods that already run are not rewritten by a new enforce label.

Lesson Class: Ops (DevOps + AKS + Pod Security Admission)
Cloud Referent: AKS PSA namespace labels + Azure Policy fleet gate
Paired Dev: Python kubernetes PSA label census
Paired Cert: CKS PSA restricted enforce
Paired Go: client-go PSA namespace informer
Grounding: CKS Q14 · Q47 · kubestronaut CKS §4
Label
enforce / audit / warn on the namespace.
Profile
privileged, baseline, restricted.
Prove
FailedCreate after delete.
Admit the narrow pod. Measure FailedCreate after the label lands.

Labels decide what may be born. Privileged pods that already run are not rewritten by a new enforce label.

§I — Frame

On AKS, Pod Security Admission (PSA) is the built-in gate that checks pod specs against Pod Security Standards (PSS) when objects are created or updated. You turn it on with namespace labels. Azure Policy for AKS can push the same posture across many namespaces and clusters. This is not NetworkPolicy (09-09), not IRSA (09-06), and not Ingress path routing (09-03). It is also not node AppArmor from the GKE hardening pass (08-31). Today is admit-time pod shape.

§II — Three profiles, three modes

ProfileIntent
privilegedUnrestricted. Useful for system namespaces that must run hostPath or privileged init.
baselineBlocks known-dangerous patterns (privileged, host namespaces, dangerous capabilities) while staying compatible with common workloads.
restrictedHardened defaults: non-root, drop ALL capabilities, no privilege escalation, no hostPath, seccomp RuntimeDefault (or Localhost).
ModeEffect
enforceReject non-conforming pods (admission denial).
auditAllow, emit audit annotations/events.
warnAllow, print warnings to the API client.

Label keys:

pod-security.kubernetes.io/enforce=<profile>
pod-security.kubernetes.io/audit=<profile>
pod-security.kubernetes.io/warn=<profile>

Optional version pins: enforce-version, audit-version, warn-version (latest or a Kubernetes minor).

§III — Worked AKS shape

Create a tenancy namespace and enforce restricted:

kubectl create namespace secure-team
kubectl label ns secure-team \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=latest \
  pod-security.kubernetes.io/warn=restricted \
  --overwrite

A Deployment that sets privileged: true, runAsUser: 0, adds NET_ADMIN, or mounts hostPath will fail create under enforce=restricted. That is the Bootcamp Q14 failure class. Fix the pod template (drop privileges, run as non-root, emptyDir instead of hostPath) and re-apply.

Fleet note for AKS: enabling the Azure Policy add-on lets you assign built-in initiatives that require PSA labels or equivalent controls across namespaces. Use cluster-local labels for one namespace; use Azure Policy when the requirement is "every app namespace is at least baseline." Document which layer owns the control so two tools do not fight.

§IV — Failure modes

  1. Enforce after Pods exist. Existing Pods keep running. PSA checks create/update of Pod objects. To prove enforce, delete a Pod (or scale) and watch the ReplicaSet FailedCreate events (Bootcamp Q47 pattern).
  2. Wrong namespace labeled. Policy on platform does not protect app.
  3. System namespaces at restricted. kube-system often needs privileged or hostPath. Leave system namespaces on privileged (or carefully baseline) and harden app namespaces first.
  4. Warn-only forever. Warn and audit are staging tools. Production tenancy that must reject root needs enforce.
  5. Confusing PSA with NetworkPolicy. PSA never filters east-west packets. A restricted pod can still dial the whole cluster unless NetworkPolicy says otherwise.

§V — How this differs from last K8s Ops

09-09 spent VPC CNI NetworkPolicy enforcement and least-permissive ingress. 09-06 spent OIDC issuer and IAM trust for ServiceAccounts. 09-03 spent AKS Ingress and Application Gateway paths. Today spends AKS PSA labels and PSS profiles, with Azure Policy as the optional fleet amplifier.

§VI — Operator checklist

  1. List namespaces and current pod-security.kubernetes.io/* labels.
  2. Choose profile per tenancy (app = restricted or baseline; system = privileged as needed).
  3. Apply enforce (and matching warn/audit during rollout).
  4. Dry-run a known-bad Pod: kubectl label ns ... --dry-run=server and kubectl apply --dry-run=server.
  5. Confirm Azure Policy assignments if fleet policy is in scope.
  6. Inventory privileged SecurityContext pods (Dev/Go companions) before flipping enforce.

§VII — Closing

Admit the narrow pod. Measure FailedCreate after the label lands. Leave packet filters and cloud identity on their own shelves.

Related