 Hi, Alex Levier here from SerbOS. Today, we're going to look at a truly cloud-native way and approach to authorizing your application, your business logic, using kind of the modern technology stack, making use of the vast ecosystem of CNCF projects. So quickly, SerbOS is an open-source decoupled access control for your software. We are a CNCF member. And this is going to be kind of a run-through of how you can use best-of-breed tools and projects to build a scalable application with fine-grained authorization, as well as it being monitorable, scalable, and observable. And we're going to kind of run through what that looks like and what a typical workflow would be if you were to go and implement something like SerbOS for authorization across your application. So a quick run-through of the app. This is a finance, it's kind of an expense tracking system. I'm sure many of you are familiar with, you know, traveling for work and having to file your expenses, those sort of things. So this is a demo app. Node back-end, route front-end, very simple API. And kind of what it exposes is we have our different users. So simplicity is a very simple kind of user switcher here. We have users, they have different roles. So from our identity system, we know what role this person, Sally, is in. We have some attributes about them. So she's in the sales department, she's at Amiga. And this is what the application kind of allows. So I can go into my expenses. I can see all the expenses that I've submitted as Sally and they've got dates, vendors, amounts, these sort of things. If I go into it, there's the different actions. So I can go and edit this expense, but I can't approve or reject because it's my own expense. If I were to go back and look at maybe when it's already approved, I can't edit it because it's not approved. So there's already some business logic in here that defines who's allowed to do what. And if I go and look over to be this other person. So now I'm Ian, I'm in the IT department. I am an admin, so I can actually see a lot more here. So suddenly I can access to this admin section. I've got a report section. If I go into expenses, I can now see expenses from Sally, but there's one from Frank. If I go and look at the actions, I can do all actions because I'm kind of the super user, some other sections that we pretend will be implemented. Now if we go and look at something a bit more interesting. So we have Frank here. So Frank is in the finance team as a user in the immediate department. If I can go into my expenses, so now I can see all the ones that have been submitted. So Sally, Frank, et cetera. If I go and look at my Frank one, I can't approve it because there's some sort of business rule that says you can't approve your own expenses. And now if I go and look at this particular one from Global Airlines, $12,000. Even though I'm in the finance department, I can't approve it. If I actually go and flip over to go and be Derek, he's actually a finance manager because he's in the finance team who's also got the manager role, he can approve it. So again, there's kind of some intrinsic logic in here. If we go and look at kind of how the rest of the applications are architected, it's all on our GitHub. So it's running a Kubernetes cluster. We have our application layer. It's talking to an API that's running. And then the data is being stored inside of Postgres and using Prisma as an ORM if you're in the TypeScript ecosystem. All our authorization checks are going through our Serbos instance and we'll go into kind of what that looks like. The Serbos instance itself is getting its policies from a Git repo. So it's pulling policies and the logic is held outside. We're using our workflow to actually test and validate those policies before pulling into the Serbos instance. And then on our monitoring stack, we have traces coming both from our app and also from Serbos. So we get kind of open telemetry, tracing so we can see what's going on. Prometheus metrics, so our app and our Serbos instance are exposing Prometheus metrics, which are being scraped and also our logs are being collected by Loki. So doing log capture from our different containers that are running here and exposing all that through a Grafana interface. So if I can flip over to our Grafana, we got various kind of interesting metrics, your typical sort of request rate by path, request latency. We have some metrics coming from our Serbos instance so we can see the different checks being done. So there's like a check resource and a plan resource. You can see number of policies that are currently in our cluster and we can actually see our decisions. So how many allows, how many denies, how many resource plans that have been committed to have been run and we'll go into what that means in a second. And then we also get all the logs. So this is all being captured by Loki. So we're actually getting audit log. So every action you are seeing me doing and clicking around is generating a log which is being outputted by Serbos in a standardized format and being in this case picked up by Loki and demonstrated here. So we can see we have some inputs or an expense. We have Derek trying to look at this particular expense ID three of these attributes, trying to see whether we can view, view approval, delete allow and we can see the different responses here. So the allow for a view approver, allow for delete, deny for an update, allow for approve, allow for a view. So we actually get a nice audit log and audit trail and we can even see what my effective roles are based on those policies, what's going on. The flow into yoga. So we can go and see traces, application traces. So here's like those API calls to go and get a particular resource. We can see the request coming in. Here we've got some middleware to do authentication. We're going to fetch the data from the database and then we actually go and do a check resource call out to the server's instance and that brings back our results as we saw kind of from the other end. So we fall from the logs, server's making decision. This is the telemetry tracing showing us of the actual request pipeline. We very quickly go and look at the actual code and see how this is all wired up. So in our server backend, as I said, is very simple kind of an express server in node. We have different routes like to render the root page to go and get a particular expense. And the way this logic is done, so we've authenticated the request already. So we know from a request header authorization header who the user is and that's being passed in this request object. So when we want to go and get a particular expense, we first go and fetch that from the database. So using Prisma, go and look up by ID and send some attributes, can't find it, return a sort of an error. We set up some spans so we can get a nice tracing around exactly what's going on. And then our business logic has different rules around who's allowed to do what. And you can imagine you might end up having to card code if this, then that, or a case switch style logic to work out, can this user, in this case, view a particular expense or not? And that logic will be kind of repeated across different API handlers. How Serbus works is you just now replace that with this SDK and we have the service SDK, which actually runs a check resource call. So we now simply package up the information about the principal. So this is our user, at the particular resource they're trying to access. So we have the resource type expense. We have various different attributes about it and then different actions that we want to check permissions for. Can this person view, can this approve, can this delete, can this update, can this approve? That goes off and then Serbus goes and checks those and you get a response back for each of those either an allow or deny. So it's a Boolean and here we're just returning a map of all the different permissions whether it's allowed or not. So these are just Booleans. Some further logic to hide certain fields based on further permissions, but ultimately we'll return back an object to the browser with the information about the particular expense if they're allowed to view it and then what permissions that user has upon it and that's actually what's being used to render these buttons. So firstly, to view the page you must have the view permission and then for each of these actions edit, approve, reject, delete, et cetera. That's being driven by Serbus and that's coming back from the response and then it's a similar setup for the other actions. So to approve, we look it up, we go and find it, we then go and check Serbus whether this user is allowed to do the approve action on this particular resource. If it is, then we actually trigger it in this case it's a database update to set the status to approve and set the idea of who approved it to the person making the request and goes off in similar logic for those. So let's go and actually look at our policy. So our Serbus policies, we have a set of common roles. So these are roles that we'll call derive roles that will get kind of added to the request or to the user base request time. So for example, the owner, you are given the owner role in the request if the owner ID attribute of the resource is equal to the ID of the person making the request. Similar, we give you the finance attribute if you have finances, your department and you're a user and we give you the finance manager role if your department is finance and you have the parent role of manager, you become a finance manager and similar sort of thing for regions. Now if we go and look at natural resource policies, this is the Serbus expense resource. We have different actions, so view, approve, create, update, delete, et cetera, kind of talking about and through these YAML definitions we define our business logic. So to view it, we're saying you must be the owner of it, you must be in the finance team or you must be regional manager. To see who approved it, we only wanna show this to people who are either the owner of the resource and it's approved. So here we say to the view, approver, action is allowed if you are the owner. So the owner ID is equal to the principal ID and the condition of status is set to approved. So you can't see who approved a particular expense unless you're the owner and it's approved. Or there's another rule here that says the view, approve, action is allowed if you're in the finance or the finance manager roles. So if you're in the finance team you can always see who approved it. Something a bit more interesting is the actual approve action. So our business logic says a principal that belongs to the finance role is allowed to perform the approve action if the amount is less than $10,000 and they did not create it. So here our logic says the approve is allowed if your derived role is finance and all these conditions meet. So here we're looking at actually the attributes about the resource and the principle to make a decision. So all of these must match. The amount field must be less than $10,000 so that's that less than $10,000 rule. The owner ID must not equal to the principal ID so you can't approve your own expense and the status must be opened. Now additionally we have our finance manager role. It's just the same logic but there's no cap, there's no limit. And what that looks like back in the UI so if I swap myself over to Frank who's just our regular finance person I'm looking at this expense which is $12,000. It's not allowed if I go and look at it as Derek the action is allowed. So Serbos is actually based on the request giving you a dynamic result for each of these actions. And that's being reflected in the traces when you see these traces go through. Here we got the request of Derek and the response is coming back for each of those particular actions. So now let's say our business logic changes and we've got a ticket and we wanna kind of evolve this logic. Normally you'd have to go back to your code, update your if-else style statements to go and change things to reflect the new logic. But because our application code and our authorization logic is decoupled through Serbos these policies and the API application is actually doing a check against the service instance we don't need to touch your application code again because all the context about who's making the request and the resource they're trying to access is already being sent to Serbos. And so in our policy here let's say we wanted to change it from $10,000 to $50,000 because you want your team's growing you wanna be able to decenimate every responsibility to different parts of the organization. So now anyone who's got a finance role should be allowed to prove it up to $50,000. So what this ultimately means is our existing finance user should now actually be allowed to do it. So I've made this change. Let's go and commit it to our Git repo. So we wanna make sure we have a nice sort of, can't type at the same time. We wanna have a nice sort of Git object style workflow. So I've now committed that change. I'm gonna go at it. I'm gonna go and commit it. Really can't type. And I'm gonna say allow a finance team members to approve up to $50,000. And of course it's not gonna like my dollar sign. There we go. So I've made that change. And the logic is going on. I'm gonna go and push that out to my Kana Co repo and it's up there. Now at this point you could set up kind of a CI pipeline in say GitHub Actions or GitLab and have this server since it's pulled down. But we're actually gonna use Argo for this. So we have our Argo instance running inside of our cluster. So again, it's all kind of locally controlled. And we're gonna submit a job to it. I'm gonna go and just go into our infra, show you how I make file. So we have this Argo job submit and we're gonna submit a workflow that we have available from Serbos. And we're saying go and grab the policies from this particular repo, this demo app expenses repo and run that workflow. And I'm gonna do this and then I'll talk through what it does to take a little time. But that's now gonna go and submit a job and we can see the jobs running and we can go in our Argo UI again, running inside of our cluster and see what's happening. So what's happening behind the scenes? We keep top of job is case manually, imagine it being triggered by a commit to the Git repo. It's gonna go and clone down the repo into it. So this is now gonna go and fetch the latest commit. It's gonna go and fetch the latest policy change that one we made in that YAML file. And this takes a little while to run, but behind the scenes is pulling down those policies and this workflow does a few different things. So with Serbos, we provide a full sort of CI CD style experience so there's tooling in there and to actually write tests and evolve things over time and run in your CI workflow, first you validate that your YAML files are in the correct format against the schema and then actually run tests. So for Serbos, for this expensive resource we've actually have our test defined here. So we have some example test data of different principles with different roles. This actually matches what you're seeing in UI. We have this example of different resources and we have some tests. So here we're testing every combination of user and resource and action to go and establish whether our policies are doing as we expect and catching on those edge cases which undoubtedly will occur and may not have thought of and here we're saying that for Saray, if this particular expense, these actions should be allowed, denied, et cetera. And we're doing it for kind of different ones so we can be certain what's going on. Here we're looking at different roles. So we have these principles of Mark and Simon, they now are sales managers, different results, different actions and what the expected results comes from and if we go and actually look at the approve we have tests that are checking this approve action. And that's what our goal is now running through is actually going to pull down as policies, firstly validate that the policies are behaving as they should and then actually run the tests against them. And if we go and jump back in here we can actually see what's going on. So we've cloned the repo already. We're running the tests, we're validating it, so this is good, so this step hasn't failed so all our policy files are valid against the specification of service policies and then we have the test phase. So this is where we're actually running that change through the tests. So this is now executing, so it's using servers itself to actually run the tests and this has surprise, surprise, failed. If we go and look at the output, we can actually see, oh yeah, this particular expenses test for Frank, for expenses three has failed. We got a deny but it was actually allowed and this is because our test data for Frank in this particular expense now breaks the rule. We expected a expense of over $10,000 and not to be allowed by Frank who isn't the finance manager, but he is now, we've changed that rule where the limit is $50,000. So that test has caught the change. So that's gonna actually just gonna fix that test because we've changed our business logic and we want to allow it. So it was approvals for Frank, for expenses three. Let's check this right, yeah, expense three approve for Frank, Frank, expense three. So this actually should be allowed now because the limit has changed. So we're gonna go commit that again, we've got the change, classic fix test commit, commit that in, synchronize the changes up and we're gonna go and just rerun that test. So now we're gonna go and give the same process, kick off that job inside of Argo, again, kind of inside our cluster. So it's gonna go and pull down the different resources, it's gonna pull down our Git repo and this time it's gonna go and run through all the tests and just repeat, I haven't touched my application code. The only thing I've changed now is our application policy. Our logs, our application, you can confirm it, our containers are still running, it's still serving requests, our service instance is still making decisions, our index hasn't changed, everything is behaving kind of as it should. And we know our logs as we kind of click around, let it again, Frank, I'm still denied and our tracing here is kind of confirming that, we can see the request going off, we can see it going off to a service instance and coming back. So you get that full visibility into when exactly what's going on. So now the CI workflow is validated, it's cloned, the tests are now passed and now what the workflow is doing is actually saying to the service instance that's running inside the cluster, saying, yep, there's new policies, they're valid, they're tested, go and pull them down and that service instance is now gonna go and fetch the new policies. If you actually go and look at the here, inside of here, the requests are kind of being handled and we'll actually see an audit event occurring telling us it's gone and downloaded those new policies which it has, so it's reloaded. So the policies are now being updated, the reload store method has been called, so again, because of the observability and tracing we have, we can see exactly what's going on and now if I just go and refresh my data here, so if I go back it out and then, this is Frank, remember we changed the logic, this one, these buttons are now enabled, so this action is now being validated. So kind of summarize what's going on here and recap what we've just seen. Through this architecture, we have an application, it's business logic for authorization, has been extracted out into a standalone service using Serbos, CACF project and that Serbos instance is pulling down as policies from a repo. We can see exactly what's going on thanks to OpenTelemetry, Yeager, Prometheus, Loki, we have all the logs, we have the metrics, we can see how things are performing, we can see the traces of a request coming out through into the database out into a Serbos instance before actually returning back thanks to the Yeager traces and then we're using Argo for end-to-end workflows for compiling, testing, validating the authorization changes and then once it's done telling the service, the Serbos service inside of our cluster to reload the policies and then that is immediately reflected inside our application because all those checks are not hard coded, they're now being dynamically resolved based on that Serbos instance. So this is just touching the tip of the iceberg in terms of what you can do using this architecture but the cool thing is you're able to fully, very much cloud native open source as well, authorization application architecture, plugging into all your existing tools around observability, monitoring metrics in a very cloud native-y fashion and allowing you to iterate on your business requirements and authorization without having to go back to a dev team to go and change the code base. You can just do it through the Serbos policies. If you want to find out more about this, the application is up on the Serbos GitHub org so github.com.com.serbos.demo.app and expenses and you can find out more about Serbos, how these policies are defined, the different tools and everything are available at service.dev. A particular shout out, we have a playground where you can actually experiment and test policy themselves and this example application, this expenses app is actually preloaded in here as an example so you can experiment directly in the browser without having to write all this infrastructure but just know that things are ready to go as and when you need them and want to deploy to production. Thanks for your time. Always have to chat more. You can come join us on our Slack community if you go to server.dev and follow the links there. You can find me at AlexLivier on Twitter and I look forward to speaking to many of you in the future. Thanks.