Ubicloud is an open and portable cloud. When we started working on it, we looked at how Identity and Access Management (IAM) systems were implemented in the public cloud. We were surprised by the big bifurcation. Hyperscalers such as AWS, Azure, and GCP, had powerful authorization models. Other cloud and hosting providers only had authorization at the most basic level.
So, we set out to deliver something as powerful as the authorization systems seen on the hyperscalers. In particular, we came up with the following requirements:
So, I spent a week understanding our options for authorization. I decided to simplify our solution space and only provide attribute-based access control (ABAC). AWS, Azure, and GCP were already going in that direction.
With that understanding, I could build a simple authorization system example on PostgreSQL in a day. I could then implement our initial ABAC system in 130 lines of code. This blog shares that example, our design and implementation, and our approach’s pros and cons.
The following diagram describes a simple example, where the users on the left have associated “tags” (classic roles) and the resources on the right also have “tags”.
ABAC is a flexible way of implementing access control policies between users and resources. (This is a rather simplified view and we’ll come back to it later.) Since a user can hold different roles with an organization, our ABAC system allows creating different “tags” for the user.
In this example, the resource can be a VM, simple storage bucket, or source file that the user wants to access. The action is what the user is trying to do with the resource. Example actions include VM view, VM create, VM delete, etc.
Access policies are then represented with the triplet <user, action, resource>. In addition to this triplet, we introduce the notion of tags. We can associate each user or resource with one or more tags. These four concepts give us an enormous amount of flexibility with our authorization model.
Let’s translate this example into something more generic using PostgreSQL.
Our ABAC design is almost entirely based on the above example. We represent our data model in 5 PostgreSQL tables. We then use one query to check if a user has permissions to access a resource.
There are only three things to be mindful of. First, ABAC is way more flexible than what we showed it to be. In the above example, we used “tags” to assign roles to a user and used those roles to grant permissions to resources. In ABAC, you can also define those permissions over attributes. In addition to roles, attributes can include things like a user’s location, client device type, or authentication method. Tailscale has a great blog post that describes various access control policies in more detail.
Second, ABAC uses slightly different terms than the example above. In ABAC terminology, the subject is the user requesting access to a resource to perform an action. The resource is the object (such as VM, simple storage bucket, or source file) that the subject wants to access. The action is what the user is trying to do with the resource. So, the triplet <user, action, resource> above is more generally represented as <subject, action, object> in our design.
Third, for clarity, all we need for authorization is the following SQL query. This query checks if a path exists from a subject (a user identified with one or more tags) to the object (a resource identified with one or more tags).
As with all software, our design comes with tradeoffs. The nice thing about this ABAC design is that it meets our original requirements.
When building our design, we also looked at Google’s influential paper on authorization systems, Zanzibar. Compared to Zanzibar, our design has the following drawbacks.
ABAC enables a flexible way to authorize users. With it, organizations and users can express complex access policies between users and resources. Further, by using attributes (tags), you can extend these policies and create dynamic relationships. As importantly, ABAC is simple enough to understand without much effort. These properties enable us to create a powerful authorization system, where the data model lives in a few database tables and the core logic in one SQL query.
We also expect our ABAC design to evolve over time. Our implementation is open on GitHub; and if you have any questions or feedback, we’d love to hear from you. Please start a conversation on GitHub discussions or reach out to us at [email protected].