Enforcing Kubernetes Security Policies with OPA Gatekeeper

By default, Kubernetes enforces very few security restrictions. Deploying a pod configured with privileged: true grants host-level permissions to the container process, effectively bypassing container isolation and increasing the risk of node compromise. To prevent insecure configurations from reaching production clusters, platform engineering teams leverage Open Policy Agent (OPA) Gatekeeper to enforce Policy as Code at the admission control layer.
The Risk of Privileged Containers
Running containers in privileged mode exposes host kernel capabilities directly to the container runtime. Should an application running inside a privileged container be compromised, an attacker could breach the container boundary, access host filesystems, or move laterally across the entire cluster network. Consequently, blocking privileged containers is a foundational security baseline for production environments.
The Solution: Gatekeeper ConstraintTemplate
Gatekeeper relies on two core custom resources to enforce operational policies: a ConstraintTemplate, which defines the underlying policy logic in Rego, and a Constraint, which applies that policy logic to specific cluster resources or namespaces.
Below is an example of a ConstraintTemplate configured to reject any pod manifest containing privileged containers:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdisallowprivileged
spec:
crd:
spec:
names:
kind: K8sDisallowPrivileged
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdisallowprivileged
violation[{"msg": msg}] {
c := input.review.object.spec.containers[_]
c.securityContext.privileged
msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
}
How the Rego Policy Works
Here is a step-by-step overview of how Gatekeeper processes admission review requests sent by the Kubernetes API server:
package k8sdisallowprivileged: Defines the namespace scope for this Rego rule, preventing naming conflicts with other cluster policies.violation[{"msg": msg}] { ... }: Declares the violation evaluation block. If all expressions within this block evaluate to true, Gatekeeper registers a policy violation and denies the admission request.c := input.review.object.spec.containers[_]: Iterates over each container in thespec.containersarray within the evaluated pod manifest (input.review.object). The_symbol serves as an implicit loop iterator.c.securityContext.privileged: Evaluates whetherprivilegedis explicitly enabled (true) on the current container (c). Ifprivilegedisfalseor absent, the evaluation condition fails for that item and iteration proceeds.msg := sprintf(...): Generates a formatted failure message sent back to the user or CI/CD pipeline when a deployment is rejected, specifying the non-compliant container name and security context configuration.
Applying the Constraint
Deploying the ConstraintTemplate alone does not actively restrict workloads; it only registers the custom resource definition (K8sDisallowPrivileged) and stores the policy logic. To enforce the policy across a cluster, platform engineers must deploy a matching K8sDisallowPrivileged custom resource targeting specific namespaces or resources. Decoupling policy definitions from policy enforcement allows teams to define governance rules once and roll them out flexibly across environments.