Open Policy Agent (OPA) and Rego Tutorial
An in-depth, hands-on guide to learning Open Policy Agent and the Rego language for unified policy enforcement across your stack.
What You'll Learn
๐ Prerequisites
- Basic understanding of JSON, YAML, and REST APIs
- Familiarity with the command line
- Docker installed (for running OPA server easily)
- A use case in mind (e.g., microservices, Kubernetes, or Terraform)
๐ท๏ธ Topics Covered
What is Open Policy Agent (OPA)? Complete Beginner's Guide to Policy Engines
Open Policy Agent (OPA) is an open-source, general-purpose policy engine. It unifies policy enforcement across different technologies and systems. Think of it as a central brain for making decisions. Instead of scattering authorization and policy logic throughout your application code, you offload those decisions to OPA.
Your service queries OPA with a JSON object (the input), and OPA returns a JSON object (the decision). This makes your services simpler and your policies easier to manage, test, and audit.
OPA Engine
The core component that evaluates policies. You can run it as a standalone binary, a Docker container, or as a library embedded in your Go application.
Rego Language
The high-level declarative language used to write policies for OPA. Rego was inspired by Datalog and is designed specifically for querying complex data structures.
Policy & Data Caching
OPA can be configured to pull policy and data from remote bundles, keeping its local cache up-to-date for fast, local decisions without network latency.
Rego Language Tutorial: Master OPA Policy Syntax and Functions
To master OPA, you must learn Rego. Let's start with the basics. Create a directory for our project, and add two files: policy.rego and data.json.
data.json
{
"roles": {
"alice": ["admin", "developer"],
"bob": ["developer"]
},
"servers": [
{"id": "app_server_1", "protocol": "https", "ports": [80, 443]},
{"id": "db_server_1", "protocol": "tcp", "ports": [5432]}
]
}policy.rego
package tutorial
# By default, deny access
default allow = false
# Allow if the user is an admin
allow = true {
input.user == "alice"
}
# Also allow if the input server uses https
allow = true {
some i
server := data.servers[i]
server.id == input.server_id
server.protocol == "https"
}
# Generate a list of all server IDs
server_ids = [server.id | some i; server := data.servers[i]]
# Check if a user has a specific role
user_has_role(user, role) {
data.roles[user][_] == role
}Now, let's use the opa eval command to query our policy. The --input flag provides the query context, and the --data flag loads our data file.
Run OPA Evaluation
# Test if alice is allowed (should be true)
$ opa eval --data data.json --input '{"user": "alice", "server_id": "app_server_1"}' 'data.tutorial.allow'
{
"result": [
{
"expressions": [
{
"value": true,
"text": "data.tutorial.allow",
"location": { "row": 1, "col": 1 }
}
]
}
]
}
# Test if bob is allowed on the db server (should be false)
$ opa eval --data data.json --input '{"user": "bob", "server_id": "db_server_1"}' 'data.tutorial.allow'
{
"result": [
{
"expressions": [
{
"value": false,
"text": "data.tutorial.allow",
"location": { "row": 1, "col": 1 }
}
]
}
]
}
# Test our custom function
$ opa eval --data data.json 'data.tutorial.user_has_role("bob", "developer")'
# ... returns true
Kubernetes Policy Enforcement with OPA Gatekeeper: Step-by-Step Guide
A primary use for OPA is enforcing policies on Kubernetes clusters via OPA Gatekeeper. Gatekeeper uses two custom resources: ConstraintTemplate (which contains the Rego logic) and a Constraint (which applies the template to specific resources).
Here is a ConstraintTemplate that ensures all namespaces have an owner label.
namespace-label-template.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
properties:
labels:
type: array
items:
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("You must provide labels: %v", [missing])
}Now, we create a Constraint to apply this logic, requiring the owner label on all namespaces.
owner-label-constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-owner
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels:
- "owner"After applying these with kubectl apply -f ., any attempt to create a namespace without the owner label will be rejected by the Kubernetes API server.
Terraform Policy Validation with OPA: Prevent Infrastructure Misconfigurations
You can use OPA to validate a Terraform plan file to catch misconfigurations before they are applied. First, generate a plan and convert it to JSON.
Generate Terraform Plan JSON
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.jsonNow, write a Rego policy to check the plan. This policy denies any AWS S3 bucket that doesn't have block public access enabled.
terraform_aws.rego
package terraform.aws
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
resource.mode == "managed"
# Check for both create and update actions
actions := {"create", "update"}
actions[resource.change.actions[_]]
# Check if the block_public_acls is not set to true
not resource.change.after.block_public_acls == true
msg := sprintf("S3 bucket '%s' must have block_public_acls enabled.", [resource.address])
}Finally, run OPA against the plan file. If there are any violations, OPA will return the deny messages.
Validate Plan with OPA
opa eval -f pretty -d terraform_aws.rego -i tfplan.json "data.terraform.aws.deny"๐ Congratulations!
Tutorial Complete
You have completed this in-depth OPA tutorial. You now have the foundational skills to:
Write Declarative Policies
Write declarative policies in the Rego language.
Decouple Policy Decisions
Decouple policy decisions from your services.
Enforce Custom Policies
Enforce custom policies for APIs, Kubernetes, and Terraform.
Build Unified Strategy
Begin building a unified policy-as-code strategy for your organization.