Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

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

Sponsored

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 production namespace").
  • 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-system namespace. 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=debug flag or by setting the spec.match field on Constraints.

How it works step by step

Here's the flow when a user creates a Pod that violates a policy:

  1. User runs kubectl apply -f pod.yaml
  2. API server admits the request (RBAC check passes)
  3. Gatekeeper's validating webhook is invoked with an AdmissionReview JSON object
  4. Gatekeeper evaluates all matching Constraints against the incoming object
  5. If any Constraint returns violation (via the Rego rule's violation key), the request is denied with a clear message
  6. 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 constraintTemplate by name (via the spec.crd.spec.names.kind)
  • Provides match (which resources to apply to) and parameters (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: dryrun to 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 violation but using the wrong input path (e.g., input.review.object.spec vs input.review.object.kind).
  • Overly broad match selectors that apply Constraints to the gatekeeper-system namespace, breaking Gatekeeper itself.
  • Missing spec.crd.spec.names.kind in ConstraintTemplate, causing the subsequent Constraint to reference a non-existent CRD.

Variations

  1. Use enforcementAction: dryrun instead of deny on a Constraint to get audit reports without blocking resources.
  2. Combine multiple Rego rules in a single ConstraintTemplate using separate violation blocks for each condition.
  3. 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 json and check status.byPod[].violations to see audit results for existing resources.
  • Start with enforcementAction: dryrun for 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.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.