OPA/Gatekeeper Policy Enforcement
Enforce policies in Kubernetes using OPA/Gatekeeper. Step-by-step tutorial covers installation, ConstraintTemplate creation, and admission control for production compliance.
Focus: use opa/gatekeeper for policy enforcement
Kubernetes clusters grow fast, and without guardrails, misconfigurations like running privileged containers or forgetting resource limits become security and stability risks. Open Policy Agent (OPA) with Gatekeeper provides a native, admission-controller-based way to enforce custom policies on API resources before they're persisted — letting you codify compliance as code.
The problem this lesson solves
Kubernetes RBAC and Pod Security Standards cover some security basics, but they don't let you express custom rules like "all images must come from a trusted registry" or "every namespace must have a cost-center label." Manual audits are slow and error-prone. Teams need a policy engine that:
- Intercepts resource creation and updates at admission time
- Evaluates policies written in a declarative language (Rego)
- Rejects noncompliant resources before they reach etcd
- Audits existing resources without breaking running workloads
OPA/Gatekeeper fills exactly that gap. It turns your cluster into a self-enforcing system where policy violations are caught immediately, not weeks later during a compliance review.
Core concept / mental model
Think of OPA/Gatekeeper as a custom admission webhook that runs a rules engine inside your cluster. Instead of embedding hardcoded logic in a webhook, you write ConstraintTemplates (which define the rule logic in Rego) and then instantiate them as Constraints (which apply the rule to specific resources).
Key components
- OPA (Open Policy Agent): The general-purpose policy engine that evaluates Rego rules.
- Gatekeeper: A Kubernetes-native project that integrates OPA as an admission webhook, providing CRDs for policy management.
- ConstraintTemplate: A CRD that contains the Rego rule and the schema for its parameters.
- Constraint: An instantiation of a ConstraintTemplate with specific parameters (e.g., "enforce this label on all Deployments in the
productionnamespace"). - Admission review: The Kubernetes API server sends admission requests to Gatekeeper, which evaluates all applicable Constraints and returns allowed or denied.
Pro tip: Gatekeeper runs as a single deployment in the
gatekeeper-systemnamespace. It uses a mutating webhook for auditing and a validating webhook for enforcement. You can configure it to audit but not reject (dry run) via the--log-level=debugflag or by setting thespec.matchfield on Constraints.
How it works step by step
Here's the flow when a user creates a Pod that violates a policy:
- User runs
kubectl apply -f pod.yaml - API server admits the request (RBAC check passes)
- Gatekeeper's validating webhook is invoked with an
AdmissionReviewJSON object - Gatekeeper evaluates all matching Constraints against the incoming object
- If any Constraint returns
violation(via the Rego rule'sviolationkey), the request is denied with a clear message - If all Constraints pass, the request proceeds to etcd
The ConstraintTemplate + Constraint pattern
- ConstraintTemplate defines a reusable rule. It includes:
spec.crd.spec.names.kind: Name of the Constraint CRD this template creates.spec.targets[0].rego: The actual Rego code.spec.targets[0].libs: Optional helper Rego libraries.- Constraint is a custom resource that:
- References a
constraintTemplateby name (via thespec.crd.spec.names.kind) - Provides
match(which resources to apply to) andparameters(dynamic values for the rule)
Hands-on walkthrough
Step 1: Install Gatekeeper
# Use the official release (replace with latest version)
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.16.0/deploy/gatekeeper.yaml
# Verify
kubectl get pods -n gatekeeper-system
Expected output:
NAME READY STATUS RESTARTS AGE
gatekeeper-audit-5b8c7c68d6-7x9fn 1/1 Running 0 2m
gatekeeper-controller-manager-7f8b9c7c9d-2k4l7 1/1 Running 0 2m
gatekeeper-controller-manager-7f8b9c7c9d-4t6w5 1/1 Running 0 2m
Step 2: Create a ConstraintTemplate that denies privileged containers
Create deny-privileged-container.yaml:
# Note: Rego is not Python, but we use fenced blocks for consistency with our tutorial
Actually, Rego is not Python. The file contains:
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8sdenyprivileged
spec:
crd:
spec:
names:
kind: K8sDenyPrivileged
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdenyprivileged
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
container.securityContext.privileged
msg := sprintf("Privileged container is not allowed, found container: %v", [container.name])
}
Apply it:
kubectl apply -f deny-privileged-container.yaml
Step 3: Create a Constraint that uses the template
Create constraint-deny-privileged.yaml:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDenyPrivileged
metadata:
name: deny-all-privileged
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["gatekeeper-system"]
Apply:
kubectl apply -f constraint-deny-privileged.yaml
Step 4: Test the policy
# This should be DENIED
kubectl run privileged-pod --image=nginx --privileged
Expected output:
Error from server ([deny-all-privileged] Privileged container is not allowed, found container: privileged-pod): admission webhook "validation.gatekeeper.sh" denied the request: [deny-all-privileged] Privileged container is not allowed, found container: privileged-pod
Now test a compliant pod:
kubectl run safe-pod --image=nginx
# Should succeed
Compare options / when to choose what
| Approach | Complexity | Flexibility | Performance | Audit support | Best for |
|---|---|---|---|---|---|
| OPA/Gatekeeper | Medium | High (Rego) | Good (delegated policy engine) | Yes (built-in) | Custom policies across namespaces, teams |
| Pod Security Standards (PSS/PSA) | Low | Low (predefined levels) | Native (no extra component) | No (enforce/audit/warn) | Quick baseline security, managed clusters |
| Custom admission webhook | High | Very high (any logic) | Depends on implementation | Manual | Legacy integrations, non-OPA rule engines |
| Kyverno | Low | Medium (YAML rules) | Good | Yes | Simpler YAML-based policy, no Rego needed |
If you need to write complex rules with conditionals and data lookups, OPA/Gatekeeper is the strongest choice. If you only need basic security postures, start with Pod Security Standards.
Troubleshooting & edge cases
Symptom: ConstraintTemplate fails to apply
Error: unable to recognize "template.yaml": no matches for kind "ConstraintTemplate" in version "templates.gatekeeper.sh/v1beta1"
Fix: Ensure Gatekeeper is installed and the CRD has been registered. Wait 10 seconds after installation or check pod logs: kubectl logs -n gatekeeper-system deployment/gatekeeper-controller-manager.
Symptom: Admission webhook denies everything
Check: Your Constraint's match field might be too broad. Use kubectl get constraint -o yaml to inspect the generated violations. Look for status.byPod[].violations.
Symptom: Rego rule has logic errors
Fix: Use the Gatekeeper playground at https://play.openpolicyagent.org/ to debug Rego before deploying. Validate the violation block structure — missing braces are common.
Edge case: Auditing existing resources
Gatekeeper audits existing resources every 60 seconds by default. To see violations for existing objects:
kubectl get constraint K8sDenyPrivileged -o json | jq '.items[].status.byPod'
What you learned & what's next
You now understand how OPA/Gatekeeper enforces custom policies in Kubernetes using ConstraintTemplates and Constraints. You can:
- Install Gatekeeper into any cluster
- Write a Rego rule to block privileged containers
- Apply Constraints with matching rules based on namespace, kind, or labels
- Troubleshoot common admission denials and Rego syntax errors
Next step: In the following lesson, you'll learn how to use Kyverno, an alternative policy engine that uses YAML-only rules — great for teams that prefer no Rego. Compare the two approaches to decide which fits your compliance workflows.
Pro tip: Before going to production with Gatekeeper, enable audit mode on critical Constraints first by adding
spec.enforcementAction: dryrunto your Constraint — this catches violations without breaking existing workloads.
Practice recap
Install Gatekeeper on a test cluster, write a ConstraintTemplate that requires all Pods to have a team label, and apply a Constraint that denies any Pod missing the label. Test with kubectl run to confirm enforcement, then check the audit status of existing pods.
Common mistakes
- Forgetting to install Gatekeeper before applying ConstraintTemplates, leading to 'no matches for kind' errors.
- Writing Rego rules that return
violationbut using the wronginputpath (e.g.,input.review.object.specvsinput.review.object.kind). - Overly broad match selectors that apply Constraints to the
gatekeeper-systemnamespace, breaking Gatekeeper itself. - Missing
spec.crd.spec.names.kindin ConstraintTemplate, causing the subsequent Constraint to reference a non-existent CRD.
Variations
- Use
enforcementAction: dryruninstead ofdenyon a Constraint to get audit reports without blocking resources. - Combine multiple Rego rules in a single ConstraintTemplate using separate
violationblocks for each condition. - Leverage Gatekeeper's external data feature to call external APIs (e.g., registry scanners) during policy evaluation.
Real-world use cases
- Enforcing that all container images in a production namespace must originate from a corporate registry (e.g.,
registry.mycompany.io/*). - Requiring every Deployment to have resource requests and limits defined, rejecting any that omit
spec.template.spec.containers[].resources. - Auditing existing Secrets to ensure they are annotated with an expiry date, flagging those without the annotation.
Key takeaways
- Gatekeeper is a Kubernetes admission webhook that enforces policies written in Rego via ConstraintTemplates and Constraints.
- Install Gatekeeper cluster-wide with a single
kubectl apply, then register ConstraintTemplates and instantiate them as Constraints. - A ConstraintTemplate defines the Rego rule and the CRD kind; a Constraint applies that rule with match selectors and parameters.
- Use
kubectl get constraint -o jsonand checkstatus.byPod[].violationsto see audit results for existing resources. - Start with
enforcementAction: dryrunfor new policies to avoid breaking running workloads during testing. - Gatekeeper is one of several policy engines — evaluate Kyverno or Pod Security Standards for simpler use cases.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.