Create a new policy
Let's create a sample policy that will help us go through some important concepts. Let's start!
We also provide a GitHub repository template that you can use to quickly port an existing policy.
Check it out: kubewarden/opa-policy-template
Requirements​
We will write, compile and execute the policy on this section. You need some tools in order to complete this tutorial:
-
opa: we will use theopaCLI to build our policy to awasmtarget. -
kwctl: we will usekwctlto execute our built policy.
The policy​
We are going to create a policy that evaluates any kind of namespaced
resource. Its goal is to forbid the creation of any resource if the
target namespace is default. Otherwise, the request will be
accepted. Let's start by creating a folder called opa-policy.
We are going to create a folder named data inside of the
opa-policy folder. This folder will contain the recorded
AdmissionReview objects from the Kubernetes API server. I reduced
them greatly for the sake of simplicity for the exercise, so we can
focus on the bits that matter.
Let us first create a default-ns.json file with the following
contents inside the data directory:
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"request": {
"uid": "1299d386-525b-4032-98ae-1949f69f9cfc",
"operation": "CREATE",
"object": {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "nginx",
"namespace": "default",
"uid": "04dc7a5e-e1f1-4e34-8d65-2c9337a43e64"
}
}
}
}
This simulates a pod operation creation inside the default
namespace. Now, let's create another request example in
other-ns.json inside the data directory:
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"request": {
"uid": "1299d386-525b-4032-98ae-1949f69f9cfc",
"operation": "CREATE",
"object": {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "nginx",
"namespace": "other",
"uid": "04dc7a5e-e1f1-4e34-8d65-2c9337a43e64"
}
}
}
}
As you can see, this simulates another pod creation request, this time
under a namespace called other.
Let's go back to our opa-policy folder and start writing our Rego policy.
Inside this folder, we create a file named request.rego inside the
opa-policy folder. The name can be anything, but we'll use that one
for this exercise. As the name suggests, this is a Rego file that has
some utility code regarding the request/response itself: in
particular, it allows us to simplify our policy code itself and reuse
this common bit across different policies if desired. The contents
are:
package policy
import data.kubernetes.admission
main = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": response,
}
response = {
"uid": input.request.uid,
"allowed": false,
"status": {"message": reason},
} {
reason = concat(", ", admission.deny)
reason != ""
} else = {
"uid": input.request.uid,
"allowed": true,
} {
true
}
We will not go too deep into the Rego code itself. You can learn about it in its website.
Suffice to say that in this case, it will return either allowed: true or allowed: false depending on whether other package
(data.kubernetes.admission) has any deny statement that evaluates
to true.
If any data.kubernetes.admission.deny evaluates to true, the
response here will evaluate to the first block. Otherwise, it will
evaluate to the second block -- leading to acceptance, because no
deny block evaluated to true, this means we are accepting the
request.
Now, this is just the shell of the policy, the utility. Now, we create
another file, called, for example policy.rego inside our
opa-policy folder with the following contents:
package kubernetes.admission
deny[msg] {
input.request.object.metadata.namespace == "default"
msg := "it is forbidden to use the default namespace"
}
This is our policy. The important part. deny will evaluate to true
if all statements within it evaluate to true. In this case, is only
one statement: checking if the namespace is default.
By Open Policy Agent design, input contains the queriable object
with the AdmissionReview object, so we can inspect it quite easily.
If everything went well, our tree should look like the following:
.
├── data
│  ├── default-ns.json
│  └── other-ns.json
├── policy.rego
└── request.rego
1 directory, 4 files