As mentioned in our earlier blog, providing authorization for your enterprise application can be challenging.
We’re eager to share our insights on the methodology adopted by a frontrunner in this field, OPA. We’ll also delve into its pros and cons, enabling you to make well-informed architectural choices when needed.
Open Policy Agent, is a powerful policy engine, written in Go and endorsed by CNCF.
OPA is versatile; it can function as an independent service, serve as a sidecar, or be integrated directly into your Go lang-based service. Despite being a policy engine by nature, it’s extensively utilized for authorization checks, broadening its practical applications.
The conceptual building blocks of OPA are,
Policiesrepresent the “Business Policies” that define the rules driving the authorization process. For example, in a Role-Based Access Control (RBAC) model, policies may consist of rules such as “A person with permission P can perform operation X”.
Datarepresents the global context used by Policies during the evaluation of a specific request. In an RBAC, data would include mappings of all the Roles and Permissions.
Inputrepresents the context of the current request for which the policy needs to be evaluated. When OPA is tasked with checking the permissibility of operation X, the input would include details about “the current user and their role”.
Policyas code to OPA, which will be executed based on the received request. OPA uses a DSL called Rego for expressing policies. Each Policy can be thought of as a function that takes Input as parameters and uses preloaded Data to evaluate and return the authorization result. In simple cases, the result can be a YES or NO indicating whether the authorization was approved or denied, while more complex data structures can also be returned as JSON.
Centralizing your authorization implementation using OPA (whether as separate services, sidecars, or embedded within services), is the best approach to avoid inconsistencies in authorization across different components of your application. Centralized policy management ensures that authorization rules are uniformly applied and eliminates the risk of variations or discrepancies in implementation.
Of course, the key here is not OPA. The key here is having a centralized authorization definition as mentioned in the previous blog.
OPA provides a testing framework designed for the policies written in Rego. This framework enables you to employ conventional unit and integration testing to validate our authorization rules. By testing policies, you can ensure their correctness and verify whether they align with the intended access control requirements.
OPA certainly has a cost advantage as opposed to writing your own centralized authorization service. The advantages comes from :
However, there is a learning curve assiciated with Rego. (To a good extent the current LLMs can help here ;))
The primary performance boost comes from the design to keep the Policy, Data and Input in-memory during execution.
If you’re choosing OPA for the right type (will explain it shortly) of the authorization problem, OPA will have good performance.
With respect to scalability, you can make OPA scalable by choosing the right architecture that fits your problem. Depending on your architecture, you can deploy OPA as a dedicated service, a sidecar alongside your existing services, or embed it directly into your services. This flexibility ensures that you can scale OPA in a way that best fits your application’s needs and supports its growth.
In addition to its core capabilities for authorization, OPA offers several advantages that enhance its versatility and usefulness:
Enforcement of Non-Authorization Policies:
OPA being a general purpose policy agent, allows you to enforce policies beyond just authorization. For example, you can implement a policy such as “users can only use a particular feature for 10 days from sign-up”. This flexibility might not be possible with traditional authorization products.
Robust Tooling Ecosystem:
OPA benefits from a rich tooling ecosystem that enhances its usability and developer experience. For example,
Integration across the Stack:
OPA can be leveraged at almost all the stacks.
In-Memory Context Data:
The primary limitation of OPA comes from the design choice of keeping the context data in-memory, especially when dealing with large amounts of data. (Though it’s more of an intentional trade-off).
Learning Curve with Rego:
There is a learning curve associated with Rego. The paradigm could be familiar if you’re already familiar with logical programming. But with respect to the programming constructs, it’s possible that you might get stuck at certain places.
Data Storage Considerations:
The choice for data storage is up to you. If the Data and Input is not designed properly, you’ll end up with a less performant system.
While OPA is a powerful tool, there are scenarios where alternative solutions may be more suitable:
Handling Huge Context Data:
If your context data for authorization requests is huge, it could be problematic. When your use cases involve checking large amounts fo data, OPA will get inefficient.
For instance, if you need to ensure all the objects returned by the list endpoint is efficient, the straightforward solution will be much more inefficient. You can still workaround this problem by having OPA return filter conditions based on the context, which can then be used to list the entities.
Optimized Access of Data with Growing ACL List:
If your permission model relies less on computation power and is more focused on optimized access to data(a growing ACL list), OPA might not be the right solution.
For example, your application might require you to track the allowed and denied list of all the users for a resource and vice-versa. This might still be doable by creating policies that return conditions as mentioned above. But the implementation might be tricky.
While OPA offers a plethora of advantages, it is not a universal solution. The effectiveness of OPA largely depends on the specific context and needs of your application’s authorization requirements. Understanding the potential trade-offs is crucial before deciding to implement OPA.
We’ve successfully leveraged OPA for authorization and policy checks within our product, Formhouse. However, it’s worth noting that not all companies have found OPA to meet their needs – for example, here’s an insightful case study detailing why Carta decided to move away from OPA.
Overall, the decision to use OPA should be based on a comprehensive evaluation of your project requirements, the size of your context data, and the computational needs of your permission model.