Policy as Code: Enforcing Security and Compliance with Open Policy Agent (OPA)

Imagine you’ve built a beautiful, efficient private cloud infrastructure. Your Ceph storage is humming, your Kubernetes clusters are orchestrating containers flawlessly, and your team is deploying code at record speed. Then, one Monday morning, you discover a developer accidentally exposed an internal database with personal data, another spun up 50 oversized test VMs over the weekend, and a compliance audit reveals that sensitive information wasn’t encrypted.

This is where Open Policy Agent (OPA) comes in.

Open Policy Agent (OPA) is an open-source, general-purpose policy engine that lets you unify policy enforcement across your entire stack. Think of it as a bouncer for your infrastructure — but one that follows rules written in code, not gut feelings. OPA decouples policy decisions from application logic, allowing you to write rules once and enforce them everywhere: from Kubernetes admission control to Terraform plans, from API gateways to CI/CD pipelines.

In this article, we’ll explore how OPA brings “policy-as-code” to life, walk through practical examples using its declarative language Rego, and show how it fits into a modern cloud native infrastructure and DevOps practices.

What Is an Open Policy Agent (OPA) and Why Should You Care?

At its simplest, OPA answers one question: “Is this action allowed?” Your application or tool sends OPA a JSON object describing a request (e.g.,Ā {“user”: “john”, “action”: “delete”, “resource”: “database”}), and OPA returns a decision:Ā allowĀ orĀ deny.

But here’s the magic: the policies that make that decision are written in a high-level declarative language called Rego, stored in Git, tested like code, and deployed independently of the services that query them. This separation of concerns is what gives a lot of flexibility.

Why does this matter for infrastructure?

  • Consistency across teams: No more “my service uses RBAC, hers uses custom logic, and nobody knows what the legacy app does.”
  • Auditability: Policy changes go through pull requests and code reviews. Every decision can be logged and the 4-eyes principle can be followed.
  • Speed: Update a policy without redeploying a single microservice.
  • Coverage: Use the same engine for Kubernetes, Terraform, API Gateways, Linux SSH, and cloud resource managers.

The Cloud Native Computing Foundation (CNCF) adopted OPA in 2018, and it has since become a cornerstone of cloud-native security and governance. Companies like Netflix, Capital One, and T-Mobile rely on it to enforce everything from PCI DSS compliance to internal cost controls.

How does Open Policy Agent (OPA) work?

The diagram below shows how OPA worksĀ  by decoupling policy decision-making from application code. It highlights how OPA answers queries based on code (Rego) and context (JSON serialized), without embedding policy logic inside the service itself.

On the diagram below, a Client sends a Request to a Service (such as an API, microservice, or Kubernetes component). The Service then issues a Query to OPA, which contains two optional parts: Policy – the rules written in OPA’s declarative language, and Data (JSON) – external information like user roles or resource attributes.

OPA evaluates the query against the loaded policies and data, producing a Decision (any JSON value, oftenĀ {“result”: true/false}Ā or structured data). The Service uses that decision to enforce authorization (allow/deny) or pass it back as the Response to the Client.

OPA_open_policy_Agent

Policy-as-Code: The Shift from Wiki Pages to Executable Rules

Traditional policy management was like: a security team writes a long-long PDF titled “Cloud Resource Tagging Guidelines” and emails it to all engineers. Developers might read it or just look through. And the compliance tick is checked manually once a quarter. It’s slow, error-prone, and guarantees that not all policies will be followed.

Policy-as-code challenges that. You encode rules in Rego, check them into Git, run them automatically against everyĀ terraform planĀ plan orĀ kubectl apply, and block non-compliant changes before they reach production. Missing tags can result in a failed pipeline, which actually saves the time to investigate after to whom the resources belong to and what they do.

This is especially important for organizations practicing FinOps, enforcing supply chain security, or meeting regulatory requirements like SOC2, GDPR or PCI DSS.Ā 

Rego: The Language of OPA

When engineers first hear “you’d need to learn a new policy language,” they often groan. But Rego is intentionally simple, designed specifically for hierarchical structured data (JSON). If you’ve ever written a basic if statement or a SQL query, you’ll pick it up quickly.

Let’s see a real example.

Imagine you want to enforce: “All pods in Kubernetes must have resource limits (CPU and memory) defined.” Which is the best practice and should be followed for productive and non-productive workloads.

Here’s how you’d write that in Rego for OPA Gatekeeper (the Kubernetes integration):

package k8sresourcelimits

