When it comes to managing enterprise data in a platform used by many different people, sooner or later, you will face the need to have these people work on a restricted set of data with a different set of capabilities. For example, some users may need read-only access to data, while others may require write access to only certain resources or APIs; lastly you will surely need people with administrative capabilities on all of the available resources.
There is an authorization mechanism that is designed for such cases, and it’s called Role‑Based Access Control (RBAC). In this article, we will cover how we implemented this solution within Mia‑Platform, and how we faced all the challenges that such a solution brought us.
What is RBAC?
RBAC is an authorization mechanism that defines the concepts of Roles, Permissions, and User Groups as building blocks. These building blocks allow you to create your access‑security model based on the real function that people have in your organization, rather than the specific actions they need to perform.
RBAC vs ABAC
RBAC is often mentioned together with Attribute‑Based Access Control (ABAC), another authorization mechanism built on top of RBAC. ABAC allows you to define your security model also on user or request attributes, that may include things like the location, timezone, authentication mechanism, device type or any cookie that the user has while accessing your services.
Benefits of adopting RBAC
RBAC is now one of the most popular access control models. Although in the design phase an in-depth study of permissions is required, once in place, RBAC brings several benefits:
- Governance simplification: system administrators will no longer need to tick every single Permission for each new user. They will only have to select the Role. Moreover, any change to a Role will automatically apply to all users.
- Cost reduction: since the governance will be easier, the permission assignment process becomes much faster, and this brings an overall governance cost reduction.
- Manual activity cut: because the Permissions will no longer be manually ticked, it will be easier to create some automation logics. Moreover, there will be less assignment mistakes.
- Security improvement: with less assignment mistakes come less undesired access to critical resources, hence the security will be improved.
- Monitoring streamlining: system administrators can monitor who has access to a resource just by looking at Roles, without any need to browse every single permission.
Mia‑Platform Use Case
So, why did we choose to adopt RBAC at Mia‑Platform?
Within our platform, users can perform several actions: they can view and collaborate on different projects, configure their microservices, create new repositories, view consumed resources, restart Kubernetes Pods, and so on and so forth. It is obvious that we can't allow anybody to perform any action without making sure they can actually do it because it is their job or responsibility.
Here are the main steps that brought us to define how to build the RBAC.
Step 1: identifying Permissions and Roles
For this reason, we started modeling our RBAC solution by identifying Permissions and Roles we already had in our platform, in order to create the entities on which the RBAC should have been modeled.
Identified Permissions were:
- view project information,
- save project configuration,
- deploy project,
- manage secreted variables.
Identified Roles were: Project Administrator, Editor, and Viewer.
For example, an Editor can view project information, can perform deploy operations, but cannot manage secret variables, since we want this responsibility to be held only by the Project Administrators.
Now that we have modeled our RBAC entities, we had to integrate it in our software, but how could we do it? We had to face many challenges before finding a definitive answer on how we wanted our RBAC to work.
Step 2: moving RBAC outside of the codebase
The first issue we had to address was that we did not want to embed authorization controls in each API. In fact, this would have meant having repeated code within our software, and also having to distribute authorization configurations throughout the code, leading them to be hardcoded and hard to maintain in the long run. Also, our APIs are developed across several microservices written in different languages; so, the controls should have been written in different languages, and thus becoming harder to abstract in software libraries.
For this reason, we wanted to move RBAC outside our current codebase. This first decision led us to our second issue.
Step 3: adopting Sidecar Container Pattern
Since RBAC was managed outside the microservices, there should be another microservice in charge of this. Developing a RBAC service that every other service has to contact in order to find out whether to authorize or not the incoming request could have been a viable solution, but any design that had the standalone RBAC manager API led to an architecture suffering for a single point of failure.
For this reason, leveraging Kubernetes Pod definition, we decided to adopt the Sidecar Container Pattern: every service in need of RBAC had to host an extra container in charge of implementing the authorization controls. The sidecar container intercepts all the incoming requests, and proxies them to the actual microservice only if authorization controls are successfully passed.
Step 4: adopting Open Policy Agent (OPA)
Now that we've found a viable solution for our architecture ‑ that is language agnostic and has no single point of failure ‑ we had to yet address another issue: how shall the authorization controls be defined and implemented?
We did not want the configuration to be decentralized because it could get hard to maintain. At the beginning, we thought about a possible solution: to implement a service connected to a database, where it can perform a query to fetch the user permissions, and then reject or accept the request.
However, we found that this solution could limit what we could do with our RBAC. What if we wanted to implement a specific permission that allows the user to manage certain properties of a document that otherwise are kept reserved? This solution wasn't enough.
For this reason, we decided that our RBAC service would need to support some user‑defined policies to evaluate whether the request shall be accepted or not. Since we didn't want to implement a new language for policy evaluation because it would have been long and expensive, as well as difficult to maintain, we decided to adopt Open Policy Agent (OPA) in our infrastructure. Our RBAC service uses OPA to evaluate specific policies (written in the Rego language), and with these policies we can achieve much more than simple permission checks: yes, we can verify whether a user has a certain Role or Permission, but we can also enhance the policies with request and user information (to fully support ABAC verifications), or even the API payloads to allow for property filtering or data enforcing.
This is how we implemented the RBAC within our platform. To recap, we firstly identified Permissions and Roles, defining the RBAC entities. Then, we decided to keep RBAC outside the codebase, because we wanted it to be language agnostic and easy to maintain. Next, we created a Sidecar Container within every service that needed RBAC: in this way, the architecture is free of single point of failure. Lastly, thanks to OPA, we ensured a centralized policy configuration that is easy to maintain.
Our RBAC solution improves data security, while giving you full independence and a 360‑degrees Permissions governance. Starting from Mia‑Platform v9.0, our RBAC solution will be generally available for everyone using the platform, and you will be able to use it in your projects by defining your own policies, premissions and roles.
The article was written by Federico Maggi, Senior Technical Leader.
© MIA s.r.l. All rights reserved