Skip to content

Security Context Constraints, explained properly

Security Context Constraints (SCCs) are the concept that catches almost everyone moving from vanilla Kubernetes to OpenShift. Nothing quite like them exists upstream, and they’re the reason your first helm install may refuse to start. Understand SCCs and most “my pod won’t schedule” mysteries evaporate.

Watch on YouTube ↗

An SCC governs what a pod is allowed to do at the OS level — not its Kubernetes permissions (that’s RBAC), but what it may do on the underlying host: run as root, escalate privileges, use host networking, mount host paths, which UID range it gets, and so on. Think of it as a security policy that wraps every pod before it’s admitted.

Pod created
Admission controller intercepts it
Looks at the pod's service account
Finds which SCCs that service account may use
Validates the pod spec against those SCCs
Admits (annotating the winning SCC) or rejects

An admitted pod gets an annotation naming the SCC that validated it — the single most useful thing to check when debugging:

Terminal window
oc get pod <pod-name> -o jsonpath='{.metadata.annotations.openshift\.io/scc}'
SCC What it allows When it’s used
restricted-v2 Non-root, no host access, dropped capabilities Default — almost everything
nonroot Any non-root UID the pod chooses Apps needing a specific non-root UID
anyuid Any UID, including root Legacy images that insist on a fixed UID
hostnetwork Host network stack Network tooling
privileged Full host access Platform components only — never app teams

It’s tempting to say “restricted-v2 is assigned by default.” More precisely: every authenticated account can use restricted-v2 because it’s bound to the system:authenticated group out of the box, and a pod is admitted under it unless the service account has been granted a different SCC that the pod’s spec actually requires.

When a service account can use several SCCs, the admission controller sorts the candidates by priority, then by restrictiveness, and picks the one that admits the pod — preferring the least-privileged that still works. restricted-v2 sits at low priority precisely so it stays the universal fallback: if you’ve also been granted something more permissive, that can win when a pod genuinely needs it, while everything that fits inside restricted-v2 keeps using it.

Terminal window
# Who can use a given SCC?
oc adm policy who-can use scc restricted-v2
# Grant a higher SCC to a service account (with justification!)
oc adm policy add-scc-to-user anyuid -z <service-account> -n <namespace>

The right instinct when a team asks for anyuid

Section titled “The right instinct when a team asks for anyuid”

Challenge it — it’s almost never truly needed. The usual cause is a container image built to run as root. The durable fix is to rebuild the image to run as an arbitrary non-root UID (own the files by group, not by a hardcoded user), not to hand out anyuid. Reserve elevated SCCs for genuine legacy that can’t be rebuilt, grant them to a specific service account rather than broadly, and put a review date on them.

How this relates to Pod Security Admission

Section titled “How this relates to Pod Security Admission”

Upstream Kubernetes replaced the old PodSecurityPolicy with Pod Security Admission (PSA), a namespace-label mechanism with privileged / baseline / restricted profiles. OpenShift runs both: SCCs do the enforcement, and OpenShift keeps PSA labels in sync (the restricted-v2 SCC lines up with the PSA restricted profile). You don’t implement PodSecurityPolicy on OpenShift — it’s gone, and SCCs were always the better-designed answer. For policy beyond what SCCs cover (say, restricting which registries images may come from), reach for Kyverno or OPA Gatekeeper.

Next up: the specific errors this produces, and how to read them.