Conf42 DevSecOps 2023 - Online

Modernizing Authorization: From Basic Roles to Decoupled ABAC

Video size:

Abstract

Authorization is a critical aspect of software development, and modernizing your approach can provide significant benefits. In this session, we will discuss how to upgrade your authorization strategy, moving from basic roles to decoupling with the open-source project Cerbos.

Summary

  • Max Livier is a software developer at heart, but now in the world of product. Has spent the last ten years working in various SaaS startups. Has begun working on the open source project Cerbos, which is now a company. Talk will cover what authorization is, why you may think you have a simple solution today.
  • Today we're going to be talking about authorization with a z or a z rather than authentication. What can they actually do inside of my application? What are they authorized to access? What actions can they perform? How roles evolve as your application and your business scales.
  • GDPR, CCPA, and various other kind of data locality and data security regulations. Now you need to add permissioning and logic checks in your application code to make sure that users are in the right region. There's now quite a large number of libraries that you can embed into various specific languages.
  • Enterprise businesses have large employee, large employee bases. They need to have clear denierations between groups and regions. With that comes a lot of controls that need to be put in place. One of those is around logging.
  • There are just some components that make sense as standalone services. All that business logic that's been written for authorizing access has to be replicated into n number of languages. Can you confidently say that every service in every language is behaving as it should? There's a proposal of a new approach which is decoupled authorization.
  • In a decoupled approach, you extract all that business logic out into an authorization service that's running alongside your app. It massively shrinks, simplifies the application code, but gives you the power of flexibility of policy.
  • Do not reinvent the wheel of authorization. It's going to be a never ending headache. There's projects out there now that can do it all for you. So you can forget about it and move on with your life and really focus on building that business value.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
