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.