Skip to main content
Version: 2.13

opa

Description#

The opa plugin is used to integrate with Open Policy Agent. By using this plugin, users can decouple functions such as authentication and access to services and reduce the complexity of the application system.

Attributes#

NameTypeRequirementDefaultValidDescription
hoststringrequiredOpen Policy Agent service host (eg. https://localhost:8181)
ssl_verifybooleanoptionaltrueWhether to verify the certificate
policystringrequiredOPA policy path (It is a combination of package and decision. When you need to use advanced features such as custom response, decision can be omitted)
timeoutintegeroptional3000ms[1, 60000]msHTTP call timeout.
keepalivebooleanoptionaltrueHTTP keepalive
keepalive_timeoutintegeroptional60000ms[1000, ...]mskeepalive idle timeout
keepalive_poolintegeroptional5[1, ...]msConnection pool limit
with_routebooleanoptionalfalseWhether to send information about the current route.
with_servicebooleanoptionalfalseWhether to send information about the current service.
with_consumerbooleanoptionalfalseWhether to send information about the current consumer. (It may contain sensitive information such as apikey, so please turn it on only if you are sure it is safe)

Data Definition#

APISIX to OPA service#

The type indicates that the request type. (e.g. http or stream) The reqesut is used when the request type is http, it contains the basic information of the request. (e.g. url, header) The var contains basic information about this requested connection. (e.g. IP, port, request timestamp) The route, service, and consumer will be sent only after the opa plugin has enabled the relevant features, and their contents are same as those stored by APISIX in etcd.

{
"type": "http",
"request": {
"scheme": "http",
"path": "\/get",
"headers": {
"user-agent": "curl\/7.68.0",
"accept": "*\/*",
"host": "127.0.0.1:9080"
},
"query": {},
"port": 9080,
"method": "GET",
"host": "127.0.0.1"
},
"var": {
"timestamp": 1701234567,
"server_addr": "127.0.0.1",
"server_port": "9080",
"remote_port": "port",
"remote_addr": "ip address"
},
"route": {},
"service": {},
"consumer": {}
}

OPA service response to APISIX#

In the response, result is automatically added by OPA. The allow is indispensable and will indicate whether the request is allowed to be forwarded through the APISIX. The reason, headers, and status_code are optional and are only returned when you need to use a custom response, as you'll see in the next section with the actual use case for it.

{
"result": {
"allow": true,
"reason": "test",
"headers": {
"an": "header"
},
"status_code": 401
}
}

Example#

First, you need to launch the Open Policy Agent environment.

$ docker run -d --name opa -p 8181:8181 openpolicyagent/opa:0.35.0 run -s

Basic Use Case#

You can create a basic policy for testing.

$ curl -X PUT '127.0.0.1:8181/v1/policies/example1' \
-H 'Content-Type: text/plain' \
-d 'package example1

import input.request

default allow = false

allow {
# HTTP method must GET
request.method == "GET"
}'

After that, you can create a route and turn on the opa plugin.

$ curl -X PUT 'http://127.0.0.1:9080/apisix/admin/routes/r1' \
-H 'X-API-KEY: <api-key>' \
-H 'Content-Type: application/json' \
-d '{
"uri": "/*",
"plugins": {
"opa": {
"host": "http://127.0.0.1:8181",
"policy": "example1"
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'

Try it out.

# Successful request
$ curl -i -X GET 127.0.0.1:9080/get
HTTP/1.1 200 OK

# Failed request
$ curl -i -X POST 127.0.0.1:9080/post
HTTP/1.1 403 FORBIDDEN

Complex Use Case (custom response)#

Next, let's think about some more complex scenarios.

When you need to return a custom error message for an incorrect request, you can implement it this way.

$ curl -X PUT '127.0.0.1:8181/v1/policies/example2' \
-H 'Content-Type: text/plain' \
-d 'package example2

import input.request

default allow = false

allow {
request.method == "GET"
}

# custom response body (Accepts a string or an object, the object will respond as JSON format)
reason = "test" {
not allow
}

# custom response header (The data of the object can be written in this way)
headers = {
"Location": "http://example.com/auth"
} {
not allow
}

# custom response status code
status_code = 302 {
not allow
}'

Update the route and set opa plugin's policy parameter to example2. Then, let's try it.

# Successful request
$ curl -i -X GET 127.0.0.1:9080/get
HTTP/1.1 200 OK

# Failed request
$ curl -i -X POST 127.0.0.1:9080/post
HTTP/1.1 302 FOUND
Location: http://example.com/auth

test

Complex Use Case (send APISIX data)#

Let's think about another scenario, when your decision needs to use some APISIX data, such as route, consumer, etc., how should we do it?

Create a simple policy echo, which will return the data sent by APISIX to the OPA service as is, so we can simply see them.

$ curl -X PUT '127.0.0.1:8181/v1/policies/echo' \
-H 'Content-Type: text/plain' \
-d 'package echo

allow = false
reason = input'

Next, update the config of the route to enable sending route data.

$ curl -X PUT 'http://127.0.0.1:9080/apisix/admin/routes/r1' \
-H 'X-API-KEY: <api-key>' \
-H 'Content-Type: application/json' \
-d '{
"uri": "/*",
"plugins": {
"opa": {
"host": "http://127.0.0.1:8181",
"policy": "echo",
"with_route": true
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'

Try it. As you can see, we output this data with the help of the custom response body function described above, along with the data from the route.

$ curl -X GET 127.0.0.1:9080/get
{
"type": "http",
"request": {
xxx
},
"var": {
xxx
},
"route": {
xxx
}
}