Skip to content

OPA & Gatekeeper Reference: Rego Policies, ConstraintTemplate, Audit & conftest CI

Open Policy Agent (OPA) is a general-purpose policy engine. Gatekeeper is its Kubernetes admission controller integration — it enforces policies as code using Rego, blocking non-compliant resources before they’re created.

1. OPA vs Gatekeeper vs Kyverno

Policy enforcement options for Kubernetes
Tool Scope Policy Language Best for
OPA standalone Any system (HTTP API) Rego Microservice authz, API gateways, CI/CD gates
OPA Gatekeeper Kubernetes admission Rego + K8s CRDs Enterprise K8s with complex policies
Kyverno Kubernetes admission YAML (no Rego) Teams who prefer YAML over a new language
# Rule of thumb:
# Gatekeeper: you already use OPA elsewhere, or need Rego's expressiveness
# Kyverno: YAML-native, simpler policies, no Rego learning curve
# Both enforce on: CREATE/UPDATE/DELETE via K8s admission webhooks

# How Gatekeeper works:
# 1. You define ConstraintTemplate (the Rego policy logic)
# 2. You define Constraint (instances of the template with parameters)
# 3. When a resource is created/updated, Gatekeeper's webhook evaluates the policy
# 4. DENY = resource is rejected with your custom error message

2. OPA Rego Basics

Policy language fundamentals — rules, references, and data
# Rego is a declarative query language
# A policy DENIES when a 'deny' rule is true

package kubernetes.admission

# Import request data (the K8s resource being admitted):
# input.request.object = the resource being created/updated
# input.request.operation = CREATE / UPDATE / DELETE

# Simple rule: deny pods without resource limits
deny[msg] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]    # _ = any element
    not container.resources.limits                          # no limits defined
    msg := sprintf("Container '%s' has no resource limits", [container.name])
}

# Check a specific field:
deny[msg] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    container.image == "latest"   # exact match
    msg := sprintf("Container '%s' uses 'latest' tag — pin to a specific version", [container.name])
}

# Check with regex:
import future.keywords.if

deny[msg] if {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    endswith(container.image, ":latest")
    msg := sprintf("Pin image tag for container '%s'", [container.name])
}

# Test Rego in the playground:
# https://play.openpolicyagent.org/

# Test locally with OPA CLI:
opa eval --data policy.rego --input input.json "data.kubernetes.admission.deny"
opa test ./policy_test.rego -v              # run Rego unit tests

3. Gatekeeper ConstraintTemplate

Define reusable policy templates with parameters
# ConstraintTemplate: defines the policy with Rego + the CRD schema
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: requiredlabels                  # lowercase, no underscores
spec:
  crd:
    spec:
      names:
        kind: RequiredLabels            # CRD kind — used in Constraint below
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items: {type: string}     # parameter: list of required label keys
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package requiredlabels

        violation[{"msg": msg}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("Missing required labels: %v", [missing])
        }

# Note: Gatekeeper uses 'violation' not 'deny', and 'input.review' not 'input.request'

4. Gatekeeper Constraints & Audit

Deploy policies, check violations, audit existing resources
# Install Gatekeeper:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.15/deploy/gatekeeper.yaml

# Constraint: instance of a ConstraintTemplate with specific parameters
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: RequiredLabels                    # must match ConstraintTemplate spec.crd.spec.names.kind
metadata:
  name: require-team-label
spec:
  match:
    kinds:
      - apiGroups: ["*"]
        kinds: ["Namespace"]
    excludedNamespaces:
      - kube-system
      - gatekeeper-system
      - istio-system
  parameters:
    labels: ["team", "environment"]    # must have both labels

# Check for violations:
kubectl get constraint -A              # list all constraints
kubectl describe requiredlabels require-team-label
# Look at: Status.Violations — lists existing resources that violate the policy

# Audit mode (gatekeeper audits ALL existing resources on a schedule):
kubectl get constraint require-team-label -o jsonpath='{.status.violations}' | jq .
# Shows existing violations — useful before enforcing a new policy

# Dry-run mode (monitor without blocking):
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: RequiredLabels
spec:
  enforcementAction: dryrun            # warn in status.violations but don't block
  # enforcementAction: deny           # block the request (default)
  # enforcementAction: warn           # allow but add warning to API response

# Common built-in policies (Gatekeeper Policy Library):
# https://github.com/open-policy-agent/gatekeeper-library
# includes: no-privileged-containers, required-labels, allowed-repos, block-nodeport, etc.

5. OPA Standalone (HTTP API)

Policy decisions for microservices and API gateways
# OPA as an authorization sidecar:
# Your service calls OPA's HTTP API, OPA returns allow/deny

# Start OPA server:
opa run --server --addr=:8181 --bundle ./policies/

# Policy (policies/authz.rego):
package authz

default allow = false

allow {
    input.method == "GET"
    input.path == ["api", "public"]
}

allow {
    input.method == "GET"
    input.path[0] == "api"
    input.user.role == "admin"
}

# Query OPA from your service:
curl -X POST http://localhost:8181/v1/data/authz/allow   -H "Content-Type: application/json"   -d '{"input": {"method": "GET", "path": ["api", "users"], "user": {"role": "admin"}}}'
# Returns: {"result": true}

# Bundle (OPA loads from remote URL, polling for updates):
opa run --server   --bundle https://s3.amazonaws.com/my-policies/bundle.tar.gz
# OPA polls the bundle URL and automatically loads new policies

# OPA in Envoy (external authorization filter):
# Configure Envoy ExtAuthz to call OPA for every request
# OPA returns allow/deny at the proxy level — no app code changes needed

6. Testing & CI Integration

Rego unit tests and conftest for CI gates
# Rego unit tests (policy_test.rego):
package requiredlabels_test

import data.requiredlabels.violation

test_missing_labels {
    violation[{"msg": _}] with input as {
        "review": {"object": {
            "metadata": {"labels": {"team": "backend"}}
        }},
        "parameters": {"labels": ["team", "environment"]}
    }
}

test_all_labels_present {
    count(violation) == 0 with input as {
        "review": {"object": {
            "metadata": {"labels": {"team": "backend", "environment": "prod"}}
        }},
        "parameters": {"labels": ["team", "environment"]}
    }
}

# Run tests:
opa test ./policies/ -v

# conftest — OPA policy testing for CI (validates Kubernetes YAML, Terraform, Dockerfiles):
# Install: brew install conftest

# policies/deny_latest.rego:
package main
deny[msg] {
    input.spec.template.spec.containers[_].image == "latest"
    msg := "Do not use the 'latest' image tag"
}

# Run against your manifests in CI:
conftest test deployment.yaml                  # runs all policies in ./policy/
conftest test deployment.yaml --policy custom/ # specific policy dir
# exit code 1 if any deny rule matches — blocks CI pipeline

Track OPA, Gatekeeper, and Kubernetes policy tool releases.
ReleaseRun monitors Kubernetes, Docker, and 13+ DevOps technologies.

Related: Kubernetes RBAC Reference | Kubernetes YAML Reference | Vault Reference

🔍 Free tool: K8s YAML Security Linter — check the K8s manifests OPA Gatekeeper constrains for 12 security misconfigurations before they reach the cluster.

Founded

2023 in London, UK

Contact

hello@releaserun.com