/oci-pac-opa

A sample policy-as-code solution for OCI, using Open Policy Agent

Primary LanguageOpen Policy AgentUniversal Permissive License v1.0UPL-1.0

Policy-as-Code on OCI Using Open Policy Agent

License: UPL Quality gate

Introduction

This is a sample solution using Open Policy Agent (OPA) to validate Terraform proposed changes (plans) against a predetermined policy (defined in OPA) that is built for OCI resources.

Solution

Open Policy Agent (OPA) is a great solution for this challenge. OPA evaluates JSON (not HCL/HCL2), which yields itself to being really flexible/powerful. Whether querying JSON output from Terraform or JSON data returned from calling the OCI API directly, OPA is pretty versatile.

There are two separate and distinct solutions provided:

  • OCI Terraform Validation
  • OCI API Body Validation

Each of these use-cases are discussed in greater detail below. Before that, let's look at the high-level advantages/disadvantages (largely applicable to both solutions).

Advantages

  • Flexible deployment model - can run as API service, container or binary exec on instance
  • Backed by Cloud Native Computing Foundation project, which means it'll hopefully be around for awhile
  • Powerful rules engine (Rego) which allows for creation of simple and complex validation rules
  • Built-in testing framework

Disadvantages

  • Rego is powerful, but also a bit complex and unnatural (initial ramp might be steep)

Getting Started

Each solution has specific use-cases and nuances to running, so please look at the OCI Terraform Validation README and/or at the OCI API Body Validation README for more information (specific to each use-case).

Interactive console

This is largely universal to both solutions. To get to an interactive OPA console:

docker run -it \
  --mount type=bind,readonly,source="$(pwd)"/input_tfplan.json,target=/input.json \
  openpolicyagent/opa:0.23.2 run repl.input:input.json

Change input_tfplan.json to whatever input JSON filename you'd like to use.

Here's an example of what might be done to interactively troubleshoot a policy:

docker run -it \
  --mount type=bind,readonly,source="$(pwd)"/oci_policy.rego,target=/policy.rego \
  --mount type=bind,readonly,source="$(pwd)"/example/oci_vars-pass.rego,target=/vars.rego \
  openpolicyagent/opa:0.23.2 run vars.rego policy.rego

This will effectively open up an interactive console with the policy and variables prepopulated, allowing you to troubleshoot scenarios. Don't forget to change to the correct package!

> package oci.tf_analysis

Here's an example of feeding some data in:

> test_input
{
  "planned_values": {
    "root_module": {
      "resources": [
        {
          "address": "oci_core_security_list.test_acl",
          "mode": "managed",
          "name": "test_acl",
          "provider_name": "oci.phx",
          "schema_version": 0,
          "type": "oci_core_security_list",
          "values": {
            "compartment_id": "ocid1.compartment.oc1..abc123",
            "display_name": "test_acl",
            "ingress_security_rules": [
              {
                "icmp_options": [],
                "protocol": "17",
                "source": "10.0.4.3/32",
                "source_type": "CIDR_BLOCK",
                "stateless": true,
                "tcp_options": [],
                "udp_options": [
                  {
                    "max": 123,
                    "min": 123,
                    "source_port_range": [
                      {
                        "max": 123,
                        "min": 123
                      }
                    ]
                  }
                ]
              }
            ]
          }
        }
      ]
    }
  }
}
> auth_errors with input as test_input
[
  "ERROR - The following Security List ingress rules are not allowed:\n\n{\"icmp_options\": [], \"protocol\": \"17\", \"source\": \"10.0.4.3/32\", \"source_type\": \"CIDR_BLOCK\", \"stateless\": true, \"tcp_options\": [], \"udp_options\": [{\"max\": 123, \"min\": 123, \"source_port_range\": [{\"max\": 123, \"min\": 123}]}]}."
]
>

Unfortunately trace() doesn't appear to work within unit tests, so this can be a helpful way of getting the exact data returned for a given scenario (especially if you're matching against exact return values as some of the unit tests do, such as looking at the error message returned).

Lastly, here's an idea if you'd like to interactively debug the JSON in a file:

docker run -it \
  --mount type=bind,readonly,source="$(pwd)"/oci_policy.rego,target=/policy.rego \
  --mount type=bind,readonly,source="$(pwd)"/example/oci_vars-pass.rego,target=/vars.rego \
  --mount type=bind,readonly,source="$(pwd)"/example/tfplan.json,target=/input.json \
  openpolicyagent/opa:0.23.2 run vars.rego policy.rego repl.input:input.json

Prerequisites

  • Docker Desktop installed and available
    • docker pull openpolicyagent/opa:latest to download the OPA container
    • Better yet, be explicit on the version pulled: docker pull openpolicyagent/opa:0.23.2
  • MacOS/Linux paths are used in this example (Windows users might need to change the path format)
  • Git repo is cloned locally

Open Policy Agent Version

This solution is compatible with OPA v0.23.2. This solution (or variations of it) have been used with older versions (back to OPA v0.15.0), though it's typically best to use the latest compatible version.

Notes/Issues

There's always room for improvement! Since this is just an example, it'll require you to adapt it to your specific needs and use-case(s). Please file an issue for any suggested improvement.

URLs

Contributing

This project is open source. Please submit your contributions by forking this repository and submitting a pull request! Oracle appreciates any contributions that are made by the open source community.

License

Copyright (c) 2021 Oracle and/or its affiliates.

Licensed under the Universal Permissive License (UPL), Version 1.0.

See LICENSE for more details.