Enforce Policy using Open Policy Agent

Open Policy Agent (OPA) Plugin

EnRoute integrates with Open Policy Agent (OPA) to enforce policy on the incoming request.

Enforcing policy using OPA provides clear separation of policy without embedding it in code.

OPA integration is achieved using a plugin in EnRoute. Attaching the OPA plugin to a GatewayHost results in enforcing policy for the application associated with the GatewayHost

Quick Introduction to OPA

The root of OPA evaluation tree is a query that evaluates a chain of OPA rules. OPA rules are defined in a namespaced OPA module.

Here is a simple example of OPA module defined in package opa.auth -

package opa.auth

# Access request inside envoy CheckRequest
import input.attributes.request.http as http_request
import input.parsed_path

default allow = false

allow {
    http_request.host == "helloenroute.enroutedemo.com"
    parsed_path[0] == "v1/helloenroute/get"
    http_request.method == "GET"
}

allow {
    http_request.host == "helloenroute.enroutedemo.com"
    parsed_path[0] == "v1/helloenroute/post"
    http_request.method == "POST"
    http_request.headers["X-Fowarded-For"][_] == "10.0.1.1"
}

Query

# Query rooted at ```data.opa.auth.allow``` (for package ```opa.auth```)
data.opa.auth.allow

The above construct provides a way to whitelist requets. Everything is blocked except -

  • request is destined to host helloenroute.enroutedemo.com AND path is v1/helloenroute/get AND request method is GET OR
  • request is destined to host helloenroute.enroutedemo.com AND path is v1/helloenroute/post AND request method is POST and the X-Forwarded-For header is set to 10.0.1.1

The OPA Section on Policy Evaluation describes this in more detail.

JWT System Diagram

EnRoute OPA
  • On an incoming request, Envoy makes a request to get a policy decision
  • OPA evaluates policy and returns a result

OPA Filter Configuration

OPA Filter Config for KubernetesOPA Filter Config for Standalone
---
apiVersion: enroute.saaras.io/v1beta1        
kind: HttpFilter
metadata:
  name: global_opa_filter
  namespace: enroute-gw-k8s
spec:
  name: global_opa_filter
  type: http_filter_opa
  routeFilterConfig:
    config: |

# Begin OPA Rego config
package opa.auth

# Access request inside envoy CheckRequest
import input.attributes.request.http as http_request
import input.parsed_path

default allow = false

allow {
    http_request.host == "helloenroute.enroutedemo.com"
    parsed_path[0] == "v1/helloenroute/get"
    http_request.method == "GET"
}

allow {
    http_request.host == "helloenroute.enroutedemo.com"
    parsed_path[0] == "v1/helloenroute/post"
    http_request.method == "POST"
    http_request.headers["X-Fowarded-For"][_] == "10.0.1.1"
}
# Variables for opa global filter
FILTER_NAME="global_opa_filter"
FILTER_TYPE="http_filter_opa"
FILTER_CONFIG='

# Begin OPA Rego config
package opa.auth

# Access request inside envoy CheckRequest
import input.attributes.request.http as http_request
import input.parsed_path

default allow = false

allow {
    http_request.host == "helloenroute.enroutedemo.com"
    parsed_path[0] == "v1/helloenroute/get"
    http_request.method == "GET"
}

allow {
    http_request.host == "helloenroute.enroutedemo.com"
    parsed_path[0] == "v1/helloenroute/post"
    http_request.method == "POST"
    http_request.headers["X-Fowarded-For"][_] == "10.0.1.1"
}

'

curl -s -X POST localhost:1323/filter         \
        -d "Filter_name=${FILTER_NAME}"       \
        -d "Filter_type=${FILTER_TYPE}"       \
        -d "Filter_config"="${FILTER_CONFIG}"

Notes

OPA is an enterprise plugin