Authorization: OPA policies for user authorization (RBAC)
Context
You use OPA policies to drive authorization decisions in a RBAC-like fashion. In particular, access to certain endpoints is granted depending on whether the user has certain roles or belong to a certain group.
Solution
You can use the Open Policy Agent Evaluator plugin to evaluate OPA policies directly within Gate.
Step 1 - Run Gate in transparent mode.
You should also have Gate deployed in your infrastructure. You should run Gate in transparent mode to ensure that everything was correctly deployed.
Step 2 - Implement your policy in Rego
To evaluate an OPA policy you need two pieces of input:
- The Rego policy
- Input data, passed to the Rego policy as the special
input
object
Policy input object example
The plugin passes the follow input format to OPA.
Note that this format is compatible with OPA-Envoy, so you can reuse your existing OPA policies with Gate.
The following is an example of input
object Gate makes available to Rego policies during evaluation in its current Version 1 format:
{
"version": {
"gate": "v1.1"
},
"request": {
"http": {
"headers": {
"Accept": [
"application/json, text/plain, */*"
],
"Accept-Encoding": [
"gzip, compress, deflate, br"
],
"Authorization": [
"Bearer eyJhb..."
],
"Cookie": [
"foo=bar"
]
"User-Agent": [
"axios/1.3.4"
]
},
"cookies": {
"foo": "bar"
}
"host": "example.com",
"method": "GET",
"path": "/some/path",
"protocol": "HTTP/1.1",
"query": "option=value",
"scheme": "https",
"url": "https://example.com/some/path?option=value"
},
"parsed_path": [
"some",
"path"
],
"parsed_query": {
"option": [
"value"
]
},
"parsed_token": {
"header": {
"alg": "RS256",
"kid": "pYsNGA"
},
"payload": {
...
},
"signature": "JvVt..."
},
"time": "2023-04-09T18:38:41.117405838Z",
"token": "eyJh..."
}
}
Note that if the plugin is configured to execute on responses as well then the input object contains both the request and response objects. The request is the same as the object shown above. The whole input object looks as follows:
{
"version": {
"gate": "v1.1"
},
"request": {
"http": {
...
}
},
"response": {
"http": {
"contentType": "application/json"
"status": 200
"body": "{}"
"headers": {
"Accept": [
"application/json, text/plain, */*"
],
"Accept-Encoding": [
"gzip, compress, deflate, br"
],
"Authorization": [
"Bearer eyJhb..."
],
"User-Agent": [
"axios/1.3.4"
]
},
},
"time": "2023-04-09T18:38:46.117405838Z",
"parsed_path": [
"some",
"path"
],
"parsed_query": {
"option": [
"value"
]
},
}
}
The output of an evaluation is expected to be a boolean to be found at the location specified in the policy_decision_path
plugin parameter.
Step 3 - Deploy your policy with Gate
You have two options to deploy OPA policies with Gate.
The first is to inline policies, this approach works best when you have a limited number of policies and performance is really important.
The second approach is to use a remote bundle. The remote bundle can be pulled from any remote host, including the SlashID distribution hub.
Example
Let's define a simple OPA policy that checks whether the user belongs to a group
called admin
.
Writing the rego policy
This is an example JWT token:
{
"aud": "c921477d-27f2-6bc2-4e3f-ab91a1c65f73",
"authenticated_methods": ["api"],
"exp": 1681720646,
"first_token": false,
"groups": [],
"groups_claim_name": "groups",
"iat": 1681634246,
"iss": "https://api.sandbox.slashid.com",
"jti": "2a4dbca88332fd40ac0dd717de6aa760",
"oid": "c921477d-27f2-6bc2-4e3f-ab91a1c65f73",
"person_id": "06414801-354e-7a95-ba08-062f71b3c9de",
"sub": "06414801-354e-7a95-ba08-062f71b3c9de"
}
We are using SlashID issued tokens in this example, but the OPA plugin is issuer-agnostic and can work with any JWT token
package authz
import future.keywords.if
default allow := false
allow if input.request.parsed_token.payload.groups[_] == "admin"
Configuring Gate
This is a sample configuration for the OPA plugin in Gate using inline policies:
# OPA plugin with an inline policy that checks whether the bearer token contains
# the 'admin' string in claim named 'groups'
- id: authz_admin
type: opa
enabled: false
parameters:
<<: *slashid_config
policy_decision_path: /authz/allow
policy: |
package authz
import future.keywords.if
default allow := false
allow if input.request.parsed_token.payload.groups[_] == "admin"
...
- pattern: "*/api/admin_opa"
target: http://backend:8000
plugins:
authz_admin:
enabled: true
The /api/admin_opa
endpoint is accessible by the user only if the policy above returns True
.
Conclusion
We have successfully deployed an OPA policy using Gate to implement RBAC on an arbitrary endpoint.