Rego#

Rego is the policy language of Open Policy Agent (OPA). A declarative, Datalog-derived language for “is this allowed?” decisions across cloud-native systems.

Used as the policy engine for Kubernetes admission control (OPA Gatekeeper, Kyverno’s competitor), service-mesh authorization (Istio, Envoy via ext_authz), CI/CD (Conftest), Terraform plan validation, microservice authz, and feature flags.

The Policy Mental Model#

OPA’s world has four moving parts: input describes the request, data holds supporting state, policy is the Rego rules that compute decisions, and the decision is whatever value the rules produce at a queried path. Everything else in OPA is plumbing around that core.

  • Input, a JSON document representing the request to evaluate.

  • Data, supporting state loaded into OPA (users, roles, allowed hosts).

  • Policy, Rego rules; produce decisions (true / false / objects).

  • Decision, the value at a queried path (e.g. data.authz.allow).

OPA evaluates the policy against input and data, returning whatever the rules compute.

A Minimal Policy#

package authz

default allow := false

# Allow if the user is in the admins list
allow if {
  input.user in data.admins
}

# Allow read for the resource owner
allow if {
  input.method == "GET"
  input.user == input.resource.owner
}

# Deny everything else (the default)

Building Blocks#

The vocabulary of a Rego file. Packages namespace; rules pair a head with a body; defaults supply a fallback; not is negation-as-failure; some and every quantify. Together these primitives compose into policies that look declarative even when the underlying logic is intricate.

  • package <name>, namespace for rules.

  • import <pkg>, bring in another package’s rules.

  • Rules, head { body } (Rego v0) or head if { body } (v1).

  • Variables start uppercase; identifiers stay lowercase.

  • default x := v, the value of x if no rule matches.

  • not, negation as failure.

  • some x in xs, existential quantification.

  • every x in xs { ... }, universal.

Sets, Lists, and Objects#

# Comprehensions
admin_users := { u | u := data.users[_]; u.role == "admin" }
user_emails := [ u.email | u := data.users[_] ]
role_users  := { role: users |
  role := data.users[_].role
  users := [u.name | u := data.users[_]; u.role == role]
}

Common Use Cases#

The kinds of work where Rego is the standard tool. Kubernetes admission control via Gatekeeper, service-mesh authorization through Envoy ext_authz, and Terraform plan validation via Conftest are the three most common; each uses the same language with a different input schema.

Kubernetes admission control (Gatekeeper).

package k8spsphardening

violation[{"msg": msg}] if {
  input.review.object.kind == "Pod"
  some c
  c := input.review.object.spec.containers[_]
  not c.securityContext.runAsNonRoot
  msg := sprintf("container %v must runAsNonRoot", [c.name])
}

Service-to-service authorization (Envoy ext_authz).

package envoy.authz

default allow := false

allow if {
  input.attributes.request.http.method == "GET"
  input.attributes.request.http.path == "/health"
}

allow if {
  token := bearer_token
  claims := io.jwt.decode_verify(token, {
    "secret": "...", "iss": "auth.example.com"
  })
  claims.role == "service"
}

Conftest / Terraform plan validation:

package main

deny[msg] if {
  resource := input.resource_changes[_]
  resource.type == "aws_s3_bucket"
  resource.change.after.acl == "public-read"
  msg := sprintf("S3 bucket %v is public", [resource.address])
}

Functions and Built-Ins#

Rego ships hundreds of built-in functions.

Built-in

Use

startswith / endswith / contains

string predicates

split / concat / sprintf

string ops

json.unmarshal / yaml.unmarshal

parse data

http.send

HTTP request from policy (carefully)

crypto.sha256 / crypto.x509.parse_*

hashing / cert parsing

io.jwt.decode_verify

JWT validation

net.cidr_contains

IP-range checks

time.parse_rfc3339_ns / time.now_ns

time

regex.match

regex

rego.metadata.rule()

access rule metadata

OPA in Production#

The deployment patterns that make OPA practical at scale. Sidecar gives every service a local policy decision point; library mode embeds OPA in-process; bundles distribute signed policy and data; decision logs ship every evaluation to a SIEM for audit. Latencies are sub-millisecond.

  • Sidecar, run OPA next to each service, query via REST or gRPC.

  • Library, some languages embed OPA / Rego directly (Go, Wasm).

  • Bundles, distribute policy + data as signed bundles to all OPA instances.

  • Decision logs, ship every decision to a SIEM for audit.

Latencies are typically sub-millisecond for evaluating a request against thousands of rules; OPA pre-compiles policies into a virtual machine.

Tooling#

The CLIs and integrations that surround Rego. The opa binary handles eval, test, fmt, and bench; Conftest tests config files; Gatekeeper is the Kubernetes admission controller; Regal lints; Styra DAS manages OPA at scale. VS Code extensions round out the authoring story.

  • opa, the binary. opa eval, opa test, opa fmt, opa bench.

  • Conftest, test config files (Kubernetes YAML, Terraform plans, Dockerfiles) against Rego.

  • Gatekeeper – Kubernetes admission controller.

  • Styra DAS, commercial OPA management.

  • Regal, Rego linter.

  • VS Code extension for syntax highlighting and opa test integration.

Testing#

Rego has a built-in test framework.

package authz_test

import data.authz

test_admin_can_do_anything if {
  authz.allow with input as {"user": "operator", "method": "POST"}
              with data.admins as ["operator"]
}

test_unknown_denied if {
  not authz.allow with input as {"user": "stranger"}
}
$ opa test policies/ -v

Strengths#

The reasons OPA wins as the cross-cutting policy choice. One language across many decision points, a compiled VM for sub-millisecond evaluation, integrations spanning Kubernetes through Terraform, and signed bundles plus structured decision logs that satisfy auditors.

  • General-purpose policy, one language, many decision points.

  • Fast, compiled VM; sub-millisecond evals.

  • Ecosystem, Kubernetes, Envoy, Terraform, Kafka, OCI registries all have integrations.

  • Auditable, decisions are deterministic; bundles are signed; decision logs are structured.

Weaknesses#

The flip side. Datalog-flavored declarative semantics confuse imperative-trained developers; variable scoping rules surprise; debugging is primitive next to a real IDE; and the v0/v1 split needs explicit version pinning until the ecosystem fully migrates.

  • Learning curve, declarative + Datalog-flavored is unfamiliar.

  • Variable scoping rules surprise newcomers.

  • Debug story, trace and print exist but feel primitive.

  • Two language versions, v0 (default in older OPA) and v1; subtle syntax differences. Pin a version.

Alternatives#

The other policy languages an operator may meet. Sentinel covers HashiCorp products; Cedar is the AWS-developed authorization language; Kyverno is Kubernetes-native; Polar and Casbin compete for application-level authz. Each wins in its niche; Rego remains the cross-cutting bet.

  • HCL, Sentinel is HashiCorp’s policy language for Terraform / Vault / Nomad; HCL-based.

  • Cedar, AWS-developed authorization language; simpler than Rego, focused on application authz.

  • Kyverno, Kubernetes-native; YAML-based policies; popular alternative to Gatekeeper.

  • OSO Polar, application authz language.

  • Casbin, lightweight authz library with multiple policy languages.

For Kubernetes-only policy, Kyverno is often easier; for cross-cutting policy across many systems, Rego/OPA is the durable bet.

See Also#