 Hello everyone, my name is Rafael Fernandez Lopez and I'm a senior software engineer at SUSE. And today I'm here to talk to you about Cube Warden, which is a project that leverages WebAssembly to write Kubernetes admission policies. So what is Cube Warden? Cube Warden is a Kubernetes policy engine. It allows you to enforce security and compliance within your cluster. And how do we do that? We do that by using WebAssembly binaries as policies. And so what these policies are going to do is they are going to receive a JSON object, which resembles a request, will get some settings, optional settings for their execution. And based on that, they are going to take a decision on whether they should accept the request, accept but mutating it, or reject the request altogether. So if we think in isolation about this, then we are processing one request and we can either do the acceptance, rejection, or mutation of that request. But how does Cube Warden intercept requests and provide responses with a decision? Because this is targeting Kubernetes. How does this work? How does this integrate with Kubernetes? And for that, Kubernetes has an answer. And this is called dynamic admission controllers. And by that, we are able to register webhooks inside the Kubernetes API server. There are different ways mutating and validating. And we can say on each of these webhook configurations in which kind of resources and API versions, groups, we are interested. And so when we register these webhooks, the Kubernetes API server, when it gets a request from a client, in this case, we have the QCTL CLI tool being called, but it could be any kind of HTTP request done to the API server, it will go ahead and we look for all the webhooks that are registered against this resource and operation. And so it will go through all the webhooks that are registered and it will produce an admission review for each one of them. And then every webhook, we have to answer to the Kubernetes API server, give an answer to it, and then the Kubernetes API server will collect all these responses from all webhooks, and it will produce the final response to the client. And we know how to connect this with Kubernetes. We saw that we are going to use WebAssembly. And the question is, why do we want to use WebAssembly? So there are many reasons for that. And the first one is that it is sandboxed. So because QWarden is a security focused project, we are very interested in that the interactions that happen between the guests, this is in this case, the policy and the host that is the runtime running the policy are controlled and are only the ones that we want. And so the sandbox model of WebAssembly fits perfectly into this. But it's not only that, it's also portable. And so when we are able to build a policy and we can build that in any kind of machine, any kind of architecture, we don't need to cross compile. We also don't need to have multi-art images, like for example, with container images. So it's portable and we can just copy and paste this policy in any kind of machine on operating system and it will work. As long as we have, if this policy needs some WASI interfaces or some WASI capabilities, then of course this is required, but the binary itself is portable. So this is super important for us. And also it's a standalone, which means that I have this binary and I don't need any kind of backing or supporting library that I am dynamically linking to or the libc, for example, I don't need any of that. They are standalone binaries. Also WebAssembly is extendable. So it allows us by custom, by using custom sections in the binary itself to allow for reach embeddings. We will look more into that later. But the idea is that we can add more information, arbitrary information to the binary. And this information will go with the binary wherever it goes. And so this is very interesting to keep important information with the policy that always flies with the policy. So if we look at all these features, we kind of see that we are kind of describing like a universal binary. So we have the way in which we are going to evaluate requests. We know what we are going to use to evaluate requests is WebAssembly. But now we are going to look into why do another policy framework, what is the state of the art right now regarding policy frameworks. So right now, and there is no right or wrong here, there are different ways to take decisions and every company might take different decisions based on this, but this is important to take into account. So right now the policy frameworks that are widely used are based on domain specific languages. And so they do have to learn about these domain specific languages. You need to learn about how to use them. You need to learn about what they allow you to do and what they don't allow you to do. And so this has a cost. You need to understand all this and then work on the bright code for these specific languages. And also we have policy maintenance and distribution is not straightforward yet. So we are right now, you are creating different deployments, different workloads running on different clusters, but policies is also something that you need to cover somehow. You need to solve it in any way, in some way that you are already doing for other things. So this is also not a super straightforward solution in this sense. So what happens if, for example, we were thinking about what if we create a policy framework that based on a universal binary, we could have kind of a universal policy framework based on that. And so this is what we are trying to aim for with Q-warden. Because if you look at the left from the top to the bottom, we have open policy agent, which is a policy engine as well. And you have to write policies in Rigo in the Rigo language. And you can also try to use Gatekeeper, which also you need to write policies with Rigo. And also, for example, you could use Q-warden SDK for writing policies in different languages that we support, like Rast, Go, Swift, and Will. More languages will come over time. But the important bit here is that we are able to build these programs or these policies to the very same thing. That is a WebAssembly binary. And so I am able to build this. I have a local binary inside my machine, inside my desktop or my laptop or wherever. And then the next problem is how I publish this because I need to publish it somewhere. And so there is a step before that, that is that we have to annotate the policy. Annotating the policy takes advantage of what we said before, the custom sections. So we need to annotate with what resources this policy understands. Policy might be targeting some API group of a specific API version, some specific kind of resource, or a list of them. It really depends. But it needs to document what kind of resources it understands. And since this information flies with the policy, the policy will always say what it is able to understand and what it is able to take decisions upon. And also, we can, by annotating, you can also add free from documentation to the policy. You can add also authors, for example, or the license or some other information. So now we have this file that we annotated. And we have this policy that is also a file that is annotated. And now the next step would be to publish it. And the keyboard supports OCI registries. And the good news is that you are already using them because you are deploying containers inside Kubernetes and you are pulling these container images from an OCI registry. And if you have an OCI registry or several of them, you will be able to publish these policies to the OCI registry. And this works just out of the box. It's just an OCI artifact inside of the OCI registry. And they are next. These policies are next to your container images. So you don't need to do anything special about that. You don't need to do anything special to support policies inside your OCI registries. Okay, so we see now that the storage is clear. But what about the execution? So we have the policy server that is the component that is going to actually run our policies inside of policy evaluators or run times, wasm run times. And so the policy server, when it starts, it reads its configuration and see what are the policies that it should be running. And so let's assume that this policy server on its configuration has three policies. For example, the first one is written for OpenPoliceAgen. The second one is for Gatekeeper. Both are written in Rigo. And the third one is written in Rust, for example, with the Qboard in SDK. But we don't care about that because these are all binaries for us. And so the policy server reads this information, this configuration file, pulls all these policies, and it starts an evaluator for each one of them and creates an endpoint for each one of them. So the policy server has an HTTP server listening with three endpoints in this case. And then every endpoint is going to forward whatever it reads to the policy that is running. And so then what happens is that the Qboard and controller will register the webhook with the right endpoints inside of the policy server. And so you will start seeing that the Qventus API server is forwarding requests or admission reviews to our policy server. And then the policy server, depending on the endpoint that was approached by the Qventus API server, is going to forward that to the right policy. And then the right policy will do the evaluation instead of the wasm runtime. So we have this way of working. We see how to deploy that. And now the question is can we do a little better regarding the cloud native intersection? And the answer is yes. Right now we have tracing support inside of the policies as well. So this is like first class citizens for us. You have tracing support on the policy server that is like the main entry point. But then every policy inside of the policy server is able to trace as they want. So you can also see if something is taking more time that it should. What a request, since this comes from with a UID from the API server, the UID goes through the goes through the policy server and then goes through, goes back, goes down to the policy itself. And then if the policy traces did somehow, it will get back to the policy server and the policy server will lock all these trace events. And so you can just open telemetry with a given collector, send that somewhere else. And you're going to also use Jager for inspecting these tracing events. Also on our roadmap, there is something interesting because we want to provide primitious metrics. And so by providing primitious metrics we are able to show whether a policy is lowering a lot or if it's rejecting a lot. We can see these numbers. We can see how many policies are running inside the cluster. We can, in general, we can expose a lot of interesting metrics that are very, very useful for a community operator. And then regarding the supply chain attacks, we have, on the roadmap, we have plans for supporting or for seeing how we can interact with the six store project regarding policy signing. Because again, since we are security focused, a very security focused, then we would like to sign policies because in the end, policies are just binaries, right? Inside the Fanocia registries, of course, you can have sassams of them. But the idea is that we also support sign signing policies. And now let's go for a short demo because I have two small demos to show. All right, so now we are going to run policies on Kubernetes. And the plan that we are going to solve in this case is going to be in our organization, we have this cluster that is exposed to production. So we have end users approaching this cluster, and we have Ingress' resources over there. And so the thing is that we are using Serr Manager, and we are using also Let's Encrypt. And we have two different ways of requesting certificates. One is with Let's Encrypt, a staging cluster is where, and the other is with Let's Encrypt, production cluster is where. So what we want to do in this case is to by enforcing the annotations, because on the Ingress' annotation for Serr Manager, you can say what cluster is where you want an annotation on the Ingress. And so what we want to do is to limit what this annotation can have with the safe annotations policy. And so if we look at the cluster and mission policy, in this case, this is the kind resource that we can deploy on the cluster. And so the KubeGuarding will reconcile this. We say where our module is. In this case, this is coming from OCI registry. What are the settings that it supports? In this case, constraint annotations, which means that this key on the resource that is going to evaluate has to be of this value. And then we are going to target a number of rules. In this case, it's just one, and it's targeting Ingress' of the AP Agreement Working Kubernetes IO on V1 version. And the operation that we are going to look after is create. And this is not a mutating policy. This is just validating one. Okay, so let's deploy this policy on the cluster, where I already deployed KubeGuarding. And now we are going to wait for it to be ready, because now the controller is going to do some paperwork for us regarding Kubernetes. And then at the end, it's going to register the webhook. And when the webhook is registered, then it's actually active. And now it finished. And now let's look at an Ingress that has a let's encrypt production cluster issuer. So this annotation is what we are expecting. This is an Ingress that we would like to let pass through, because this is right. This is fine. This will get a valid certificate. And so we are going to try to create this one. And this was properly created. Now let's look at another one. In this case, as you can see, this has the let's encrypt the staging cluster issuer. And this would result in an invalid certificate for many of our users, because this CA is not trusted by browsers unless you manually trust it. And so we are going to try to create this one. And as expected, we get this error coming from the policy, because this cluster issuer value is not matching what we expect. And so this is how we can run policies inside of a cluster. But now let's say that you have already invested time in having gatekeeper policies or open policy agent policies. You can actually reuse them with Q Warden. You don't really need to change anything. You just need to build them. So what I'm going to do, what I did, is to build, to create an eco policy, which is super simple. It will just do one thing. What it does is it accepts some settings, and the settings basically say whether it should reject the request or not. And if it rejects the request, what is the message it will provide back because of the rejection? And it will not even evaluate the request itself. It will just do whatever the settings say. That's why it's called the eco one. And so I'm going to build it. And for that, any DOPA build command that comes with DOPA CLI, I target the wasm and I provide the entry point for my policy. And so I build and extract the policy.wasm file. And now what I'm going to do is to run that locally. I don't want to annotate and publish that into an OC registry first. I just want to run that. And I want to see if it runs as I expect. And so I run that with KWCTL. And you can see on the settings that I say reject false, which means that it's going to be accepted. And this is as expected a load. And now if I do the very same thing, but with the different settings, are in this case, reject true and with a rejected message, then I'm going to get a load false with the message that I am expecting from the settings and also this part coming from the policy itself. And now let's do the very same thing. Let's run this again, but in verbosity mode. So we see some things coming from the policy evaluator. But the important bit here is this bit here that is coming directly from the policy. The policy is calling to the tracing call from Rigo. And Rigo is calling to the policy evaluator. And the policy evaluator is reporting this through tracing. So this is working out of the box. And now let me go back. Okay. So some shout outs. Wasm time. We are using this project to do the runtime of policies. We are also using WAPC for doing the communication between the guest and the host. And we are also using the OCA distribution crate that we use to pull and push to an OCA registry. So we can push and pull OCA artifacts to the OCA registry. And this is part of the Keras project. All of them are super useful and really nice communities over there. So, yeah, really big shout outs to them. And also a big shout out to you. So you're in the community. We have the Q-word and channel on the physical QV net. This is like you can join and look for some policies on the Q-word and hub. We need to write more policies. So we welcome all the policies that you want to contribute. You can also report any issues inside of our org. We have a number of reports over there and also some documentation that we have. You can learn more about the project over there. And you can also contribute to the documentation if anything is missing or is not clear. And with that, I will be open for questions. So thank you for your time. And I really hope you enjoy this event. So thank you.