violation[{"msg": msg}] {
    container := input.review.object.spec.containers[_]
    not container.resources.limits.cpu
    msg := sprintf("Container '%v' is missing CPU limits", [container.name])
}

violation[{"msg": msg}] {
    container := input.review.object.spec.containers[_]
    not container.resources.limits.memory
    msg := sprintf("Container '%v' is missing memory limits", [container.name])
}

What’s happening here?

  1. The package statement namespaces the policy.
  2. violation rules return an error message if their conditions are true.
  3. The _ (wildcard) iterates over all containers in the pod.
  4. If a container lacks resources.limits.cpu or resources.limits.memory, OPA returns a custom policy violation message.
  5. Kubernetes’ admission controller then blocks the pod creation and shows that message to the engineer who tries to deploy stuff.

This is infinitely more maintainable than hardcoding checks in every deployment script.

meme_me_before_opa

OPA in Action: 4 Practical Use Cases for Your Stack

Let’s explore how Open Policy Agent solves real problems today. Here are four ways you can deploy immediately. The first two are specific to your operational needs.

1. Enforce Internal Container Registry for all images

Problem: Developers might pull images from Docker Hub or other public registries. Those images could contain unpatched vulnerabilities, malicious code, or simply violate your organization’s security policy of using only scanned, approved base images. The number of such supply chain attacks has grown a lot in the past few years.

Solution: Write a Gatekeeper policy that inspects each pod’sĀ image field and blocks any deployment where the image is not from your internal registry (e.g.,Ā registry.internal.company.com).

Rego Example:

package k8sallowedregistry

violation[{"msg": msg}] {
    container := input.review.object.spec.containers[_]
    not startswith(container.image, "registry.internal.company.com/")
    msg := sprintf("Image '%v' not from internal or allowed container registry", [container.image])
}

Now, any attempt to run nginx:latest from DockerHub gets denied, with a clear error message. This ensures every container passes through your vulnerability scanner before it ever runs in production.

2. Enforce Metadata Requirements (Ownership, Cost Center, etc.)

Problem: You have 100+ microservices and no one knows for sure which team owns what. When something breaks at 3 AM, the on-call engineer has no idea which team to contact. Or your finance department needs to allocate cloud costs, but half the namespaces lack aĀ cost_centerĀ label.

Solution: Require that every namespace (or even K8s deployment) includes specific labels, such asĀ owner,Ā team, orĀ cost_center. Use OPA to reject any resource creation that misses these required metadata fields.

Rego Example:Ā Ā 

package k8srequiredlabels

violation[{"msg": msg}] {
    metadata := input.review.object.metadata
    not metadata.labels.owner
    msg := "Every resource must have an 'owner' label"
}

violation[{"msg": msg}] {
    metadata := input.review.object.metadata
    not metadata.labels.cost_center
    msg := "Every resource must have a 'cost_center' label"
}

This policy directly enables the FinOps practices we discussed in our Cloud Cost Optimization in 2026 post. Without cost_center labels, chargeback and showback become guesswork. OPA turns that requirement from a “best practice” into an enforced gate.

With this policy, your platform or DevOps team no longer chases developers for missing labels. OPA simply blocks the deployment until the developer adds the required metadata — shifting compliance directly into the standard workflow.

3. Terraform Compliance (conftest)

Problem: Developers provision cloud resources with missing tags, oversized instances, or public S3 buckets, running up costs and creating security holes.

Solution: Use conftest, a CLI tool that evaluates Terraform plans against OPA policies. In your CI pipeline (GitHub Actions, GitLab CI, etc.), runĀ conftest test plan.jsonĀ before everyĀ terraform apply. Policies can require:

  • Every resource hasĀ Owner andĀ Environment tags.
  • No oversized VMs in development accounts.
  • S3 buckets haveĀ block_public_acls = true.

A failed policy breaks the build, preventing it from reaching production and generating security issues or skyrocketing monthly cloud bills.

4. API Authorization (Envoy + OPA)

Problem: Your API gateway needs fine-grained access control — not just “admin vs user,” but “user from deptĀ foobar can performĀ GET but notĀ POST requests to /sales-data.”

Solution: Integrate OPA with Envoy proxy using the Envoy External Authorization filter. Envoy sends each request (with headers, JWT claims, etc.) to OPA; OPA evaluates the policy and returnsĀ allow orĀ deny. The best part? You can update authorization rules on the fly without restarting Envoy or your services!

OPA vs. Kyverno: Which Policy Engine Should You Choose?