You. Hi everyone, Alex Livier here. And today we're going to be looking at solving the never ending requirements of authorization so quickly. By way of introduction, I'm Max Livier. I'm a software developer at heart, but now in the world of product. I started my career at Microsoft working on big enterprise customers, mainly on back end infrastructure and data side of things, and then really spent the last ten years working in various SaaS startups ranging from e commerce to finance to connected fitness to supply chain. And really my focus has always been on data and infrastructure components. And one of the pains and one of the issues and one of the areas I had to work on a lot was around authorization roles and permissions, RBAC, ABAC, those kind of concepts. And through my journey at these various different companies, I kind of got annoyed that there wasn't a better way of scalably defining and managing authorization. And so have begun working on the open source project Cerbos, which is now a company as well. And really this talk is going to be covering sort of what authorization is, why you may think you have a simple solution today, but some of the challenges that kind of come as you evolve and implement and scale your platform and your application. So just to make sure we are all on the same page, today we're going to be talking about authorization with a z or a z rather than authentication. So just to kind of clarify those two things, authentication is what you do when you go and challenge a user to provide a credential. So log in with Google, GitHub, username, password, those sort of things, and you get back some sort of verified identity. So now your application knows. Great. This person is who they say they are. Authorization, on the other hand, is the next level down. Okay, so now you know who the user is, their identity, their roles, those sort of things. What can they actually do inside of my application? What are they authorized to access? What are they permitted? What actions can they perform? And this is the space we're going to be talking about today. So authentication, users, identity, authorization, what can they actually do? So I think many of you will know this quote, in this world, nothing is certain but death, as in taxes. But from my own experience and many, many other people I've spoken to, I'd like to add on to this also the never ending authorization requirements changing. So what do you mean by this? You might start off with a very simple application with simple admin and users, but those requirements and those needs and those logic will have to change and evolve over time. So through this talk, we're going to actually going to scale a company. We're going to start out from day one, see what the authorization logic needs to be and how roles that kind of evolve as your application and your business scales. So we'll start off in day one, stage one, very simple, blissful days of roles, I call it, where you might have in your code base a check here that says, hey, if the user's authentication includes an email and that email contains our company's domain, then hey, they're an admin, they should be able to do everything. Great. Pretty simple. You can put that code in across your code base, probably in the request handler without any sort of issue. Very simple, all good. And if you want to get a bit fancy, maybe you can actually use maybe a scope inside of an Oauth token or a jot. But ultimately you're just checking a boolean. Is this person an employee or not? If they are an employee, they should be able to do stuff, if not, they shouldn't. Pretty simple. Great. Amazing. So now imagine whatever software you're building has been successful. You maybe got a sales team and that application has multiple different feature capabilities. And what kind of inevitably will happen at some point is you want to actually go and slice and dice how you maybe sell or package the application feature set to different users, to different companies, and sort of different positioning and packaging those sort of things. And this is something that might come down from a sales or a commercial team that want to kind of slice and dice applications. So now you go into your application code and now you need to now differentiate between your admins. So the people with the company's email address or looking at what package the particular user's company has. So here we've now evolved this logic check to first we're checking whether a user is an admin. Thus they should be able to do the action, or the package that that user currently has is the premium package, in which case, yay, they should be able to do the particular action. Either they're an admin or they have the premium package. And this is kind of your first step of, of sort differentiating and evolving your authorization logic to different user roles. And maybe you can use like a feature flagging system for this. That is certainly one way of doing it. Great. Now that's going to scale up, say amazingly successful. The new packages you've built, your sales is really happy and now you actually want to expand the business and you maybe want to start selling to another region. So I'm in the UK, we've sold in Europe, maybe we want to go out to the US. Well, here comes a level of complexity. So we're in the world now of GDPR, CCPA, and various other kind of data locality and data security regulations. And so now you need to actually add permissioning and logic checks in your application code to make sure that users are in the right region or accessing data storage in the right different regions. So now you need to go back into your code base, figure out where data fetching is occurring. You want to check whether someone's an admin, you want to check, make sure they have the premium package to maybe access some feature. And now you need to add another authorization check to work out whether the user is in the correct region or not, because you don't want someone in Europe accessing us data or vice versa. And many parts of the world have similar regulations. So now you have this further authorization. Can this person access this data or view this data, or store this data across your code base? And here maybe you can maybe use a scope. Again from your token, there's now quite a large number of libraries that you can embed into various specific languages. So Castle, Kankang, Caspin are in kind of the JavaScript ruby ecosystem. There's pundit and other different libraries out there which maybe help you abstract this logic app. Next challenge. Okay, cool. So we're now in multiple regions, we're selling, we've got different packages, but now you actually want to go and start selling to quote unquote, enterprise customers. And what's a common characteristics for enterprise businesses? They have large employee, large employee bases, and they have very clear denierations between groups and regions, and they need to access controls that can basically mirror that. What you don't want to do, and what I've done in the past is where you go and sign a company with 70,000 employees and you're trying to shoehorn them into maybe a couple of roles. That's just not going to work. These businesses are global businesses, multinational businesses. They will need to have abilities and controls in place to lock users down to certain regions or certain capabilities based on their team, their group, their, their geographical office location, all these sort of things. So now you start dealing with like directory type information. So groups and membership and those sort of things. And the most basic example is maybe you create a group of users called managers. So now in your code base, you're checking the admin, you're checking, they got the premium package, you're checking whether now they have access to the data in the right region you're also now checking whether someone belongs to this managers user group. And maybe you actually use a directory system. So active directory, LDAP, these one type of systems that will give you that membership and give you that assignment. But really now in your code base and every request handler, you're going to have to go and check whether someone belongs to that particular group. And such as you probably guessed, this logic is getting a bit kind of complicated. Now say you've mastered that and you want to go a bit further. Okay, so we've maybe got a new CISO hired or you want to go for your certifications. So ISO 27,001 sock two these kind of compliance certs. And with that comes a lot of controls that need to be put in place. One of those is around logging. So audit log access controls, those sort of things. So now in your code base, anytime you make an authorization decision, you need to capture the result of that decision. So capture the decision, capture the inputs to that decision, capture the output of that decision, and you want to create an order log of exactly who has access to what, where and when. So now back in your code base, you're checking whether someone is an admin, you're checking their package, checking their group, checking their region. And now whenever you have a decision, you want to actually create an order log, or exactly that. So here we have an order log that says great access denied or which region the data was fetched from. And you can maybe just use some logging library, which is great, but that's kind of logging. For application logging, this is something a bit more concrete, which is your access logs, your audit logs, which you're going to have to be able to demonstrate when you get audited and such. So you want something that will kind of scale with you and give you a very strict and standardized output for it. So at some point on this journey, it could have happened earlier, it could happen later, you might have come to the point where microservice discussion comes along. You can get quite far with monoliths these days, but there are just some components that make sense as standalone services. So let's say your main application is in node typescript, but you want to have some data modeling work, or ML or dare I say AI LLM type functionality in your application. Well, that's obviously going to be running more as a Python type experience, Python type of service. So now you're in this world of, okay, we now have our application which needs to have authorization in it, but we now have microservices that are going to be in different languages. You might have your node, your python, you might have some legacy net component, you might have x number of services running inside of your application architecture. What that now means is all that business logic that's been written for authorizing access. So checking groups, roles, assignments, emails, regions, audit, logging, all that logic is going to have to be replicated into n number of languages that you're running inside of your stack, which maybe if you use one other language is not too bad. But maybe if you got a plethora of languages, this is going to get a bit more manageable, unmanageable, and particularly when these requirements change, which as we've seen, they inevitably will, you're going to have to go and rewrite that business logic into code in n number of languages that you have and have sufficient tests and such around it to make sure that every service in every language is coming up with a correct authorization decision in every scenario. This gets a bit scary and hairy. And can you confidently say that every service in every language is behaving as it should? It's tricky. So there's a proposal of a new approach which is decoupled authorization. So we're in a world now of amazing services and software, many of them for free, many of them open source of this kind of build versus buy versus using some sort of SaaS system. And if we look at the authentication space, for example, there are many ways to do authentication in your application, but there's kind of a clear leader when it comes to can enterprise ready scalable offering. And that's kind of auth zero acquired by octave for billions of dollars not that long ago. And each part of your application stack you can really make a decision of is this core to my business or is this just something we need? Is this an infrastructure component? Is this just a common functionality their application needs, which is basically sort of undifferentiated. And authentication has gone through that maturity curve. Authorization is kind of going through at the moment like you as a business. Should you be building an authorization layer or should you just be pulling one off the shelf like an opensource project and putting it in? Really when you're thinking about building your applications, you really want to focus on the end user business value. What really are you giving your users, your customers that really differentiates? Kind of your solution to everyone else. That's going to be your value. That's going to be the thing you're selling everything else underneath it. You really take a hard look at is this core to our business or is this something that we should kind of slot in. So if we were to imagine authorization as a service or as a component running in a stack that you put off the shelf, what's it look like? Well, it's kind of an input. You have a user, they're trying to do some action on some resource. You don't want to hard code all that logic. So you send a request over to this authorization service. This authorization service needs to know how to answer the question of can this user do this action on this resource so that authorization service will have into it policy. And these policies basically need to define the different resource type, the different actions, and under which condition those actions should be either allowed or denied. So that authorization service takes that input, user resource action, evaluates those policies, roles up with a decision, either allow or deny. And that's basically what the output of that service needs to be back into your application code base. So if we were to go and look at kind of how we take maybe this code and convert it into something that's more policy based. Here's an example of a Cerbos policy where you kind of take that complex authorization logic, which is a massive if else case switch style statement that we saw earlier, and put in something a bit more structured. This is an example of a YAML authorization policy from Cerbos where you have a different policy for each resource. In this case, we're looking at a contact resource and we have a load of rules. So a very simple one here is a wildcard rule that says if the user has the admin role, they should be able to do everything. So it goes back to our kind of the user's email contains the company domain example. But then as you get more granular, you want to be able to define logic for both role based access control and attribute based access control, the difference being RBAC or role based access control. You simply allow an action based on whether someone has a role or not, or attribute based access control ABAC, which is where the decision is based not just on whether someone has a role, but actually look at the values and the conditions and the attributes of the actual resource they're accessing. And the best example of this is you can never have an owner role because owner of a resource is contextual based on who the user is and a specific instance of a resource you're accessing. So you want to be able to check the logic and check the attributes of the principal and the resource, or the user and the resource making the request to come up with the authorization decision. So you want to be able to write kind of expressions and rules as policy, as we have here as an example, where we're actually looking at the values about the resource and the principle to ultimately make can authorization decision. But the key thing with extracting this business logic out is it's now independent of your code base, and this is ultimately a service that you run inside of your stack. We're in the world of kubernetes now. We're in the world of microservices, et cetera. The rise of the sidecars through all the drama, I'd say it's going through at the moment you could say, but really it gives you this nice kind of decoupled component that runs inside of your stack. So you can see sidecars being used with like authentication, logging metrics, et cetera at the moment. And one of the nice characteristics of these is it's colocated with your application services. So authorization is not something that you can cache. Authorization checks need to be done on every single call, and it's in the blocking path of every request. Unlike authentication, which you can do once and get a token for say 30 minutes, that's valid. Authorization you need to do on every single check. So you want to make sure that this is performant, you want to make sure it's local. You don't want to be going out over the Internet to make authorization checks. That's just a terrible design decision. You want to make sure that the checks are as fast as possible. And the best way of doing it is actually colocating your authorization service with your application. And this colocated approach is what enables you to decouple the authorization logic from your code base, but scalably manage it in a way that is performant. So we were to look at what this kind of authorization service would look like in practice. And this is a bit of reference, but obviously a servos is an open source project that fills the authorization service box here. But you have your end users on a client, be it a website, mobile app, those kind of things. They're making requests to your application. And this application block here is representing your app. It could be a microservice architecture, it could be a monolith, could be a load of lambda functions. It kind of doesn't really matter. But a request comes in and when that request comes in, you really know two things. Firstly, you know the user's identity, so they're authenticated. You got a JWT token, you got something along those lines where you know this person's been challenged, they provided a credential, we've got an identity. With that identity you might have some metadata about it. So what team, what groups, what location they're in, those sort of things. You can go out to a directory service, go and get memberships and organizational hierarchy information, but basically you have the user's identity and roles. The second thing you know is based on your API, is what resource they're trying to access. So you can go out to your database, you know they're trying to access the contact resource, you've got the id, you can go and fetch that record from your application database or some other backend service. And now in your code base, this is when you would have had that if else style block we were going through earlier based on the input of identity and resource, can this user do an action or not? Now in a decoupled approach, you extract all that business logic out into an authorization service that's running alongside your app. And now in your code base, you're making a simple API call out to that service and you're basically providing the user's identity. So the principal, as we call it, it's id. Their attribute memberships, teams, that sort of thing. The actual resource they're trying to access. So they're trying to access the contact resource id, one, two, three with attributes of owner, XYZ, et cetera. Again, it's kind of open ended based on your application. And then what action they're trying to do, create, read, update, delete are the common ones, but you want something that's very flexible and much more business use case specific. So approve, deny, comment, flag, forward, export. Whatever the actions inside of your system are, you would provide that along. So authorization service has loaded it into its policies and those policies are something that's hopefully versionable, testable, store, no, get, repo in a cloud storage bracket, even a database. Make sure you're selecting a tool that gives you the ability to do testing. So unit testing is your policy. This is something the Cerbos open source project does and ultimately inside your authorization service. It then evaluates your policies against those input of principal resource and action, comes up with a decision, creates an audit log of that decision, and then returns back to your application whether it should be an allow or deny. So now in your code base, it's a very simple if statement. If the authorization server says allow, do the action. If not, return some sort of error and you kind of get on with your life. So this model really has two main advantages. Firstly, all the authorization logic is an abstracted out into policy which are external to your application code. So when you want to evolve or change your authorization logic, you evolve it in one place. You can run tests against those policies standalone. You can have full version control if you use like a git repo to store your policy files in. And then when the authorization requirements change, there's one place to update it. And then all the application services that are checking permissions are calling an authorization service which has the updated policies in. Thus you can iterate and change all your authorization logic independently of where your authorization checks are being done. Front end, back ends. Amazing job. Everything's always going to be up to date without you having to touch your application code or redeploy your app in any way. The second benefit is because this authorization service is the point making the decisions. It's also the very clean place where the audit log can be created at this time. This principal tries to do this action and this resource and is either allowed or denied, and those logs then be picked up and sent off to some log capture system or cold storage as such for use in your monitoring. And also when it comes to audit time for your compliance, you can actually demonstrate a clean audit log of exactly what's going on. So in the application code, you've gone from this before where we had the big fl style statement, to the after where it's a single call out, an API call out. In this case we're using the servos SDK, but it can be basically to any server that's doing these checks, and you simply provide the user the resource and the action. And what comes back is either an allow or deny and it massively shrinks, simplifies the application code, but gives you the power of flexibility of policy. So if we look at the kind of advantages disadvantages of this model of decoupling authorization logic, first massive advantage is your authorization logic is now defined centrally. This one place where everything is defined, everything's set. You don't need to look across various different repos or application roles or languages to understand what's going on. It's one place your policies can evolve independently of your application code. And because it is just another service, a microservice running in your stack, it's completely agnostic to any language or framework or architectural choice you've made. You want to drop in a new service one day, that's maybe in a say go. Great. You just make an API call out from your go code and you get the same authorization decisions without having to re implement any logic. By having your authorization logos as policy rather than code. You can actually go to a GitHub style workflow, so you can run tests against your policies, you can do branch based deployment, all those sort of things. And then you have your policy authorization points like cerbos actually pull the policy directly from that repo. So once your tests pass, you merge domain and then your production pdps, your policy decision points will pull in those changes and start serving based on the new decision and the consistent audit trail, being able to actually truly demonstrate to your auditors or just your internal teams exactly what's happened inside of your system in a clean and consistent format without having to worry about implementing authorization audit logging across your application code base. Because all the decisions are being done in one place, there's a single place that generates the logs. They're going to always be in the same format, they're always going to be structured, they're always going to be captured. Obviously it's not all upsides. There's a couple of challenges here. You do have another service to run and deploy and worry about scale. Making sure you choose an authorization service that is predominantly stateless, so you have essentially infinite scale with authorization is key. That's something that servos does, lots of different approaches to do it, but really you want to make sure that whatever service you pick to do authorization is very light touch and doesn't put massive resource strain on your system and can actually scale up and down with your application without sweat. There is going to be a new domain specific language to learn in some cases. With Cerbos project, we decided to go with YaMl, love it or hate it. There's a lot of tooling and there's a great ecosystem around it in terms of autocompletion schema and those sort of things. But there is going to be a bit of a learning curve to take your business logic and write it as policy files. And then finally there is a new component in your critical path. Every request is now going to have to hit the authorization service before it can be handled again. It's kind of an architectural point with whatever service you go with to make sure that it is scalable, preferably stateless. Everything's done in memory. In the case of servos, every decision is evaluated in sub millisecond. Anything beyond that is really kind of network. So making smart decisions around how you deploy in a way that's the shortest number of hops possible. Hence sidecars are great if you can kubernetes because it's inside the same pod on the same node, but there is naturally going to be another component in place. So being sensible with health checks and those sort of things. So I've mentioned a few times, Servos is an open source project that does exactly what I've explained this authorization service needs to do. It's been going for a couple of years now, over 2000 stars on GitHub. We have users from around the world using servos completely free open source to patch two license off you go. We've also just launched Cerbos Hub, which is a managed control plane and gives you a UI for managing policies as well. But if you're happy to run everything yourself, you can just use the open source project. Go and hit us up on servers. Dev is our website, hit us up on GitHub as well. Have a look at the repo and we have a slack community as well, which if you join, we have plenty of t shirts and stickers to send out. So drop us a message and we will get something sent out to you. So that's it for me. Thank you very much for your time. I hope you found this interesting. And please, please do not reinvent the wheel of authorization. It's going to be never ending headache and there's projects out there now that can do it all for you. So you can forget about it and move on with your life and really focus on building that business value.
...

Alex Olivier

Founding Team, Product @ Cerbos

Alex Olivier's LinkedIn account Alex Olivier's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways