Skip to content

Commit

Permalink
Merge pull request #368 from jhkrug/tutorial-rego
Browse files Browse the repository at this point in the history
Refresh language for tutorial on Rego.
  • Loading branch information
jhkrug authored Mar 6, 2024
2 parents 56d5100 + b7957ae commit 3f5f058
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 313 deletions.
110 changes: 51 additions & 59 deletions docs/tutorials/writing-policies/rego/01-intro-rego.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,99 +13,91 @@ doc-topic: [writing-policies, rego, introduction]
</head>

:::note
Rego support has been introduced starting from these releases:
Rego support is available from these releases:

- kwctl: v0.2.0
- policy-server: v0.2.0

Rego policies support context aware data starting from the Kubewarden 1.9 release.
Rego policies support context aware data from the Kubewarden 1.9 release.

:::

The Rego language is a domain specific language designed to embrace
policies as
code. [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
is a language inspired by Datalog.
The Rego language is a domain specific language to enable policies as code.
[Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
is a language inspired by [Datalog](https://en.wikipedia.org/wiki/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.
There are two ways of writing Rego policies to implement policies as code in Kubernetes,
Open Policy Agent and Gatekeeper.

While the next couple of sections detail how the two frameworks are different
even though the same language is used, if you're more interested in the Kubewarden
implementation you may directly visit the framework-specific documentation we have linked below.
The next couple of sections shows how the two frameworks are different,
even though using the same language.
If you're more interested in the Kubewarden implementation,
you can directly visit the framework-specific documentation linked below.

- [Open Policy Agent](../rego/open-policy-agent/01-intro.md)
- [Gatekeeper](../rego/gatekeeper/01-intro.md)

## One language. Two frameworks
## One language, two frameworks

### Open Policy Agent
### Open Policy Agent (OPA)

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.
Open Policy Agent is a project that allowing you to implement policies as code in any project.
You can use OPA for any policy based check your application needs.

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 evaluate requests using Open Policy Agent,
that will in turn execute the policies written in Rego.
In this context, writing policies for Kubernetes is simply exercising OPA.
By using Kubernetes admission webhooks,
it's possible to evaluate requests using OPA,
which executes 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' ConfigMap objects. You can
read more about it on [its project
page](https://github.com/open-policy-agent/kube-mgmt).
OPA has optional integration with Kubernetes through its `kube-mgmt` sidecar.
When deployed on Kubernetes, and with the OPA server evaluating the Rego policies,
it's able to replicate the configured Kubernetes resources into Rego.
So, those Kubernetes resources are visible to all policies.
With OPA you can define policies inside Kubernetes' ConfigMap objects.
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.
Gatekeeper focuses, only, on use in Kubernetes,
and takes advantage of that as much as possible,
making Kubernetes workflows easier than OPA in certain 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.
Both OPA and Gatekeeper policies use Rego to describe policies as code.
Each solution has differences when it comes to writing policies in Rego,
as shown in the next sections.

## Entry point

The entry point is the name of a rule within a package, and is the
rule to be invoked by the runtime when the policy is instantiated.
The entry point is the name of a rule within a package,
and is the rule invoked by the runtime when the policy runs.

## Current limitations

### Context-aware policies

Context-aware policies are policies that don't evaluate the input
request in isolation. They take other factors into account in order to
take a decision. For example, a policy that evaluates namespaced
resources and uses an annotation on the parent namespace to configure
something on the policy. Another example would be a policy that
evaluates `Ingress` resources, but that in order to take a decision
has the list of the already existing `Ingress` resources.
Context-aware policies are policies that don't evaluate the input request in isolation.
They take other factors into account to take a decision.
For example, a policy that evaluates namespaced resources,
and uses an annotation,
on the parent namespace to configure something in the policy.
Another example would be a policy that evaluates `Ingress` resources,
but to make a decision also has the list of the existing `Ingress` resources.

The concept of context-aware policies can also extend to custom
resources, so your policy might want to evaluate a request based on
currently persisted custom resources as well.
The idea of context-aware policies can also extend to custom resources,
so your policy might want to evaluate a request based on currently used custom resources as well.

Both Open Policy Agent and Gatekeeper support context-aware
policies starting from the Kubewarden 1.9 release.
Both OPA and Gatekeeper support context-aware policies,
starting from the Kubewarden 1.9 release.

More details about context aware policies can be found [here](../../../reference/spec/05-context-aware-policies.md).
More details about context aware policies are
[here](../../../reference/spec/05-context-aware-policies.md).

### Mutating policies

Gatekeeper has support for mutating policies, but Kubewarden has not
yet implemented mutating policies with Gatekeeper compatibility. You
can use policies that use the Kubewarden SDK to write mutating
policies, but at the time of writing, you cannot run Gatekeeper
mutating policies in Kubewarden yet.
Gatekeeper has support for mutating policies,
but Kubewarden hasn't yet implemented mutating policies with Gatekeeper compatibility.
You can use policies that use the Kubewarden SDK to write mutating policies,
but currently, you can't run Gatekeeper mutating policies in Kubewarden.
53 changes: 24 additions & 29 deletions docs/tutorials/writing-policies/rego/02-builtin-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,32 @@ doc-topic: [writing-policies, rego, built-in-support]
<link rel="canonical" href="https://docs.kubewarden.io/tutorials/writing-policies/rego/builtin-support"/>
</head>

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).

When building a Rego policy into a WebAssembly module, some of these
built-in functions are going to be implemented inside of the Wasm file
itself (the built-ins marked with a green check in the previously
linked table) -- regardless of the runtime; while others have to be
provided at execution time by the WebAssembly runtime evaluating the
module.

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.

There are still some built-ins that are not yet provided by us,
however, based on the policies we have seen in the open, the ones we
already support should be enough for the majority of Kubernetes users.
Additionally, to building a policy for the `wasm` target, it needs running.

The Open Policy Agent (OPA) team has a page you can check for
[built-in support](https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions).

When building a Rego policy into a WebAssembly module,
certain of these built-in functions are in the Wasm file itself.
They're the built-ins marked with a green check in the linked built-in page.
The WebAssembly runtime evaluating the module, provides the others.

The built-ins marked as `SDK-dependent` are the ones that the host has to implement,
in this case, Kubewarden.
OPA and Gatekeeper may use them depending on the needs of the policy.
In any case, these built-ins are exposed to the policy and any new or existing policy could depend on them.

There are still ceratin built-ins that aren't yet provided by Kubewarden,
however, based on the policies seen in the open,
the ones already supported should be enough for the most Kubernetes users.

[This GitHub issue](https://github.com/kubewarden/policy-evaluator/issues/56)
keeps track of the Rego built-ins we have still to implement. Feel free to
comment over there to prioritize our work.
keeps track of the Rego built-ins we've still to implement.
Feel free to comment there to help prioritize our work.

## Executing policies with missing built-ins

When a policy is instantiated with `kwctl` or with `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.
When a policy is run with `kwctl` or with `policy-server`,
the list of built-ins used by the policy is inspected.
If any of the used built-ins are missing,
the program stops execution logging a fatal error reporting the missing built-ins.
21 changes: 9 additions & 12 deletions docs/tutorials/writing-policies/rego/gatekeeper/01-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,25 @@ doc-topic: [writing-policies, rego, gatekeeper, introduction]
</head>

:::note
Gatekeeper support has been introduced starting from these releases:
Gatekeeper support starts from these releases:

* kwctl: v0.2.0
* policy-server: v0.2.0
:::

Gatekeeper is a project targeting Kubernetes, and as such, has some
features that are thought out of the box for being integrated with it.
Gatekeeper is a project targeting Kubernetes with out-of-the-box features for integration.

## Compatibility with existing policies

All the existing Gatekeeper policies should be compatible with
Kubewarden as we will explain during this chapter.
All existing Gatekeeper policies should be compatible with Kubewarden as explained in this chapter.

:::info
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.
If you find this not to be true, for your Gatekeeper policies,
report it,
and we'll work to ensure your Gatekeeper policy runs with Kubewarden.
:::

Policies have to be compiled with the `opa` CLI to the `wasm` target.
Policies need compilation with the `opa` CLI to `wasm` target.

In terms of policy execution, you can read more about the [Open Policy
Agent built-in support that is implemented in
Kubewarden](../builtin-support).
For policy execution, you can read more about the Open Policy Agent
[built-in support](../builtin-support) in Kubewarden.
64 changes: 30 additions & 34 deletions docs/tutorials/writing-policies/rego/gatekeeper/02-create-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,31 @@ doc-topic: [writing-policies, rego, gatekeeper, create-policy]
<link rel="canonical" href="https://docs.kubewarden.io/tutorials/writing-policies/rego/gatekeeper/create-policy"/>
</head>

Let's implement the same policy that [we wrote with Open Policy
Agent](../open-policy-agent/create-policy): a policy that
rejects a resource if it's targeting the `default` namespace.
For this tutorial you'll implement the same policy that you wrote with
[Open Policy Agent](../open-policy-agent/create-policy).
Namely, a policy that rejects a resource if it's targeting the `default` namespace.

:::note
We also provide a GitHub repository template
that you can use to quickly port an existing policy.

Check it out: [kubewarden/gatekeeper-policy-template](https://github.com/kubewarden/gatekeeper-policy-template)
There is a
[repository template](https://github.com/kubewarden/gatekeeper-policy-template)
that you can use as a base to port an existing policy.
:::

## Requirements

As in the previous section, we will require the following tools:
You need the following tools:

- `opa`
- `kwctl`

## The policy

Gatekeeper policies must return none or more violation objects. If no
violations are reported, the request will be accepted. If one, or more
violations are reported, the request will be rejected.
Gatekeeper policies must return none or more violation objects.
If no violations are reported, the request is accepted.
If one, or more violations are reported, the request is rejected.

We create a new folder, named `rego-policy`. Inside of it, we create a
`policy.rego` file with contents:
Create a new folder, named `rego-policy`.
In it, create a `policy.rego` file with the contents:

```rego
package policy
Expand All @@ -48,27 +47,23 @@ violation[{"msg": msg}] {
}
```

In this case, our entrypoint is `policy/violation`, and because of how
Rego works, the policy can have the following outcomes:
In this case, the entrypoint is `policy/violation`,
and due to how Rego works, the policy can have the following outcomes:

- return 1 violation: the object being reviewed is targeting the
default namespace.
- Return 1 violation: the object reviewed is targeting the default namespace.

- return 0 violations: the object being reviewed is compliant with the
policy.
- Return 0 violations: the object reviewed is compliant with the policy.

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`.
Take a moment to compare this policy with the one written 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 with 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.
Now, you can create the requests to evaluate in the next section.

Let us first create a `default-ns.json` file with the following
contents inside the `data` directory:
You first create a `default-ns.json` file with the following contents inside the `data` directory:

```json
{
Expand All @@ -90,9 +85,10 @@ contents inside the `data` directory:
}
```

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:
Now, create another `AdmissionReview` object that, this time,
is targeting a namespace different to the `default` one.
Name this file `other-ns.json`.
It has the following contents:

```json
{
Expand All @@ -114,5 +110,5 @@ this file `other-ns.json`. It has the following contents:
}
```

As you can see, this simulates another pod creation request, this time
under a namespace called `other`.
You can see, this simulates another pod creation request,
this time under a namespace called `other`.
Loading

0 comments on commit 3f5f058

Please sign in to comment.