-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This documentation covers the general Rego information and also two specific sections about Open Policy Agent and Gatekeeper.
- Loading branch information
Showing
11 changed files
with
796 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Rego | ||
|
||
The Rego language is a tailor made language designed to embrace | ||
policies as | ||
code. [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) | ||
is a language inspired by Datalog. | ||
|
||
There are two ways of writing Rego policies as of today in order to | ||
implement policies as code in Kubernetes: Open Policy Agent and | ||
Gatekeeper. | ||
|
||
## One language. Two frameworks | ||
|
||
### Open Policy Agent | ||
|
||
Open Policy Agent is a project that allows you to implement policies | ||
as code in any project. You can rely on Open Policy Agent for any | ||
policy based check that you might require in your own application, | ||
that will in turn execute the required Rego policies. | ||
|
||
In this context, writing policies for Kubernetes is just another way | ||
of exercising Open Policy Agent. By using Kubernetes admission | ||
webhooks, it's possible to leverage Kubernetes' admission webhooks to | ||
evaluate requests using Open Policy Agent, that will in turn execute | ||
the policies written in Rego. | ||
|
||
Open Policy Agent has some optional integration with Kubernetes | ||
through its `kube-mgmt` sidecar. When deployed on top of Kubernetes | ||
and next to the Open Policy Agent server evaluating the Rego policies, | ||
it is able to replicate the configured Kubernetes resources into Rego | ||
-- so those Kubernetes resources are visible to all policies. It also | ||
lets you define policies inside Kubernetes' configmaps. You can read | ||
more about it on [its project | ||
page](https://github.com/open-policy-agent/kube-mgmt). | ||
|
||
### Gatekeeper | ||
|
||
Gatekeeper is very different from Open Policy Agent in this regard. It | ||
is focused exclusively to be used in Kubernetes, and takes advantage | ||
of that as much as it can, making some Kubernetes workflows easier | ||
than Open Policy Agent in many cases. | ||
|
||
## Looking at the differences | ||
|
||
Both Open Policy Agent and Gatekeeper policies use Rego to describe | ||
their policies as code. However, this is only one part of the | ||
puzzle. Each solution has differences when it comes to writing real | ||
policies in Rego, and we are going to look at those differences in the | ||
next sections. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Builtin support | ||
|
||
Building a policy for the `wasm` target is only half of the problem, | ||
it needs to be executed. | ||
|
||
The Open Policy Agent team has a dedicated page you can check in order | ||
to [find out the built-in support | ||
level](https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions). | ||
|
||
Every green check in this table means that those built-ins are | ||
implemented regardless of the runtime: they are implemented already on | ||
the policy you have built. | ||
|
||
The built-ins marked as `SDK-dependent` are the ones that the host has | ||
to implement -- in this case, Kubewarden. Open Policy Agent and | ||
Gatekeeper may use them depending on the needs of the policy. In any | ||
case, this built-ins are exposed to the policy and any new or existing | ||
policy could depend on them. | ||
|
||
These are the [built-ins implemented up until now in | ||
Kubewarden](https://github.com/kubewarden/policy-evaluator/issues/56#issue-983937559). | ||
|
||
## Executing policies with missing built-ins | ||
|
||
When a policy is instantiated with `kwctl` or the `policy-server`, the | ||
list of built-ins used by the policy will be inspected, and if any of | ||
the used built-ins is missing, the program will abort execution | ||
logging a fatal error reporting what are the missing built-ins. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Gatekeeper | ||
|
||
Gatekeeper is a project targeting Kubernetes, and as such, has some | ||
features that are thought out of the box for being integrated with it. | ||
|
||
## Compatibility with existing policies | ||
|
||
All Gatekeeper policies that you have written already should be | ||
compatible with Kubewarden as we will explain during this chapter. | ||
|
||
> **Note**: if this is not the case, please report it to us and we | ||
> will do our best to make sure your policy runs flawlessly with | ||
> Kubewarden. | ||
Policies have to be compiled with the `opa` CLI to the `wasm` target. | ||
|
||
In terms of policy execution, you can read more about the [Open Policy | ||
Agent built-in support that is implemented in | ||
Kubewarden](../02-builtin-support.md). |
101 changes: 101 additions & 0 deletions
101
src/writing-policies/rego/gatekeeper/02-create-policy.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# Create a new policy | ||
|
||
Let's implement the same policy that [we wrote with Open Policy | ||
Agent](../open-policy-agent/02-create-policy.md): a policy that | ||
rejects a resource if it's targeting the `default` namespace. | ||
|
||
## Requirements | ||
|
||
As in the previous section, we will require the following tools: | ||
|
||
- `opa` | ||
- `kwctl` | ||
|
||
## The policy | ||
|
||
Since Gatekeeper is targeting Kubernetes, it has the freedom to be | ||
more handy in what the policy has to return. | ||
|
||
With Open Policy Agent we had to construct a whole `AdmissionReview` | ||
object as the response of our policy. With Gatekeeper, we only have to | ||
return none or more violations from our policy entrypoint. | ||
|
||
If no violations were reported, the request will be accepted. If one, | ||
or more violations were reported, the request will be rejected. | ||
|
||
We create a new folder, named `rego-policy`. Inside of it, we create a | ||
`policy.rego` file with contents: | ||
|
||
```rego | ||
package policy | ||
violation[{"msg": msg}] { | ||
input.review.object.metadata.namespace == "default" | ||
msg := "it is forbidden to use the default namespace" | ||
} | ||
``` | ||
|
||
In this case, our entrypoint is `policy/violation`, and because of how | ||
Rego works, it can either have 1 violation: if the object to be | ||
reviewed is targeting the `default` namespace, or 0 violations | ||
otherwise. | ||
|
||
Take a moment to compare this policy with the one we wrote in the Open | ||
Policy Agent section. That one had to build the whole | ||
`AdmissionReview` response, and the inputs were slightly | ||
different. In the Gatekeeper mode, the `AdmissionRequest` object is | ||
provided at the `input.review` attribute. All attributes of the | ||
`AdmissionRequest` are readable along with `object`. | ||
|
||
Now, let's create the requests that we are going to evaluate in the | ||
next section. | ||
|
||
Let us first create a `default-ns.json` file with the following | ||
contents inside the `data` directory: | ||
|
||
```json | ||
{ | ||
"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" | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Now, let's create another `AdmissionReview` object that this time is | ||
targeting a namespace different than the `default` one. Let us name | ||
this file `other-ns.json`. It has the following contents: | ||
|
||
```json | ||
{ | ||
"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`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# Build and run | ||
|
||
Building and running the policy is done exactly the same way as a Rego | ||
policy targeting Open Policy Agent. The structure is like: | ||
|
||
``` | ||
. | ||
├── data | ||
│ ├── default-ns.json | ||
│ └── other-ns.json | ||
└── policy.rego | ||
1 directory, 3 files | ||
``` | ||
|
||
## Build | ||
|
||
Let's build our policy by running the following `opa` command: | ||
|
||
```shell | ||
~/gatekeeper-policy » opa build -t wasm -e policy/violation policy.rego | ||
``` | ||
|
||
What this does is build the rego policy, with: | ||
|
||
- `target`: `wasm`. We want to build the policy for the `wasm` target. | ||
- `entrypoint`: `policy/violation`. The entry point is the `violation` | ||
rule inside the `policy` package. | ||
- `policy.rego`: build and include the `policy.rego` file. | ||
|
||
After the build is complete, `opa build` will have generated a | ||
`bundle.tar.gz` file. You can extract the wasm file from it: | ||
|
||
```shell | ||
gatekeeper-policy » tar -xf bundle.tar.gz /policy.wasm | ||
``` | ||
|
||
The tree looks like the following: | ||
|
||
``` | ||
. | ||
├── bundle.tar.gz | ||
├── data | ||
│ ├── default-ns.json | ||
│ └── other-ns.json | ||
├── policy.rego | ||
└── policy.wasm | ||
1 directory, 5 files | ||
``` | ||
|
||
We can now execute our policy! | ||
|
||
## Run | ||
|
||
Let's use `kwctl` to run our policy as follows: | ||
|
||
``` | ||
gatekeeper-policy » kwctl run -e gatekeeper --request-path data/other-ns.json policy.wasm | jq | ||
{ | ||
"uid": "1299d386-525b-4032-98ae-1949f69f9cfc", | ||
"allowed": true | ||
} | ||
``` | ||
|
||
Given that this is our resource created in the namespace called | ||
`other`, this resource is accepted, as expected. Now let's execute a | ||
request that will be rejected by the policy: | ||
|
||
``` | ||
gatekeeper-policy » kwctl run -e gatekeeper --request-path data/default-ns.json policy.wasm | jq | ||
{ | ||
"uid": "1299d386-525b-4032-98ae-1949f69f9cfc", | ||
"allowed": false, | ||
"status": { | ||
"message": "it is forbidden to use the default namespace" | ||
} | ||
} | ||
``` | ||
|
||
As you can see, our Gatekeeper policy rejected this resource as expected. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# Distribute | ||
|
||
Policies have to be annotated for them to be pushed, and eventually | ||
executed by the Kubewarden `policy-server` in a Kubernetes cluster. | ||
|
||
Annotating and distributing our gatekeeper policy is very similar to | ||
distributing an Open Policy Agent one. Let's go through it. | ||
|
||
## Annotating the policy | ||
|
||
We are going to write a `metadata.yaml` file in our policy directory | ||
with contents: | ||
|
||
```yaml | ||
rules: | ||
- apiGroups: [""] | ||
apiVersions: ["*"] | ||
resources: ["*"] | ||
operations: ["CREATE"] | ||
mutating: false | ||
contextAware: false | ||
executionMode: gatekeeper | ||
annotations: | ||
io.kubewarden.policy.title: no-default-namespace | ||
io.kubewarden.policy.description: This policy will reject any resource created inside the default namespace | ||
io.kubewarden.policy.author: The Kubewarden Authors | ||
io.kubewarden.policy.url: https://github.com/kubewarden/some-policy | ||
io.kubewarden.policy.source: https://github.com/kubewarden/some-policy | ||
io.kubewarden.policy.license: Apache-2.0 | ||
io.kubewarden.policy.usage: | | ||
This policy is just an example. | ||
You can write interesting descriptions about the policy here. | ||
``` | ||
As you can see, everything is the same as the Open Policy Agent | ||
version metadata, except for the `executionMode: gatekeeper` bit. | ||
|
||
Let's go ahead and annotate the policy: | ||
|
||
```console | ||
gatekeeper-policy » kwctl annotate policy.wasm --metadata-path metadata.yaml --output-path annotated-policy.wasm | ||
``` | ||
|
||
## Pushing the policy | ||
|
||
Let's push our policy to an OCI registry: | ||
|
||
```console | ||
gatekeeper-policy » kwctl push annotated-policy.wasm registry.my-company.com/kubewarden/no-default-namespace-gatekeeper:v0.0.1 | ||
Policy successfully pushed | ||
``` | ||
|
||
## Deploying on Kubernetes | ||
|
||
We have to pull our policy to our `kwctl` local store first: | ||
|
||
```console | ||
» kwctl pull registry://registry.my-company.com/kubewarden/no-default-namespace-gatekeeper:v0.0.1 | ||
pulling policy... | ||
``` | ||
|
||
We can now create a scaffold `ClusterAdmissionPolicy` resource: | ||
|
||
```console | ||
» kwctl manifest registry://registry.my-company.com/kubewarden/no-default-namespace-gatekeeper:v0.0.1 --type ClusterAdmissionPolicy | ||
--- | ||
apiVersion: policies.kubewarden.io/v1alpha2 | ||
kind: ClusterAdmissionPolicy | ||
metadata: | ||
name: generated-policy | ||
spec: | ||
module: "registry://registry.my-company.com/kubewarden/no-default-namespace-gatekeeper:v0.0.1" | ||
settings: {} | ||
rules: | ||
- apiGroups: | ||
- "" | ||
apiVersions: | ||
- "*" | ||
resources: | ||
- "*" | ||
operations: | ||
- CREATE | ||
mutating: false | ||
``` | ||
|
||
We could now use this `ClusterAdmissionPolicy` resource to deploy our | ||
policy to a Kubernetes cluster. |
Oops, something went wrong.