During a recent audit of a large-scale Kubernetes deployment, we identified that approximately 14% of Terraform-managed resources contained configurations that violated internal security baselines. These weren't overt vulnerabilities but subtle misconfigurations: unencrypted EBS volumes, S3 buckets with public read access, and IAM roles with overly permissive "Shadow Admin" capabilities. Manual code reviews failed to catch these because the infrastructure state was too complex for human reviewers to track across thousands of lines of HCL (HashiCorp Configuration Language).
What is Rego Policy as Code?
Rego is a declarative query language designed specifically for the Open Policy Agent (OPA). Unlike imperative languages where you define how to check a condition, Rego allows you to define what the state should be. It is inspired by Datalog, a subset of Prolog, which makes it highly efficient for querying complex hierarchical data structures like JSON and YAML.
In the context of Infrastructure as Code (IaC), Rego treats your infrastructure definitions as data. When you run a Terraform plan and export it to JSON, Rego queries that JSON to identify non-compliant resources. This shift from manual "checklists" to automated "policies" allows security teams to enforce compliance at the pull-request stage, long before a single resource is provisioned in the cloud.
The Evolution of Governance: From Manual Checks to Automated Policy
Traditional governance relied on periodic audits and manual spreadsheets. In a modern DevOps environment, this approach is fundamentally broken. By the time an auditor identifies a non-compliant security group, that resource might have been exposed to the public internet for weeks.
Automated policy evaluation integrates directly into the developer workflow. We are seeing a transition where compliance is no longer a "gate" at the end of the development cycle but a continuous process. For Indian enterprises, this is particularly critical, especially when defending against industrialized botnets targeting regional infrastructure, as the Digital Personal Data Protection (DPDP) Act 2023 mandates strict data processing and storage requirements. Automated Rego policies ensure that every deployment adheres to these legal frameworks by default.
Why Modern DevOps Teams are Adopting Open Policy Agent (OPA)
OPA has become the industry standard because it decouples policy logic from the application or infrastructure tool. Whether you are securing a Kubernetes cluster, an Envoy proxy, or a Terraform pipeline, the policy language remains the same.
- Consistency: Use the same Rego language for API authorization, secure SSH access for teams, and IaC validation.
- Portability: OPA runs as a lightweight sidecar, a standalone service, or a CLI tool.
- Performance: OPA caches policies in memory, allowing for sub-millisecond decision-making.
What is Open Policy Agent (OPA)?
OPA is a CNCF graduated project that serves as a general-purpose policy engine. It accepts a JSON input, evaluates it against a set of Rego policies, and returns a JSON response. This architecture allows OPA to be "tool-agnostic." It doesn't care if the input comes from a Terraform plan, a Kubernetes Admission Review, or a custom Python microservice.
I have found that OPA’s strength lies in its ability to handle "Context-Aware" policies. For example, a policy might allow an S3 bucket to be public if it is tagged as "Public-Assets" but deny it for any other tag. This requires the engine to look at multiple data points simultaneously, which OPA handles through its unified data model.
How Rego Functions as a Declarative Query Language
Rego works by searching for sets of values that satisfy the conditions defined in your rules. If a rule's conditions are met, the rule is "true" or "defined." In the context of security, we typically write "deny" rules. If OPA finds any resource that matches the criteria in a "deny" block, the deployment is blocked.
One common point of confusion for developers is how Rego handles iteration. Rego uses the [_] operator to iterate over arrays. Instead of writing a for loop, you define a statement that must be true for any element in the array. This makes the code significantly more concise than equivalent logic in Python or Go.
The Benefits of Decoupling Policy from Application Logic
When policy is hardcoded into an application, changing a compliance requirement (like increasing password complexity or restricting cloud regions) requires a full re-compile and re-deploy of the service. By decoupling policy, you can update your Rego files in a central repository and OPA will pick up the changes without any downtime for the underlying infrastructure.
This is vital for meeting RBI (Reserve Bank of India) guidelines for FinTechs. When the RBI issues a new directive regarding data localization or transaction limits, security teams can push a new Rego policy to the OPA engine, instantly enforcing the new rule across all microservices and infrastructure components.
Basic Syntax and Data Structures in Rego
Every Rego file begins with a package declaration. This creates a namespace for your rules. We then define rules that evaluate to either a boolean or a set of messages.
package terraform.analysis
default allow = false
Deny if EBS volume is not encrypted
deny[msg] { resource := input.resource_changes[_] resource.type == "aws_ebs_volume" resource.change.after.encrypted == false msg = sprintf("ASSET_NON_COMPLIANT: EBS volume %s must be encrypted.", [resource.address]) }
Deny if SSH (port 22) is open to 0.0.0.0/0
deny[msg] { resource := input.resource_changes[_] resource.type == "aws_security_group" ingress := resource.change.after.ingress[_] ingress.from_port <= 22 ingress.to_port >= 22 ingress.cidr_blocks[_] == "0.0.0.0/0" msg = sprintf("NETWORK_EXPOSURE: Security Group %s allows public SSH access.", [resource.address]) }
In the snippet above, the deny[msg] syntax creates a set of all error messages. If any resource matches the conditions, the msg is added to the set. If the set is not empty, the CI/CD pipeline should fail.
Kubernetes Admission Control Policy Examples
OPA is frequently used as an Admission Controller in Kubernetes. When a user sends a command like kubectl apply, the Kubernetes API server sends the request to OPA for validation.
We use this to prevent containers from running as root. This is a foundational security requirement to prevent container breakout attacks, often referenced in OpenSSH security best practices for broader infrastructure hardening.
package kubernetes.admission
deny[msg] { input.request.kind.kind == "Pod" container := input.request.object.spec.containers[_] container.securityContext.runAsNonRoot == false msg = sprintf("SECURITY_VIOLATION: Container %s in Pod %s must not run as root.", [container.name, input.request.object.metadata.name]) }
API Authorization and Microservices Security Patterns
For microservices, OPA can authorize requests based on JWT tokens or user roles. This moves authorization logic out of the code and into a manageable policy layer.
package api.authz
default allow = false
allow { input.method == "GET" input.path == ["v1", "public", "health"] }
allow { input.user.role == "admin" }
allow { input.user.role == "editor" input.method == "POST" input.path == ["v1", "content"] }
Infrastructure as Code (IaC) Validation with Rego
The most powerful use case for Rego today is IaC validation. We use it to enforce "Sovereign Cloud" requirements. For Indian organizations, this means ensuring that no data-sensitive resources are provisioned outside of the Mumbai (ap-south-1) or Hyderabad (ap-south-2) regions.
package terraform.compliance
allowed_regions = ["ap-south-1", "ap-south-2"]
deny[msg] { resource := input.resource_changes[_] region := resource.change.after.region not is_allowed(region) msg = sprintf("DPDP_VIOLATION: Resource %s is in unauthorized region %s. Only Mumbai/Hyderabad allowed.", [resource.address, region]) }
is_allowed(region) { region == allowed_regions[_] }
This policy directly addresses the DPDP Act 2023 requirements by preventing the accidental placement of personal data in foreign jurisdictions, which could lead to significant fines (up to ₹250 Crore under the act).
Automating Policy Checks during Deployment
To implement this in a real-world pipeline, we first need to convert the Terraform plan into a format OPA can read. Terraform plans are binary by default, so we use the terraform show command to output JSON.
Initialize and plan terraform
$ terraform init $ terraform plan -out=tfplan.binary
Convert the binary plan to JSON
$ terraform show -json tfplan.binary > tfplan.json
Evaluate the plan against our Rego policies
$ opa eval --data policy.rego --input tfplan.json "data.terraform.analysis.deny"
If the output is an empty array [], the plan is compliant. If it contains strings, the pipeline should exit with a non-zero code to block the deployment.
Testing Rego Policies: Using the 'opa test' Command
Writing policy is writing code, and code needs tests. OPA provides a built-in test runner that allows you to mock inputs and verify that your rules behave as expected. I recommend a 1:1 ratio of policy files to test files.
package terraform.analysis_test
import data.terraform.analysis
test_deny_unencrypted_ebs { mock_input = {"resource_changes": [{"type": "aws_ebs_volume", "change": {"after": {"encrypted": false}}}]} analysis.deny["ASSET_NON_COMPLIANT: EBS volume must be encrypted."] with input as mock_input }
Run the tests using the following command:
$ opa test ./policies -v data.terraform.analysis_test.test_deny_unencrypted_ebs: PASS (1.2ms) -------------------------------------------------------------------------------- PASS: 1/1
Monitoring and Auditing Policy Decisions
When OPA is running in server mode, it can be configured to log every decision it makes. These "Decision Logs" are invaluable for forensic analysis and can be integrated into a SIEM for real-time threat detection. If a security incident occurs, you can look back at the logs to see exactly which policy was in place and why OPA allowed or denied a specific request.
Start OPA in server mode with logging enabled
$ opa run --server --addr :8181 --log-level info
For Indian FinTechs, these logs serve as audit evidence for SEBI or RBI compliance checks, proving that automated controls were active at the time of deployment.
Clarifying the Term: Software Policy vs. Vehicle 'Rego'
In the cybersecurity community, "Rego" refers exclusively to the OPA policy language. However, in Australia and parts of New Zealand, "Rego" is the common shorthand for vehicle registration. This can lead to significant confusion when searching for technical documentation or troubleshooting errors.
When searching for OPA-related content, always include the keyword "Policy" or "OPA" to avoid results related to transport departments. If you see terms like "VIN," "License Plate," or "Plate Number," you are looking at vehicle registration data, not software policy.
Understanding Insurance Codes on Vehicle Registration
For those working in Australian tech sectors, you may encounter "Rego" in the context of fleet management software. In that domain, registration involves specific insurance codes that determine the level of coverage for a vehicle. This is entirely separate from the logical codes used in OPA Rego.
Does CTP Come with Rego? (Common Australian Registration FAQ)
A common question in the Australian context is whether Compulsory Third Party (CTP) insurance is included in the "Rego" fee. In New South Wales (NSW), you must purchase a "Green Slip" (CTP) separately before you can renew your Rego. In other states like Queensland, it is bundled.
In our cybersecurity context, think of "Rego" (the language) as the engine, and "OPA" as the vehicle. You need both to have a functioning compliance system. Just as you wouldn't drive an unregistered car, you shouldn't run production infrastructure without an automated policy engine.
Writing Performant and Readable Rego Code
Rego can become slow if you use too many nested iterations over large datasets. We optimize Rego by using "Indexed Lookups" instead of linear searches.
- Avoid heavy recursion: Rego supports recursion, but it is computationally expensive. Use built-in functions like
object.getoranywhere possible. - Use Helper Rules: Break complex logic into smaller, reusable rules. This improves readability and allows OPA to cache partial results.
- Minimize 'walk': The
walkfunction traverses entire JSON trees. Only use it when the structure of the input is unknown.
Version Control Strategies for Policy Files
Policy files should be treated with the same rigor as application code. We store all .rego files in a dedicated policy repository.
- Semantic Versioning: Tag policy releases (e.g.,
v1.2.0). This allows different environments to pin to specific policy versions. - Peer Review: Every change to a security policy must require approval from at least one security engineer and one DevOps engineer.
- Automated Testing: No policy should be merged into the
mainbranch unless allopa testcases pass in the CI pipeline.
The Future of Policy as Code in Cloud-Native Environments
We are moving toward a "Policy-First" world. Tools like conftest (based on OPA) are making it easier to test non-cloud files like Dockerfiles, HCL, and even documentation.
Using conftest to test a Dockerfile
$ conftest test Dockerfile --policy ./rego-policies FAIL - Dockerfile - Do not use 'latest' tag for base images
The integration of OPA with service meshes like Istio and Linkerd is also maturing. This allows for fine-grained, identity-based access control at the network layer, enforced by the same Rego policies that check your Terraform code.
Summary of Rego's Impact on Security and Compliance
Rego and OPA have fundamentally changed how we approach cloud security. By moving from reactive scanning to proactive policy enforcement, we reduce the "Mean Time to Remediation" to zero—the vulnerability is blocked before it is even created.
For Indian organizations, this automation is the most viable path to complying with the DPDP Act 2023. Manual compliance is not only slow but legally risky in an era of high-frequency deployments.
Next Steps for Getting Started with OPA
To begin implementing this in your own environment, start by auditing your current Terraform state. Export a plan to JSON and write a single Rego rule to check for a common misconfiguration, such as missing tags or unencrypted storage.
Be aware of known vulnerabilities documented in the NIST NVD. For instance, CVE-2022-23652 highlighted a risk in OPA's http.send function that could lead to SSRF if policies fetch external metadata from untrusted sources. Always keep your OPA binaries and Conftest versions updated to mitigate issues like CVE-2023-49568, which involved path traversal during policy pulling.
The most effective way to learn Rego is the OPA Playground (play.openpolicyagent.org). Paste your Terraform JSON there and start writing rules to see immediate feedback on your logic.
$ opa eval --input tfplan.json --data policy.rego "data.terraform.analysis.deny" --format pretty