Kyverno is another popular CNCF project designed specifically for Kubernetes. Unlike OPA’s general-purpose approach, Kyverno speaks native Kubernetes YAML via added Custom Resource Definitions (CRDs). Here’s how those two compare:

Feature OPA (with Gatekeeper) Kyverno

Primary Scope

General-purpose (any system: K8s, Terraform, Envoy, cloud APIs)

Kubernetes-only

Policy Language

Rego (declarative, functional, powerful, but new syntax)

YAML + JSON (reuses Kubernetes concepts)

Learning Curve

Moderate – requires learning Rego and policy structure

Low – if you already write Kubernetes manifests, you’re 80% there

Mutation / Generation

Limited (primarily validation-focused; mutation requires extra work)

Native support for mutating resources (e.g., adding labels, sidecars)

Background Scans

Available via Gatekeeper’s audit mode

Built-in, can report violations without blocking admission

Policy Library

Smaller out-of-box, but extensible

Larger set of ready-to-use policies (pod security, labels, image checks)

Performance

Very fast; policies compile to decision trees

Very fast; uses in-memory cache

When to choose OPA: You need a single policy engine across multiple systems — Kubernetes, Terraform, Envoy, cloud APIs (AWS IAM, GCP), and even CI pipelines. OPA’s generality means you learn Rego once and reuse it everywhere.

When to choose Kyverno: You are 100% Kubernetes-native and want YAML-based policies with mutation capabilities. Kyverno feels like writing Kubernetes resources, which many teams might find easier.

Can you use both? Yes. Some organizations use OPA for cross-cutting, multi-system policies (e.g., ā€œS3 buckets must be encryptedā€) and Kyverno for Kubernetes-specific rules (e.g., ā€œautomatically inject a Istio sidecarā€). They are not mutually exclusive albeit OPA is more universal.

Getting Started: Your First OPA Policy in 10 Minutes

Ready to try Open Policy Agent (OPA) yourself? Follow these simple steps.

Step 1: Install OPA

# macOS
brew install opa

# Linux
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64
chmod +x ./opa
sudo mv ./opa /usr/local/bin/opa

Step 2: Write Your First Rego Policy

Create a fileĀ test.rego:Ā 

package authz

default allow = false

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

allow {
    input.user.role == "admin"
}

Step 3: Test It

Save an input fileĀ input.json:

{
    "method": "GET",
    "path": ["v1", "public"],
    "user": {"role": "viewer"}
}

Run:

opa eval --data test.rego --input input.json "data.authz.allow"

If everything works, OPA returnsĀ true. Change the method toĀ “POST”Ā and watch it returnĀ false.

From here, integrate with conftest, Gatekeeper, or your own services.

Best Practices for OPA Adoption

Before you go all-in, a few tips from the trenches:

  1. Start small, then expand. First, enforce tagging on a single Terraform module. Then add Kubernetes namespace policies. Then tackle API auth.
  2. Treat policies like code. Store them in Git, write tests, run them in CI, and require code reviews.
  3. Use OPA’s tooling. TheĀ opa testĀ command lets you unit-test policies. TheĀ opa benchĀ command helps performance-test them.
  4. Plan for data scaling. OPA can load external data (user roles, resource labels) via bundles or the REST API. For large datasets, use partial evaluation or push data into OPA.
  5. Don’t boil the ocean. You don’t need to replace every custom auth check overnight. Start with high-risk areas: privileged containers, public buckets, cost explosions.Ā 

Conclusion: Policy-as-Code Is Not Optional in 2026

In 2026, “move fast and break things” is no longer acceptable when one misconfigured bucket can leak millions of records or a single oversized VM can cost $10,000 over a weekend. Open Policy Agent (OPA) gives you the ability to move fast without breaking things by encoding your organization’s wisdom, rules, and boundaries into executable, version-controlled policies.

Whether you’re running Kubernetes, Terraform, Envoy, or a full private cloud stack, OPA provides a consistent, auditable, and developer-friendly way to say “yes” or “no” with confidence.

And if you’re building that stack on c12n private cloud, you’ll have a secure, automated, fully open foundation — with the freedom to choose your own governance tools, including OPA.

Ready to Take Full Control of Your Private Cloud?

Don’t let policy chaos slow you down or put you at risk. Contact us for a demo to see how Cloudification can help you build a private cloud that is secure, scalable, and truly yours with Kubernetes, OpenStack, Ceph and ArgoCD.

šŸ“Ø Get in touch

šŸ“š Browse more topics in our Cloud Blog

Blog > Cloud > Policy as Code: Enforcing Security and Compliance with Open Policy Agent (OPA)
Let's Get Social: