Integrate a custom authorizer with OPA

December 9, 2022 | 5 minute read
Karan Kapoor
Principal Solution Architect
Text Size 100%:

Modern day system architectures are quite diverse and complex in nature and have a very strong focus on security. With distributed workloads getting more and more common, having the correct access controls in place is important. It’s equally important to use a common toolset for defining these access control policies. This post explores how Oracle Cloud Infrastructure (OCI) API Gateway can enforce policies from Open Policy Agent (OPA) for the following actions:

  • Centralize policy definition and decision-making

  • Segregate policy decision and decision enforcement

Before we go into the solution, let’s give you a brief introduction of Open Policy Agent (OPA). OPA is a Cloud Native Computing Foundation (CNCF) project that aims at unifying policy decision making process. It uses REGO as the language to define or query the policies.

Authorizer functions open the API Gateway up to an almost infinite range of authorization scenarios. Whenever the API developer needs to perform specialized AuthZ validation, they can write a function in their language of choice, including Java, Python, Ruby, Go, Node.js and C#.

The authorizer function allows API Gateway to integrate with Open Policy Agent to obtain AuthZ decisions and then enforce them. You can pass contextual information like access token, request parameterss, headers to the authorizer functions. The authorizer function is responsible for integrating with OPA APIs to query the access policy. OPA evaluates the policy and decide based on the information received.

A graphic depicting the API Gateway and OPA integration solution design.

This image depicts the following flow:

  1. The backend API is being invoked by a user.

  2. The call is received by the API Gateway, which forwards the request to the authorizer function. The gateway passes contextual information to the function.

  3. The function calls the REST endpoint of OPA for policy evaluation. OPA returns the policy evaluation decision.

  4. API Gateway enforces the decision whether to allow or deny the access.

API Gateway configuration

When configuring the deployment in API Gateway, select Authorizer Function as the authentication type and then choose the argument type (Single or multiple) based on the number of arguments you want to pass to the authorization function. In the following example, we’re passing Authorization header to the auth function.

Now we need to configure the auth function with the URL of the OPA REST endpoint.

A screenshot of the Edit Deployment page for authorizer functions with Oracle Functions on the authentication tab.

Input request

The following code block shows a sample request payload when calling OPA REST endpoint from the auth function. The access token received by the auth function is passed in the request.

{

    "input": {

        "state": {

            "token": "< ACCESS_TOKEN >"

        }

    }

}

Sample policy

Let’s look at a sample policy written in REGO. The following policy accepts a JSON web token (JWT) access token, decodes the token to get the claims, and then validates the audience and issuer claim. It also checks if the token has expired. If all three conditions are successfully met, it sets authorization to True.

package auth.token

default authorized := false

authorized := true {

    count(allow) > 0

}

decoded := {"header": header, "payload": payload, "signature": signature} {

    [header, payload, signature] := io.jwt.decode(input.state.token)

}

allow["iss_aud_expat"] {

    decoded.payload["iss"] == "< ISSUER >"

    decoded.payload["aud"] == "< AUDIENCE >"

    now := time.now_ns()

    time.diff(decoded.payload["exp"] * 1000000000, now) > 0

}

Response

According to the sample policy, the OPA REST endpoint returns a response like the following example. Based on the response, if the value of authorized parameter is true, the policy evaluation resulted in success and access is be granted to the backend API. The response also provides the decoded JWT token.

{

    "result": {

        "allow": [],

        "authorized": true/false,

        "decoded": { 

            "header": { 

                "alg": "RS256", 

                "kid": "mSIXxLh-nnKCEE-Ka4xpG", 

                "typ": "JWT" 

            }, 

            "payload": { 

                "aud": "< AUDIENCE >", 

                "azp": "6EI8v8a8RouScs0AAKzzBPcxaNg22nH5", 

                "exp": "< TOKEN_EXPIRY >", 

                "gty": "client-credentials", 

                "iat": "< ISSUED_AT >", 

                "iss": "< ISSUER >", 

                "permissions": [ 

                    "read:actions/invoke" 

                ], 

                "scope": "read:actions/invoke", 

                "sub": "< SUBJECT >" 

            }, 

            "signature": "< SIGNATURE >" 

         } 

     } 

} 

Conclusion

In this post, we demonstrated how you can integrate a custom authorizer function with OPA Policy engine to delegate authorization decisions. You can further extend this example to various other use cases. For more examples, refer to the OPA documentation.

For more details about using these Oracle Cloud Infrastructure services, see the following references:

Karan Kapoor

Principal Solution Architect


Previous Post

Announcing the general availability of OCI SDK v3 for Java

Kartik Hegde | 5 min read

Next Post


Configuring dynamic scaling in Oracle Exadata Cloud Infrastructure

Sanjay Rahane | 6 min read