Let's first understand some fundamental terms in order to understand OPAL.
Identity management: Organizations use this to maintain track of employees' identities and the departments in which they are working.
Authentication (AuthN): It provides a way in, mainly through login screens, to the application. It decides whether to allow or deny access to the application after authenticating the user's identity from a trusted source. modern applications collect user data and store it in JWT (Json-Web-Token). A claim about the user will be included in the JWT. The identity and claims are then cryptographically signed, allowing decisions to be made later on the basis of the user's identification without requiring re-authentication.
Authorization (AuthZ): It controls user access within the application. Policy-decision-point (PDP) and policy-enforcement-point (PEP) are two components of the AuthZ architecture, which is specifically designed for microservices.
- PDPs mainly answer to the authorization inquiry with the appropriate application decision.
- PEPs are employed as gates before to accessing content, data, services, or features.
The most crucial component of authorization is policy, these are the rules that control who can access an application. The RBAC and ABAC are two of the comman policies.
Why we need OPAL and OPA?
We have switched from monolithic to microservice architectures nowadays, so with all of those services comes a need for communication between all of the components. We also need to determine who may talk to which component and how much, so we must configure access control and permissions for these. Additionally, there are more areas where access is necessary due to the use of physical devices and third-party services; as a result, authorization infrastructure is needed.
Every application has some policies in place, but as time goes on, those policies change in various ways and must be updated. To do this, we need a dynamic mechanism to update the policy in accordance with requirements and authorized data updates as well. Since we want to keep our application secure and to give users real-time updates, the policies include all the rules that continuously become more complex over time.
In order to handle security and compliance effectively, we must grant specific individuals access to certain information and resources. We will have a difficult time if this is not handled properly.
OPA
It stands for Open Policy Agent. It is an open source policy engine that allows us to enforce rules across the stack. Rego, a declarative language, allows you to specify policy as code. Therefore, utilizing OPA, we can apply policies to Kubernetes, API gateways, CI/CD pipelines, microservices, etc.
OPA was originally created by Styra and is proud to be a graduated project in the Cloud Native Computing Foundation (CNCF) landscape.
It frees up the software to make policy decisions. Without OPA, you have to handle all of your system's policies from scratch. It offers ways to improve control and visibility of your system's policies. OPA separates the policy-decision making from policy enforcement. Every time the software has to make a decision, it queries OPA and provides information in JSON format; OPA then evaluates the policy and uses the data to make the appropriate decision. The policy need not just be yes/no, allow/deny, it might also produce output in JSON format.
OPAL (Open Policy Administration Layer)
Nowadays, applications are complex, run at scale, and distributed, and with that comes a lot of authorization challenges. The administration layer of OPA is called OPAL. It speeds up open policy to the level required for live applications. The OPAL pushes any policy or data updates to the OPA in real time. OPAL will ensure that the authorized data, services, and policy are always in sync. Although OPAL makes use of policy engines, it is not a policy engine.
OPAL has two key components:
OPAL Server:
- It is linked to the git repository and monitors policy updates via a webhook ( any other versioned repository is also supported like S3, SVN, etc)
- It accepts data changes from REST API.
- It establishes the websocket Pub/Sub channel for the OPAL Client to receive all live updates.
- It may be scaled with the server instances using a Backbone Pub/Sub channel that is configurable.
OPAL Client:
- It is deployed adjacent to the OPA and updates the OPA.
- It subscribes to the pub/sub for data and policy updates.
- It obtains the data source configuration from the server and then it fetches the data from the relevant data source (Databases, APIs, third party services)
- Downloads policies from the server.
In-Depth Architecture:
OPAL Server: It manages the policy and data updates and pub/sub channel for clients to subscribe and get updates.
OPAL Client: It runs along the same side as the policy agent. It subscribes to the pub/sub channel for the policy and data updates. When any data updates happen, it collects all the data from the relevant data sources. The majority of firewall and networking issues are solved by clients opening an outward websocket connection to servers, resulting in a bidirectional Pub/Sub channel.
Open Policy Engine (OPA): It is a policy engine, provides with the decision.
Application Services: The authorization layer has been created for this.
Data Service: It is a component of the application that communicates with the OPAL server and alerts it to changes in authorized data.
Git Repository: The policies are stored in a version control system. The webhook is triggered whenever a policy change occurs.
Data sources: There are various internal or external sources that hold and serve data that needs to be used in an authorization decision by the policy agent. OPAL-Clients can be extended with different FetchProviders to allow the extraction of data from various sources. Examples are REST APIs, SQL DB, Stripe, Salesforce.
OPAL admins: These are the developers who keep the application up to date. New policies can simply be created and pushed to git. From the control-plane, they can control OPAL and OPA, can quickly sync authorization to match the changed application data.
End Users: These are the application users who are having a great time and working within their bounds. The application's authorization layer adapts to their requirements in real-time thanks to OPAL - a new user who has been invited can access right away, and newly assigned permissions take effect right away. There is no delay and no redeployment.
In OPAL we have two Pub/Sub Channel-
- Client - Server: It is a lightweight websocket pub/sub channel.
- Server - Server: It is a Backbone pub/sub channel.
Since we are only running one OPAL server, no infrastructure is needed for the lightweight pub/sub to supply clients with real-time updates. If scaling the OPAL server is necessary, we use Backbone pub/sub to sync all of the servers. In order for a client connecting to one server to receive updates from the other servers as well. The Broadcaster module connects the lightweight Pub/Sub to the backbone Pub/Sub.
DEMO
- Run this command to get started after cloning the repository.
$ docker-compose -f docker/docker-compose-example.yml up
The docker-compose Yaml runs 3 containers-Broadcast, OPAL Server, and OPAL Client.
It will operate a single OPAL server and OPAL client alongside OPA. The example policy repository found here will be pushed by OPA. It is an RBAC (role-based access control) policy. As a result, we have an OPAL server, OPAL Client, and OPA. As soon as the server starts, it monitors the git repository, instructs OPAL Client, and then OPAL Client pushes changes to OPA, functioning similarly to GitOps.
- OPAL Client: localhost:7000.
- OPAL Server: localhost:7002.
- OPA It is being run as a managed process in the OPAL client container at localhost:8181.
We can perform any query because OPA is operating as an API in: 8181. We can ask to obtain the user's role assignment and metadata because the RBAC policy is the default policy in the example repository:
curl --request GET 'http://localhost:8181/v1/data/users' --header 'Content-Type: application/json' | python -m json.tool
We can also open this in Postman by going to localhost:8181/v1/data and obtain the same outcome as with the cli.
- Let's run an authorization query; this is an input-driven query.
$ curl -w '\n' --request POST 'http://localhost:8181/v1/data/app/rbac/allow' \
> --header 'Content-Type: application/json' \
> --data-raw '{"input": {"user": "bob","action": "read","object": "id123","type": "finance"}}'
{"result":true}
This query asks whether Bob can read finances, and as you can see above, the result is true.
- Let's proceed with the OPAL Server data update now. Since there is now just US-based access, let's switch the user's location and view the most recent information in real time. Install the Opal client for that.
pip install opal-client
- We are now overwriting the user location in order to submit an update using the Opal cli.
$ opal-client publish-data-update --src-url https://api.country.is/23.54.6.78 -t policy_data --dst-path /users/bob/location
Publishing event:
id=None entries=[DataSourceEntry(url='https://api.country.is/23.54.6.78', config={}, topics=['policy_data'], dst_path='/users/bob/location', save_method='PUT')] reason='' callback=UpdateCallback(callbacks=[])
Event Published Successfully
- Bob is able to access certain information since his IP address is dependent on his location, which is the US in this case. However, if we change that, it would be against the rules and the status would become false.
curl -w '\n' --request POST 'http://localhost:8181/v1/data/app/rbac/allow' \
> --header 'Content-Type: application/json' \
> --data-raw '{"input": {"user": "bob","action": "read","object": "id123","type": "finance"}}'
{"result":false}
Visit their documentation to learn more about OPAL and OPA and to get started.