OPA & Gatekeeper

A comprehensive guide to Open Policy Agent (OPA) and Gatekeeper covering policy as code, admission control, constraint templates, validation, and practical implementation strategies for Kubernetes policy enforcement.

OPA Policy as Code Admission Control Kubernetes Native
What is OPA and Gatekeeper?

Open Policy Agent (OPA) is an open-source, general-purpose policy engine that enables policy-as-code across the stack. Gatekeeper is a Kubernetes-specific implementation of OPA that acts as an admission controller, enforcing policies at the API server level.

OPA and Gatekeeper provide a powerful way to implement security, compliance, and operational policies in Kubernetes without modifying application code or relying on manual reviews.

  • Policy as Code: Policies are written in Rego (OPA's declarative language)
  • Admission Control: Policies are enforced during resource creation, update, or deletion
  • Audit Mode: Policies can be audited without enforcement for testing
  • Dry Run: Test policies before enforcing them in production
  • Reusable Templates: Create reusable policy templates
Key Benefits:
  • Enforce security best practices consistently across all clusters
  • Prevent misconfigurations before they reach production
  • Automate compliance with regulations (GDPR, HIPAA, SOC2)
  • Reduce manual review overhead
  • Enable self-service policies for development teams
OPA and Gatekeeper Components

OPA (Open Policy Agent)

General-purpose policy engine
OPA is a lightweight policy engine that evaluates policy decisions based on Rego policies. It can be used for Kubernetes, API gateways, and other services.
Policy evaluation engine

Gatekeeper

Kubernetes admission controller
Gatekeeper extends OPA to Kubernetes, acting as a validating admission webhook. It intercepts API requests and evaluates policies before resources are created or modified.
Kubernetes policy enforcement

ConstraintTemplate

Reusable policy template
A ConstraintTemplate defines a reusable policy with a schema and Rego logic. It can be instantiated multiple times with different parameters.
Policy definition

Constraint

Instantiated policy
A Constraint is an instance of a ConstraintTemplate with specific parameters. It defines which resources the policy applies to and the specific requirements.
Policy enforcement
Installing Gatekeeper
# Install Gatekeeper using kubectl kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.12/deploy/gatekeeper.yaml # Install Gatekeeper using Helm helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts helm repo update helm install gatekeeper gatekeeper/gatekeeper \ --namespace gatekeeper-system \ --create-namespace # Verify installation kubectl get pods -n gatekeeper-system kubectl get validatingwebhookconfigurations | grep gatekeeper # Check Gatekeeper status kubectl get crd | grep -E "constraint|gatekeeper" # Enable audit mode (optional) kubectl patch -n gatekeeper-system deployment/gatekeeper-controller \ -p '{"spec":{"template":{"spec":{"containers":[{"name":"controller","args":["--audit=true"]}]}}}}' # Enable dry-run mode (test without enforcement) # Add --dry-run flag to Gatekeeper deployment
Installation Considerations:
  • Gatekeeper requires Kubernetes 1.16+
  • Install in a dedicated namespace (gatekeeper-system)
  • Ensure cluster has sufficient resources (CPU/memory) for Gatekeeper pods
  • Test in a non-production environment before enforcing policies in production
Rego: The Policy Language

Rego is the declarative language used by OPA to write policies. It's designed to express policy decisions and is highly expressive yet readable.

# Basic Rego Policy Structure package mypolicy # Violation rule - returns violation message violation[{"msg": msg, "details": details}] { # Policy logic # If condition matches, violation is triggered not input.review.object.metadata.labels["app"] msg := "Pod must have an 'app' label" details := {"missing": "app label"} } # Multiple violation rules violation[{"msg": msg}] { container := input.review.object.spec.containers[_] container.securityContext.allowPrivilegeEscalation == true msg := sprintf("Container %v allows privilege escalation", [container.name]) } violation[{"msg": msg}] { container := input.review.object.spec.containers[_] not container.securityContext.runAsNonRoot == true msg := sprintf("Container %v must run as non-root", [container.name]) } # Helper functions is_production(namespace) { namespace == "production" } is_production(namespace) { namespace == "prod" } # Policy with parameter allowed_registries(registries) { container := input.review.object.spec.containers[_] image := container.image # Check if image comes from allowed registries allowed := {reg | reg := registries[_]} starts_with(image, allowed) } # Example Rego playgound: https://play.openpolicyagent.org/
Rego Best Practices:
  • Use clear package names to organize policies
  • Write expressive violation messages for easier debugging
  • Use helper functions to avoid code duplication
  • Test policies with sample inputs before deploying
  • Keep policies focused on single concerns
  • Use comments to explain complex logic
Constraint Templates: Reusable Policies

ConstraintTemplates define reusable policy templates with a schema and Rego logic. They can be instantiated with different parameters across namespaces.

# ConstraintTemplate: Require Labels apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8srequiredlabels spec: crd: spec: names: kind: K8sRequiredLabels validation: openAPIV3Schema: type: object properties: labels: type: array items: type: string message: type: string targets: - target: admission.k8s.gatekeeper.sh rego: | package k8srequiredlabels violation[{"msg": msg, "details": {"missing_labels": missing}}] { provided := {label | input.review.object.metadata.labels[label]} required := {label | label := input.parameters.labels[_]} missing := required - provided count(missing) > 0 msg := sprintf("%s: you must provide labels: %v", [input.parameters.message, missing]) } # ConstraintTemplate: Require Security Context apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8srequiredsecuritycontext spec: crd: spec: names: kind: K8sRequiredSecurityContext validation: openAPIV3Schema: type: object properties: runAsNonRoot: type: boolean allowPrivilegeEscalation: type: boolean dropCapabilities: type: array items: type: string targets: - target: admission.k8s.gatekeeper.sh rego: | package k8srequiredsecuritycontext violation[{"msg": msg, "details": {"containers": containers}}] { containers := [container | container := input.review.object.spec.containers[_]] container := containers[_] not container.securityContext.runAsNonRoot == input.parameters.runAsNonRoot msg := sprintf("Container %v must run as non-root", [container.name]) } # ConstraintTemplate: Restrict Image Registry apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8sallowedregistries spec: crd: spec: names: kind: K8sAllowedRegistries validation: openAPIV3Schema: type: object properties: registries: type: array items: type: string targets: - target: admission.k8s.gatekeeper.sh rego: | package k8sallowedregistries violation[{"msg": msg}] { container := input.review.object.spec.containers[_] image := container.image allowed := [reg | reg := input.parameters.registries[_]] not any({reg | startswith(image, reg)}) msg := sprintf("Image %v must be from allowed registry: %v", [image, allowed]) }
Template Best Practices:
  • Use meaningful names for templates (k8s* prefix)
  • Define clear schemas with OpenAPI validation
  • Include descriptive violation messages
  • Test templates with dry-run before enforcing
  • Version control templates like any other code
  • Create templates for common security and compliance requirements
Constraints: Enforcing Policies

Constraints are instances of ConstraintTemplates with specific parameters. They define which resources the policy applies to and the specific requirements.

# Constraint: Require Labels in Production apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredLabels metadata: name: require-app-label-prod spec: match: kinds: - apiGroups: [""] kinds: ["Pod"] namespaces: - "production" - "prod" - "staging" labelSelector: matchLabels: environment: production parameters: labels: - "app" - "team" - "environment" message: "All production pods must have app, team, and environment labels" # Constraint: Require Security Context apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredSecurityContext metadata: name: require-secure-context spec: match: kinds: - apiGroups: [""] kinds: ["Pod"] parameters: runAsNonRoot: true allowPrivilegeEscalation: false dropCapabilities: - "ALL" # Constraint: Allow Only Trusted Registries apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sAllowedRegistries metadata: name: allow-trusted-registries spec: match: kinds: - apiGroups: [""] kinds: ["Pod"] parameters: registries: - "docker.io/" - "gcr.io/" - "mycompany.azurecr.io/" - "ghcr.io/mycompany/" # Constraint: Enforce Resource Limits apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredResourceLimits metadata: name: require-limits spec: match: kinds: - apiGroups: [""] kinds: ["Pod"] namespaces: - "production" parameters: memory: "512Mi" cpu: "500m" # Constraint: Restrict HostNetwork apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRestrictHostNetwork metadata: name: no-host-network spec: match: kinds: - apiGroups: [""] kinds: ["Pod"] parameters: enabled: false # Check constraints status kubectl get constraints kubectl describe constraint require-app-label-prod # View violations kubectl get constraint -o yaml | grep -A 5 violations
Constraint Enforcement:
  • Start with enforcementAction: dryrun to test policies without blocking
  • Use match to target specific resources (kinds, namespaces, labels)
  • Monitor constraint violations with Gatekeeper audit logs
  • Gradually enforce policies in production after testing in staging
  • Document exceptions and use excludedNamespaces for exemptions
Audit and Dry Run: Testing Policies Safely

Gatekeeper provides audit and dry-run modes to test policies before enforcing them in production.

# Audit mode - Check existing resources for violations # Enable audit by adding --audit flag to controller kubectl patch -n gatekeeper-system deployment/gatekeeper-controller \ -p '{"spec":{"template":{"spec":{"containers":[{"name":"controller","args":["--audit=true"]}]}}}}' # Check audit results kubectl get constraint -o yaml | grep -A 10 status # Example audit output status: auditTimestamp: "2025-01-15T10:00:00Z" violations: - enforcementAction: deny kind: Pod message: "Pod must have an 'app' label" name: violating-pod namespace: default # Dry run mode (test without enforcement) # Add enforcementAction: dryrun to constraint apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredLabels metadata: name: test-labels spec: enforcementAction: dryrun # Don't block, just log violations match: kinds: - apiGroups: [""] kinds: ["Pod"] parameters: labels: ["app", "team"] # Watch for dryrun violations kubectl get constraint test-labels -o yaml | grep -A 5 violations # Simulate a request with Gatekeeper # Use the webhook test endpoint kubectl run test-pod --image=nginx -o yaml --dry-run=client | \ kubectl apply -f - --server-side --dry-run=server
Testing Workflow:
  1. Create ConstraintTemplate and test with Rego playground
  2. Deploy Template with enforcementAction: dryrun
  3. Create Constraint and monitor audit violations
  4. Review violations and fix non-compliant resources
  5. Enable enforcementAction: deny for enforcement
  6. Monitor and refine policies based on feedback
Common Policy Examples
# Policy: Prevent running as root apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8snonroot spec: crd: spec: names: kind: K8sNonRoot targets: - target: admission.k8s.gatekeeper.sh rego: | package k8snonroot violation[{"msg": msg}] { container := input.review.object.spec.containers[_] not container.securityContext.runAsNonRoot == true msg := sprintf("Container %v must run as non-root", [container.name]) } # Policy: Disallow hostNetwork apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8sdisallowhostnetwork spec: crd: spec: names: kind: K8sDisallowHostNetwork targets: - target: admission.k8s.gatekeeper.sh rego: | package k8sdisallowhostnetwork violation[{"msg": msg}] { input.review.object.spec.hostNetwork == true msg := "hostNetwork is not allowed" } # Policy: Enforce image pull policy Always apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8simagepullpolicy spec: crd: spec: names: kind: K8sImagePullPolicy targets: - target: admission.k8s.gatekeeper.sh rego: | package k8simagepullpolicy violation[{"msg": msg}] { container := input.review.object.spec.containers[_] container.imagePullPolicy != "Always" msg := sprintf("Container %v must have imagePullPolicy Always", [container.name]) } # Policy: Prevent latest tag apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8snolatesttag spec: crd: spec: names: kind: K8sNoLatestTag targets: - target: admission.k8s.gatekeeper.sh rego: | package k8snolatesttag violation[{"msg": msg}] { container := input.review.object.spec.containers[_] endswith(container.image, ":latest") msg := sprintf("Container %v uses 'latest' tag - specify a version", [container.name]) } # Policy: Require Readiness and Liveness Probes apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8srequiredprobes spec: crd: spec: names: kind: K8sRequiredProbes targets: - target: admission.k8s.gatekeeper.sh rego: | package k8srequiredprobes violation[{"msg": msg}] { container := input.review.object.spec.containers[_] not container.readinessProbe msg := sprintf("Container %v missing readinessProbe", [container.name]) } violation[{"msg": msg}] { container := input.review.object.spec.containers[_] not container.livenessProbe msg := sprintf("Container %v missing livenessProbe", [container.name]) }
Gatekeeper vs Other Policy Tools
Feature Gatekeeper (OPA) Pod Security Standards Kyverno Validating Webhooks
Policy Language Rego (rich) Built-in profiles YAML/JSON Custom code
Custom Policies Yes Limited Yes Yes
Audit Mode Yes Yes Yes No
Dry Run Yes Yes Yes No
Constraint Templates Yes No Yes (policies) No
Complexity High Low Medium High
Learning Curve Steep (Rego) Low Medium High
GitOps Support Yes Yes Yes Yes
Choosing a Policy Tool:
  • Gatekeeper: Best for complex, custom policies and enterprise compliance
  • Pod Security Standards: Best for simple, built-in security profiles
  • Kyverno: Best for teams familiar with YAML and simpler policy needs
  • Validating Webhooks: Best for very specific, custom logic not covered by other tools
Troubleshooting Gatekeeper
# Check Gatekeeper pods status kubectl get pods -n gatekeeper-system # View Gatekeeper logs kubectl logs -n gatekeeper-system deployment/gatekeeper-controller # Check for Rego syntax errors # Use OPA playground: https://play.openpolicyagent.org/ # Validate ConstraintTemplate kubectl get constrainttemplate -o yaml # Check constraint status kubectl get constraint -o yaml # Debug admission request # Add --log-level=debug to Gatekeeper controller kubectl patch -n gatekeeper-system deployment/gatekeeper-controller \ -p '{"spec":{"template":{"spec":{"containers":[{"name":"controller","args":["--log-level=debug"]}]}}}}' # Test policy with mock input # Create test input file cat > test-input.json < --overwrite \ "gatekeeper.sh/sync=true"
Common Issues:
  • Rego syntax errors: Use OPA playground to validate syntax
  • Missing schemas: Ensure CRD validation matches parameters
  • Admission timeouts: Increase webhook timeout if policies are complex
  • Resource exhaustion: Monitor memory usage of Gatekeeper pods
  • Policy conflicts: Ensure policies don't contradict each other
Frequently Asked Questions
What is the difference between OPA and Gatekeeper?
OPA is a general-purpose policy engine that can be used for any service. Gatekeeper is a Kubernetes-specific implementation of OPA that acts as an admission controller. Gatekeeper uses OPA to evaluate policies against Kubernetes resources.
How does Gatekeeper differ from Pod Security Standards?
Pod Security Standards are built-in Kubernetes profiles (Privileged, Baseline, Restricted). Gatekeeper provides more flexibility with custom policies written in Rego. Use Pod Security Standards for simple security requirements, Gatekeeper for complex custom policies.
What is a ConstraintTemplate?
A ConstraintTemplate is a reusable policy template that defines the schema and Rego logic for a policy. It can be instantiated multiple times with different parameters to create specific Constraints.
How do I test policies without blocking resources?
Use enforcementAction: dryrun in your Constraint. Gatekeeper will evaluate policies and report violations without blocking resource creation. Use audit mode to check existing resources.
Can Gatekeeper audit existing resources?
Yes! Gatekeeper has an audit mode that checks existing resources against policies. Enable the audit flag and Gatekeeper will report violations in the constraint status.
What happens when a policy violation occurs?
When a resource violates a policy, the API request is rejected with an error message describing the violation. The resource is not created or updated. Violations are logged in the Gatekeeper controller logs.
Can I use Gatekeeper with GitOps?
Yes! Gatekeeper resources (Templates and Constraints) are Kubernetes custom resources and can be managed via GitOps. Store them in your Git repository and apply them during cluster bootstrap or sync.
What are the performance implications of Gatekeeper?
Gatekeeper adds latency to API requests (typically 10-50ms). It uses caching and optimized evaluation to minimize impact. For large clusters, ensure sufficient resources for Gatekeeper pods and monitor their performance.
Previous: RBAC Best Practices Next: Network Security

OPA and Gatekeeper provide powerful policy-as-code capabilities for Kubernetes. Start with simple policies, test thoroughly, and gradually expand your policy library to enforce security, compliance, and operational best practices.