Skip to content

Commit 686fb86

Browse files
committed
Add OPA documentation
This documentation covers the general Rego information and also two specific sections about Open Policy Agent and Gatekeeper.
1 parent b957f8d commit 686fb86

File tree

11 files changed

+833
-0
lines changed

11 files changed

+833
-0
lines changed

src/SUMMARY.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@
1616
- [Write a mutation policy](./writing-policies/rust/05-mutation-policy.md)
1717
- [Logging](./writing-policies/rust/06-logging.md)
1818
- [Build and distribute](./writing-policies/rust/07-build-and-distribute.md)
19+
- [Rego](./writing-policies/rego/01-intro.md)
20+
- [Open Policy Agent](./writing-policies/rego/open-policy-agent/01-intro.md)
21+
- [Create a new policy](./writing-policies/rego/open-policy-agent/02-create-policy.md)
22+
- [Build and run](./writing-policies/rego/open-policy-agent/03-build-and-run.md)
23+
- [Distribute](./writing-policies/rego/open-policy-agent/04-distribute.md)
24+
- [Gatekeeper](./writing-policies/rego/gatekeeper/01-intro.md)
25+
- [Create a new policy](./writing-policies/rego/gatekeeper/02-create-policy.md)
26+
- [Build and run](./writing-policies/rego/gatekeeper/03-build-and-run.md)
27+
- [Distribute](./writing-policies/rego/gatekeeper/04-distribute.md)
28+
- [Builtin support](./writing-policies/rego/02-builtin-support.md)
1929
- [Go](./writing-policies/go/01-intro.md)
2030
- [Create a new policy](./writing-policies/go/02-scaffold.md)
2131
- [Define policy settings](./writing-policies/go/03-policy-settings.md)

