With microservices development, I often came across a problem with implementing Authentication and Authorization (A&A). We want a robust and centrally managed authentication and authorization strategy. But, the distributed nature of the application makes it difficult to implement. In this post, I will explore how you can implement authorization in a microservices environment with Open Policy Agent.
Before we begin, letâs take a quick look at the definition for Authentication and Authorization:
Authentication refers to identifying the user (âwhoâ).
Whereas Authorization refers to determining the level of access an authenticated user has (âwhatâ).
My focus for this blog post is the Authorization part. For simplicity sake, I have created a sample application with a set of microservices. There is a basic user interface, where we can carry out various operations and see the results. The only purpose of this application is to show how various authorization scenarios are handled by Open Policy Agent. In the subsequent posts, we will extend this application to cover increasingly complex use cases, and policy administration.
So, letâs get started!
First, some context about the application. I am taking an example of a CPQ application commonly used by sales teams to configure quotes for customers.
There are following roles :
1. Sales - Users that have Sales role can create new offers for their customers and update the offers. The users that have Sales role cannot, however, delete an offer. 2. Sales Support - Support staff who can see all the offers but cannot edit any offer. 3. Sales Admin - Administration staff, can see all the offers but cannot edit/create any offer. However, they can delete an offer if required to ensure cleanup.Since we are focusing on the authorization part, I have assumed that the user is already authenticated and has a valid JSON Web Token (JWT). Every API request contains this JWT in the request header.
Download the opa-ms-sample application from github Follow the install instructions in README
and you should be able to access the UI with url http://<MINIKUBE URL>/
Based on the roles we saw above, the expectation is that the Sales team is able to
* Create a new offer * List the offers * Update an existing offerWhereas Sales Support team is only able to list the offers but not able to edit/create them. Sales Admin team is only able to list the offers and delete them.
The UI shows multiple buttons each representing an action from the user. Select a role that you want to use, and then try creating, editing or deleting an offer. The UI will give a feedback on whether the action is successful or not.
Letâs take a quick look at how the setup is currently.
The application has two microservices Offer and Customer. A NGINX Reverse proxy exposes the APIs to outside world. NGINX intercepts each API request and requests authorization
service to validate if user is allowed to execute the requested action or not. We use auth_request
directive of NGINX to intercept the incoming API calls. Each API call has an Authorization
header which contains a JWT. Entire user information including the Role
is contained in the JWT.
The Authorization service has two containers -
1.Authorization
- A custom built service (Authorization) to receive the request and create formatted input request for Open Policy Agent. 2. Open Policy Agent (OPA)
- Runs as a sidecar and exposes http endpoints for communication with Authorization container. Basically, NGINX sends the /authorize
request to the Authorization container to authorize an API call. Authorization _service then consults Open Policy Agent whether to authorize the request or not (true/false). It then returns either a success (200 OK) or an error (403 Forbidden) response to NGINX. Accordingly, NGINX either allows the API call or returns a _403 Forbidden response to the client.
What better than to quote the Open Policy Agent website itself:
âThe Open Policy Agent (OPA, pronounced âoh-paâ) is an open source, general-purpose policy engine that unifies policy enforcement across the stack. OPA provides a high-level declarative language that letâs you specify policy as code and simple APIs to offload policy decision-making from your software. You can use OPA to enforce policies in microservices, Kubernetes, CI/CD pipelines, API gateways, and more.â
OPA, basically, decouples the decision making with enforcement. It accepts structured data as input (JSON) and can return either a decision (true/false) or arbitrary structured data as output.
OPA uses rego
as the policy language. You can read more about rego and open policy agent at this Open Policy Agent Documentation
Letâs look at the Authorization service in details to understand how it works.
We run Open Policy Agent in a server mode and make use of its REST APIs to update policies and get policy decisions.
* Open Policy Agent exposes a REST API to create or update a policy.PUT /v1/policies/<id>
Content-Type: text/plain
For our example, update the policy in OPA using this endpoint with following request (this is mentioned in the GitHub README as well).
curl -X PUT --data-binary @policies/httpapi.authz.rego http://<MINIKUBE URL>/authorize/v1/policies/httpapi/authz
POST /v1/data/<path>
Content-Type: application/json
here <path>
is the path of the policy identified by the package
e.g. package httpapi.authz
. For our example, To delete an offer id â1000â, the Authorization service will invoke the endpoint http://localhost:8181/data/httpapi/authz
with the following request body :
{
"input" : {
"method": "DELETE",
"api": "/offer/1000",
"jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NzQ2NjM3MDAsImV4cCI6NDA5OTE4NTMwMCwiYXVkIjoib3BhLWV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjoiU2FsZXMgQWRtaW4ifQ._UtjZtowF3NNN3IF1t0LBHuzQhdfIfsO8jC-46GvbRM"
}
}
Authorization
header. The Authorization application will extract the JWT and add it to the input request to OPA. Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJMb2NhbEpXVElzc3VlciIsImlhdCI6MTU3MzcyNzM5MSwiZXhwIjo0MDk4MjQ1Mzc4LCJhdWQiOiJvcGEtZXhhbXBsZS5jb20iLCJzdWIiOiJzYWxlc0BleGFtcGxlLmNvbSIsIkdpdmVuTmFtdyI6IkpvaG5ueSIsIlN1cm5hbWUiOiJTYWxlcyIsIkVtYWlsIjoianNhbGVzQGV4YW1wbGUuY29tIiwiUm9sZSI6IlNhbGVzIn0.UbHWQpCMwupzsFp8f0CQ4o_bJSVaBugKijhcURZ_Mko
io.jwt.decode
. - You can try the rego policy we used for this example in rego playground. Play around with different input requests and you will see the output that OPA generates for each of these. So, in a nutshell, Open Policy Agent provide a way to decouple authorization decisions from business logic in microservices. System administrators can setup Open Policy Agent and delegate the responsibility to generate authorization policies (rego policies) to individual microservice owners. Neither the microservice owners nor the system administrators deal with an area out of their boundaries.
I used a simplistic example here. But, there are many more considerations in a production system that this post doesnât cover.
* Current setup only authorizes based on API Endpoint URL. The policy doesnât take into account the request body or other contextual information. For e.g. Sales teams may have more granular restrictions based on customer segments, geographical locations etc. OPA supports loading data from external data sources (data document
) as well as partial evaluation of policies to address this. * We updated the rego policies using a REST API endpoint of OPA. I have to repeat this every time the Authorization service restarts. * With more than one OPA instances running, updating policies through REST APIs is not scalable. OPA circumvents this through Bundle Service APIs to manage and distribute the policies to multiple instances. * Performance concern - sending each API call to authorization service will result into performance degradation. * The policy used here only provides a true/false type answer. OPA can return arbitrary structured data (JSON) as output. You can use this to enrich the request with additional attributes. This comes handy when implementing transparent data filtering. OPA has solutions for all of these. I plan to publish a series of blog posts to cover these points one by one. I will enhance the example CPQ application we started in this post and explore the OPA capabilities to build a production grade authorization approach.
Till then, I hope, this gives you an overview of how OPA can be used during microservices authorization. If you like this post or identify a mistake or simply like to discuss the use case, please do start a conversation on Twitter.
We hate đ spam as much as you do! You're in a safe company.
Only delivering solid AI & cloud native content.