
A Traefik middleware plugin that integrates with Open Policy Agent (OPA) for request authorization. This plugin allows you to implement flexible and powerful authorization policies using OPA's policy language (Rego).
To enable the plugin in your Traefik instance:
experimental:plugins:open-policy-agent:moduleName: "github.com/unsoon/traefik-open-policy-agent"version: "v1.2.1"
http:middlewares:my-opa-middleware:plugin:open-policy-agent:url: "http://opa.kube-system:8181/v1/data/httpapi/authz"allowField: "allow"errorResponse:statusCode: 403contentType: "application/json"headers:X-Error-Type: "authorization_failed"body:error: "Access denied by policy"
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | - | OPA server URL with policy path |
allowField | string | No | allow | Field name in OPA response for allow/deny |
errorResponse.statusCode | int | No | 403 | HTTP status code for denied requests |
errorResponse.contentType | string | No | application/json | Content type of error response (text/plain or application/json) |
errorResponse.headers | map[string]string | No | {} | Additional headers to include in error response |
errorResponse.body | interface{} | No | nil | Custom response body |
Here's a simple example of an OPA policy that allows requests based on specific criteria:
package httpapi.authzimport data.io.jwtdefault allow = falseenv := opa.runtime().envallow if {[token] := input.headers["Authorization"]substring(token, 0, 6, prefix)prefix == "Basic "substring(token, 6, -1, basic_token)base64url.decode(basic_token, decoded)split(decoded, ":", [username, password])crypto.hmac.equal(username, env.USERNAME)crypto.hmac.equal(crypto.md5(password), env.HASHED_PASSWORD)}allow if {[token] := input.headers["Authorization"]substring(token, 0, 7, prefix)prefix == "Bearer "substring(token, 7, -1, bearer_token)io.jwt.verify_rs256(bearer_token, env.JWKS_URL)}
http:middlewares:api-auth:plugin:open-policy-agent:url: "http://opa.kube-system:8181/v1/data/httpapi/authz"allowField: "allow"
http:middlewares:secure-api:plugin:open-policy-agent:url: "http://opa.kube-system:8181/v1/data/httpapi/authz"allowField: "allow"errorResponse:statusCode: 401contentType: "application/json"headers:WWW-Authenticate: 'Bearer realm="example"'body:message: "Authorization required"details: "Please provide valid credentials"
The plugin sends the following data structure to OPA:
{"input": {"method": "POST","path": ["api", "posts"],"headers": {"authorization": ["Bearer token"],"content-type": ["application/json"]},"query": {"filter": ["active"],"sort": ["desc"]},"body": {"title": "New Post","author": "john.doe","content": "Hello World!"}}}
Here's an example of using request body content for authorization decisions:
package httpapi.authz# Default denydefault allow = false# Allow users to modify only their own postsallow if {# Check if this is a POST or PUT requestinput.method in ["POST", "PUT"]# Extract user from JWT token[token] := input.headers["Authorization"]# Check if the token is a Bearer tokenstartswith(token, "Bearer ")# Extract the JWT tokensubstring(token, 7, -1, jwt)# Decode the JWT tokenio.jwt.decode(jwt, [_, payload, _])# Extract the username from the JWT tokenusername := payload.sub# Verify the username is not emptyusername# Extract the author from the request bodyauthor := input.body.author# Verify the author is not emptyauthor# Verify the author in request body matches the authenticated user's usernamecrypto.hmac.equal(author, username)}
Configure the middleware to use this policy:
http:middlewares:author-check:plugin:open-policy-agent:url: "http://opa.kube-system:8181/v1/data/httpapi/authz"errorResponse:statusCode: 403contentType: "application/json"body:error: "You can only create/modify your own posts"
This policy ensures users can only create or modify posts where they are the author.
This plugin is distributed under the MIT License.