src/writing-policies/rego/01-intro.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Rego
2+
3+
The Rego language is a tailor made language designed to embrace
4+
policies as
5+
code. [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
6+
is a language inspired by Datalog.
7+
8+
There are two ways of writing Rego policies as of today in order to
9+
implement policies as code in Kubernetes: Open Policy Agent and
10+
Gatekeeper.
11+
12+
## One language. Two frameworks
13+
14+
### Open Policy Agent
15+
16+
Open Policy Agent is a project that allows you to implement policies
17+
as code in any project. You can rely on Open Policy Agent for any
18+
policy based check that you might require in your own application,
19+
that will in turn execute the required Rego policies.
20+
21+
In this context, writing policies for Kubernetes is just another way
22+
of exercising Open Policy Agent. By using Kubernetes admission
23+
webhooks, it's possible to leverage Kubernetes' admission webhooks to
24+
evaluate requests using Open Policy Agent, that will in turn execute
25+
the policies written in Rego.
26+
27+
Open Policy Agent has some optional integration with Kubernetes
28+
through its `kube-mgmt` sidecar. When deployed on top of Kubernetes
29+
and next to the Open Policy Agent server evaluating the Rego policies,
30+
it is able to replicate the configured Kubernetes resources into Rego
31+
-- so those Kubernetes resources are visible to all policies. It also
32+
lets you define policies inside Kubernetes' configmaps. You can read
33+
more about it on [its project
34+
page](https://github.com/open-policy-agent/kube-mgmt).
35+
36+
### Gatekeeper
37+
38+
Gatekeeper is very different from Open Policy Agent in this regard. It
39+
is focused exclusively to be used in Kubernetes, and takes advantage
40+
of that as much as it can, making some Kubernetes workflows easier
41+
than Open Policy Agent in many cases.
42+
43+
## Looking at the differences
44+
45+
Both Open Policy Agent and Gatekeeper policies use Rego to describe
46+
their policies as code. However, this is only one part of the
47+
puzzle. Each solution has differences when it comes to writing real
48+
policies in Rego, and we are going to look at those differences in the
49+
next sections.
50+
51+
## Common concepts
52+
53+
### Entry point
54+
55+
The entry point is the name of the rule within a package, and is
56+
therule to be invoked by the runtime when the policy is
57+
instantiated.
58+
59+
## Current limitations
60+
61+
### Context-aware policies
62+
63+
Context-aware policies are policies that don't evaluate the input
64+
request in isolation. They take other factors into account in order to
65+
take a decision. For example, a policy that evaluates namespaced
66+
resources and uses an annotation on the parent namespace to configure
67+
something on the policy. Another example would be a policy that
68+
evaluates `Ingress` resources, but that in order to take a decision
69+
has the list of the already existing `Ingress` resources.
70+
71+
The concept of context-aware policies can also extend to custom
72+
resources, so your policy might want to evaluate a request based on
73+
currently persisted custom resources as well.
74+
75+
Policies written for Open Policy Agent with the `kube-mgmt` sidecar
76+
integration, or Gatekeeper support context-aware policies. Kubewarden
77+
has not yet implemented this functionality, but it is in our plans to
78+
do so.
79+
80+
### Mutating policies
81+
82+
Gatekeeper has support for mutating policies, but Kubewarden has not
83+
yet implemented mutating policies with Gatekeeper compatibility. You
84+
can use policies that use the Kubewarden SDK to write mutating
85+
policies, but at the time of writing, you cannot run Gatekeeper
86+
mutating policies in Kubewarden yet.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Builtin support
2+
3+
Building a policy for the `wasm` target is only half of the problem,
4+
it needs to be executed.
5+
6+
The Open Policy Agent team has a dedicated page you can check in order
7+
to [find out the built-in support
8+
level](https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions).
9+
10+
Every green check in this table means that those built-ins are
11+
implemented regardless of the runtime: they are implemented already on
12+
the policy you have built.
13+
14+
The built-ins marked as `SDK-dependent` are the ones that the host has
15+
to implement -- in this case, Kubewarden. Open Policy Agent and
16+
Gatekeeper may use them depending on the needs of the policy. In any
17+
case, this built-ins are exposed to the policy and any new or existing
18+
policy could depend on them.
19+
20+
These are the [built-ins implemented up until now in
21+
Kubewarden](https://github.com/kubewarden/policy-evaluator/issues/56#issue-983937559).
22+
23+
## Executing policies with missing built-ins
24+
25+
When a policy is instantiated with `kwctl` or the `policy-server`, the
26+
list of built-ins used by the policy will be inspected, and if any of
27+
the used built-ins is missing, the program will abort execution
28+
logging a fatal error reporting what are the missing built-ins.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Gatekeeper
2+
3+
Gatekeeper is a project targeting Kubernetes, and as such, has some
4+
features that are thought out of the box for being integrated with it.
5+
6+
## Compatibility with existing policies
7+
8+
All Gatekeeper policies that you have written already should be
9+
compatible with Kubewarden as we will explain during this chapter.
10+
11+
> **Note**: if this is not the case, please report it to us and we
12+
> will do our best to make sure your policy runs flawlessly with
13+
> Kubewarden.
14+
15+
Policies have to be compiled with the `opa` CLI to the `wasm` target.
16+
17+
In terms of policy execution, you can read more about the [Open Policy
18+
Agent built-in support that is implemented in
19+
Kubewarden](../02-builtin-support.md).
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Create a new policy
2+
3+
Let's implement the same policy that [we wrote with Open Policy
4+
Agent](../open-policy-agent/02-create-policy.md): a policy that
5+
rejects a resource if it's targeting the `default` namespace.
6+
7+
## Requirements
8+
9+
As in the previous section, we will require the following tools:
10+
11+
- `opa`
12+
- `kwctl`
13+
14+
## The policy
15+
16+
Since Gatekeeper is targeting Kubernetes, it has the freedom to be
17+
more handy in what the policy has to return.
18+
19+
With Open Policy Agent we had to construct a whole `AdmissionReview`
20+
object as the response of our policy. With Gatekeeper, we only have to
21+
return none or more violations from our policy entrypoint.
22+
23+
If no violations were reported, the request will be accepted. If one,
24+
or more violations were reported, the request will be rejected.
25+
26+
We create a new folder, named `rego-policy`. Inside of it, we create a
27+
`policy.rego` file with contents:
28+
29+
```rego
30+
package policy
31+
32+
violation[{"msg": msg}] {
33+
input.review.object.metadata.namespace == "default"
34+
msg := "it is forbidden to use the default namespace"
35+
}
36+
```
37+
38+
In this case, our entrypoint is `policy/violation`, and because of how
39+
Rego works, it can either have 1 violation: if the object to be
40+
reviewed is targeting the `default` namespace, or 0 violations
41+
otherwise.
42+
43+
Take a moment to compare this policy with the one we wrote in the Open
44+
Policy Agent section. That one had to build the whole
45+
`AdmissionReview` response, and the inputs were slightly
46+
different. In the Gatekeeper mode, the `AdmissionRequest` object is
47+
provided at the `input.review` attribute. All attributes of the
48+
`AdmissionRequest` are readable along with `object`.
49+
50+
Now, let's create the requests that we are going to evaluate in the
51+
next section.
52+
53+
Let us first create a `default-ns.json` file with the following
54+
contents inside the `data` directory:
55+
56+
```json
57+
{
58+
"apiVersion": "admission.k8s.io/v1",
59+
"kind": "AdmissionReview",
60+
"request": {
61+
"uid": "1299d386-525b-4032-98ae-1949f69f9cfc",
62+
"operation": "CREATE",
63+
"object": {
64+
"kind": "Pod",
65+
"apiVersion": "v1",
66+
"metadata": {
67+
"name": "nginx",
68+
"namespace": "default",
69+
"uid": "04dc7a5e-e1f1-4e34-8d65-2c9337a43e64"
70+
}
71+
}
72+
}
73+
}
74+
```
75+
76+
Now, let's create another `AdmissionReview` object that this time is
77+
targeting a namespace different than the `default` one. Let us name
78+
this file `other-ns.json`. It has the following contents:
79+
80+
```json
81+
{
82+
"apiVersion": "admission.k8s.io/v1",
83+
"kind": "AdmissionReview",
84+
"request": {
85+
"uid": "1299d386-525b-4032-98ae-1949f69f9cfc",
86+
"operation": "CREATE",
87+
"object": {
88+
"kind": "Pod",
89+
"apiVersion": "v1",
90+
"metadata": {
91+
"name": "nginx",
92+
"namespace": "other",
93+
"uid": "04dc7a5e-e1f1-4e34-8d65-2c9337a43e64"
94+
}
95+
}
96+
}
97+
}
98+
```
99+
100+
As you can see, this simulates another pod creation request, this time
101+
under a namespace called `other`.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Build and run
2+
3+
Building and running the policy is done exactly the same way as a Rego
4+
policy targeting Open Policy Agent. The structure is like:
5+
6+
```
7+
.
8+
├── data
9+
│   ├── default-ns.json
10+
│   └── other-ns.json
11+
└── policy.rego
12+
13+
1 directory, 3 files
14+
```
15+
16+
## Build
17+
18+
Let's build our policy by running the following `opa` command:
19+
20+
```shell
21+
~/gatekeeper-policy » opa build -t wasm -e policy/violation policy.rego
22+
```
23+
24+
What this does is build the rego policy, with:
25+
26+
- `target`: `wasm`. We want to build the policy for the `wasm` target.
27+
- `entrypoint`: `policy/violation`. The entry point is the `violation`
28+
rule inside the `policy` package.
29+
- `policy.rego`: build and include the `policy.rego` file.
30+
31+
After the build is complete, `opa build` will have generated a
32+
`bundle.tar.gz` file. You can extract the wasm file from it:
33+
34+
```shell
35+
gatekeeper-policy » tar -xf bundle.tar.gz /policy.wasm
36+
```
37+
38+
The tree looks like the following:
39+
40+
```
41+
.
42+
├── bundle.tar.gz
43+
├── data
44+
│   ├── default-ns.json
45+
│   └── other-ns.json
46+
├── policy.rego
47+
└── policy.wasm
48+
49+
1 directory, 5 files
50+
```
51+
52+
We can now execute our policy!
53+
54+
## Run
55+
56+
Let's use `kwctl` to run our policy as follows:
57+
58+
```
59+
gatekeeper-policy » kwctl run -e gatekeeper --request-path data/other-ns.json policy.wasm | jq
60+
{
61+
"uid": "1299d386-525b-4032-98ae-1949f69f9cfc",
62+
"allowed": true
63+
}
64+
```
65+
66+
Given that this is our resource created in the namespace called
67+
`other`, this resource is accepted, as expected. Now let's execute a
68+
request that will be rejected by the policy:
69+
70+
```
71+
gatekeeper-policy » kwctl run -e gatekeeper --request-path data/default-ns.json policy.wasm | jq
72+
{
73+
"uid": "1299d386-525b-4032-98ae-1949f69f9cfc",
74+
"allowed": false,
75+
"status": {
76+
"message": "it is forbidden to use the default namespace"
77+
}
78+
}
79+
```
80+
81+
As you can see, our Gatekeeper policy rejected this resource as expected.

0 commit comments

Comments
 (0)