diff --git a/slides/k8s/kyverno-colors.md b/slides/k8s/kyverno-colors.md
new file mode 100644
index 00000000..c07475d6
--- /dev/null
+++ b/slides/k8s/kyverno-colors.md
@@ -0,0 +1,249 @@
+## Painting pods
+
+- As an example, we'll implement a policy regarding "Pod color"
+
+- The color of a Pod is the value of the label `color`
+
+- Example: `kubectl label pod hello color=yellow` to paint a Pod in yellow
+
+- We want to implement the following policies:
+
+ - color is optional (i.e. the label is not required)
+
+ - if color is set, it *must* be `red`, `green`, or `blue`
+
+ - once the color has been set, it cannot be changed
+
+ - once the color has been set, it cannot be removed
+
+---
+
+## Immutable primary colors, take 1
+
+- First, we will add a policy to block forbidden colors
+
+ (i.e. only allow `red`, `green`, or `blue`)
+
+- One possible approach:
+
+ - *match* all pods that have a `color` label that is not `red`, `green`, or `blue`
+
+ - *deny* these pods
+
+- We could also *match* all pods, then *deny* with a condition
+
+---
+
+.small[
+```yaml
+@@INCLUDE[k8s/kyverno-pod-color-1.yaml]
+```
+]
+
+---
+
+## Testing without the policy
+
+- First, let's create a pod with an "invalid" label
+
+ (while we still can!)
+
+- We will use this later
+
+.lab[
+
+- Create a pod:
+ ```bash
+ kubectl run test-color-0 --image=nginx
+ ```
+
+- Apply a color label:
+ ```bash
+ kubectl label pod test-color-0 color=purple
+ ```
+
+]
+
+---
+
+## Load and try the policy
+
+.lab[
+
+- Load the policy:
+ ```bash
+ kubectl apply -f ~/container.training/k8s/kyverno-pod-color-1.yaml
+ ```
+
+- Create a pod:
+ ```bash
+ kubectl run test-color-1 --image=nginx
+ ```
+
+- Try to apply a few color labels:
+ ```bash
+ kubectl label pod test-color-1 color=purple
+ kubectl label pod test-color-1 color=red
+ kubectl label pod test-color-1 color-
+ ```
+
+]
+
+---
+
+## Immutable primary colors, take 2
+
+- Next rule: once a `color` label has been added, it cannot be changed
+
+ (i.e. if `color=red`, we can't change it to `color=blue`)
+
+- Our approach:
+
+ - *match* all pods
+
+ - add a *precondition* matching pods that have a `color` label
+
+ (both in their "before" and "after" states)
+
+ - *deny* these pods if their `color` label has changed
+
+- Again, other approaches are possible!
+
+---
+
+.small[
+```yaml
+@@INCLUDE[k8s/kyverno-pod-color-2.yaml]
+```
+]
+
+---
+
+## Comparing "old" and "new"
+
+- The fields of the webhook payload are available through `{{ request }}`
+
+- For UPDATE requests, we can access:
+
+ `{{ request.oldObject }}` → the object as it is right now (before the request)
+
+ `{{ request.object }}` → the object with the changes made by the request
+
+---
+
+## Missing labels
+
+- We can access the `color` label through `{{ request.object.metadata.labels.color }}`
+
+- If we reference a label (or any field) that doesn't exist, the policy fails
+
+ (with an error similar to `JMESPAth query failed: Unknown key ... in path`)
+
+- If a precondition fails, the policy will be skipped altogether (and ignored!)
+
+- To work around that, [use an OR expression][non-existence-checks]:
+
+ `{{ requests.object.metadata.labels.color || '' }}`
+
+[non-existence-checks]: https://kyverno.io/docs/policy-types/cluster-policy/jmespath/#non-existence-checks
+
+---
+
+## Load and try the policy
+
+.lab[
+
+- Load the policy:
+ ```bash
+ kubectl apply -f ~/container.training/k8s/kyverno-pod-color-2.yaml
+ ```
+
+- Create a pod:
+ ```bash
+ kubectl run test-color-2 --image=nginx
+ ```
+
+- Try to apply a few color labels:
+ ```bash
+ kubectl label pod test-color-2 color=purple
+ kubectl label pod test-color-2 color=red
+ kubectl label pod test-color-2 color=blue --overwrite
+ ```
+
+]
+
+---
+
+## Immutable primary colors, take 3
+
+- Last rule: once a `color` label has been added, it cannot be removed
+
+- Our approach is to match all pods that:
+
+ - *had* a `color` label (in `request.oldObject`)
+
+ - *don't have* a `color` label (in `request.Object`)
+
+- And *deny* these pods
+
+- Again, other approaches are possible!
+
+---
+
+.small[
+```yaml
+@@INCLUDE[k8s/kyverno-pod-color-3.yaml]
+```
+]
+
+---
+
+## Load and try the policy
+
+.lab[
+
+- Load the policy:
+ ```bash
+ kubectl apply -f ~/container.training/k8s/kyverno-pod-color-3.yaml
+ ```
+
+- Create a pod:
+ ```bash
+ kubectl run test-color-3 --image=nginx
+ ```
+
+- Try to apply a few color labels:
+ ```bash
+ kubectl label pod test-color-3 color=purple
+ kubectl label pod test-color-3 color=red
+ kubectl label pod test-color-3 color-
+ ```
+
+]
+
+---
+
+## Background checks
+
+- What about the `test-color-0` pod that we create initially?
+
+ (remember: we did set `color=purple`)
+
+- We can see the infringing Pod in a PolicyReport
+
+.lab[
+
+- Check that the pod still an "invalid" color:
+ ```bash
+ kubectl get pods -L color
+ ```
+
+- List PolicyReports:
+ ```bash
+ kubectl get policyreports
+ kubectl get polr
+ ```
+
+]
+
+(Sometimes it takes a little while for the infringement to show up, though.)
diff --git a/slides/k8s/kyverno-ingress.md b/slides/k8s/kyverno-ingress.md
new file mode 100644
index 00000000..acf763c7
--- /dev/null
+++ b/slides/k8s/kyverno-ingress.md
@@ -0,0 +1,223 @@
+## Detecting duplicate Ingress routes
+
+- What happens when two Ingress resources have the same host+path?
+
+--
+
+- Undefined behavior!
+
+--
+
+- Possibilities:
+
+ - one of the Ingress rules is ignored (newer, older, lexicographic, random...)
+
+ - both Ingress rules are ignored
+
+ - traffic is randomly processed by both rules (sort of load balancing)
+
+ - creation of the second resource is blocked by an admission policy
+
+--
+
+- Can we implement that last option with Kyverno? 🤔
+
+---
+
+## General idea
+
+- When a new Ingress resource is created:
+
+ *check if there is already an identical Ingress resource*
+
+- We'll want to use the `apiCall` feature
+
+ (to retrieve all existing Ingress resources across all Namespaces)
+
+- Problem: we don't care about strict equality
+
+ (there could be different labels, annotations, TLS configuration)
+
+- Problem: an Ingress resource is a collection of *rules*
+
+ (we want to check if any rule of the new Ingress...
+
...conflicts with any rule of an existing Ingress)
+
+---
+
+## Good news, everyone
+
+- There is an example in the Kyverno documentation!
+
+ [Unique Ingress Host and Path][kyverno-unique-ingress]
+
+--
+
+- Unfortunately, the example doesn't really work
+
+ (at least as of [Kyverno 1.16 / January 2026][kyverno-unique-ingress-github])
+
+- Can you see problems with it?
+
+--
+
+- Suggestion: load the policy and make some experiments!
+
+ (remember to switch the `validationFailureAction` to `Enforce` for easier testing)
+
+[kyverno-unique-ingress]: https://kyverno.io/policies/other/unique-ingress-host-and-path/unique-ingress-host-and-path/
+[kyverno-unique-ingress-github]: https://github.com/kyverno/policies/blob/release-1.16/other/unique-ingress-host-and-path/unique-ingress-host-and-path.yaml
+
+---
+
+## Problem - no `host`
+
+- If we try to create an Ingress without specifying the `host`:
+ ```
+ JMESPath query failed: Unknown key "host" in path
+ ```
+
+- In some cases, this could be a feature
+
+ (maybe we don't want to allow Ingress rules without a `host`!)
+
+---
+
+## Problem - no UPDATE
+
+- If we try to modify an existing Ingress, the modification will be blocked
+
+- This is because the "new" Ingress rules are checked against "existing" rules
+
+- When we CREATE a new Ingress, its rules don't exist yet (no conflict)
+
+- When we UPDATE an existing Ingress, its rules will show up in the existing rules
+
+- By definition, a rule will always conflict with itself
+
+- So UPDATE requests will always be blocked
+
+- If we exclude UPDATE operations, then it will be possible to introduce conflicts
+
+ (by modifying existing Ingress resources to add/edit rules in them)
+
+- This problem makes the policy useless as it is (unless we completely block updates)
+
+---
+
+## Problem - poor UX
+
+- When the policy detects a conflict, it doesn't say which other resource is involved
+
+- Sometimes, it's possible to find it manually
+
+ (with a bunch of clever `kubectl get ingresses --all-namespaces` commands)
+
+- Sometimes, we don't have read permissions on the conflicting resource
+
+ (e.g. if it's in a different Namespace that we cannot access)
+
+- It would be nice if the policy could report the exact Ingress and Namespace involved
+
+---
+
+## Problem - useless block
+
+- There is a `preconditions` block to ignore `DELETE` operations
+
+- This is useless, as the default is to match only `CREATE` and `UPDATE` requests
+
+ (See the [documentation about match statements][kyverno-match])
+
+- This block can be safely removed
+
+[kyverno-patch]: https://kyverno.io/docs/policy-types/cluster-policy/match-exclude/#match-statements
+
+---
+
+## Solution - no `host`
+
+- In Kyverno, when doing a lookup, the way to handle non-existent keys is with a `||`
+
+- For instance, replace `{{element.host}}` with `{{element.host||''}}`
+
+ (or a placeholder value like `{{element.host||'NOHOST'}}`)
+
+---
+
+## Solution - no UPDATE
+
+- When retrieving existing Ingress resources, we need to exclude the current one
+
+- This can look like this:
+ ```yaml
+ context:
+ - name: ingresses
+ apiCall:
+ urlPath: "/apis/networking.k8s.io/v1/ingresses"
+ jmesPath: |
+ items[?
+ metadata.namespace!='{{request.object.metadata.namespace}}'
+ ||
+ metadata.name!='{{request.object.metadata.name}}'
+ ]
+ ```
+
+---
+
+## Solution - poor UX
+
+- Ideally, when there is a conflict, we'd like to display a message like this one:
+ ```
+ Ingress host+path combinations must be unique across the cluster.
+ This Ingress contains a rule for host 'www.example.com' and path '/',
+ which conflicts with Ingress 'example' in Namespace 'default'.
+ ```
+
+- This requires a significant refactor of the policy logic
+
+- Instead of:
+
+ *loop on rules; filter by rule's host; find if there is any common path*
+
+- We need e.g.:
+
+ *loop on rules; nested loop on paths; filter ingresses with conflicts*
+
+- This requires nested loops, and way to access the `element` of each nested loop
+
+---
+
+## Nested loops
+
+- As of January 2026, this isn't very well documented
+
+ (author's note: I had to [dive into Kyverno's code][kyverno-nested-element] to figure it out...)
+
+- The trick is that the outer loop's element is `element0`, the next one is `element1`, etc.
+
+- Additionally, there is a bug in Kyverno's context handling when defining a variable in a loop
+
+ (the variable needs to be defined at the top-level, with e.g. a dummy value)
+
+TODO: propose a PR to Kyverno's documentation! 🤓💡
+
+[kyverno-nested-element]: https://github.com/kyverno/kyverno/blob/5d5345ec3347f4f5c281652461d42231ea3703e5/pkg/engine/context/context.go#L284
+
+---
+
+## Putting it all together
+
+- Try to write a Kyverno policy to detect conflicting Ingress resources
+
+- Make sure to test the following edge cases:
+
+ - rules that don't define a host (e.g. `kubectl create ingress test --rule=/=test:80`)
+
+ - ingresses with multiple rules
+
+ - no-op edits (e.g. adding a label or annotation)
+
+ - conflicting edits (e.g. adding/editing a rule that adds a conflict)
+
+ - rules for `host1/path1` and `host2/path2` shouldn't conflict with `host1/path2`
diff --git a/slides/k8s/kyverno.md b/slides/k8s/kyverno.md
index 675d5cad..92a7c19b 100644
--- a/slides/k8s/kyverno.md
+++ b/slides/k8s/kyverno.md
@@ -4,17 +4,16 @@
- It has many use cases, including:
- - validating resources when they are created/edited
-
(blocking or logging violations)
+ - enforcing or giving warnings about best practices or misconfigurations
+
(e.g. `:latest` images, healthchecks, requests and limits...)
+
+ - tightening security
+
(possibly for multitenant clusters)
- preventing some modifications
(e.g. restricting modifications to some fields, labels...)
- - modifying resources automatically
-
- - generating resources automatically
-
- - clean up resources automatically
+ - modifying, generating, cleaning up resources automatically
---
@@ -118,14 +117,6 @@
---
-## Kyverno in action
-
-- We're going to install Kyverno on our cluster
-
-- Then, we will use it to implement a few policies
-
----
-
## Installing Kyverno
The recommended [installation method][install-kyverno] is to use Helm charts.
@@ -150,9 +141,9 @@ The recommended [installation method][install-kyverno] is to use Helm charts.
- Which resources does it *select?*
- - can specify resources to *match* and/or *exclude*
+ - *match* and/or *exclude* resources
- - can specify *kinds* and/or *selector* and/or users/roles doing the action
+ - match by *kind*, *selector*, *namespace selector*, user/roles doing the action...
- Which operation should be done?
@@ -164,183 +155,47 @@ The recommended [installation method][install-kyverno] is to use Helm charts.
---
-## Painting pods
+## Validating objects
-- As an example, we'll implement a policy regarding "Pod color"
+Example: [require resource requests and limits][kyverno-requests-limits].
-- The color of a Pod is the value of the label `color`
-
-- Example: `kubectl label pod hello color=yellow` to paint a Pod in yellow
-
-- We want to implement the following policies:
-
- - color is optional (i.e. the label is not required)
-
- - if color is set, it *must* be `red`, `green`, or `blue`
-
- - once the color has been set, it cannot be changed
-
- - once the color has been set, it cannot be removed
-
----
-
-## Immutable primary colors, take 1
-
-- First, we will add a policy to block forbidden colors
-
- (i.e. only allow `red`, `green`, or `blue`)
-
-- One possible approach:
-
- - *match* all pods that have a `color` label that is not `red`, `green`, or `blue`
-
- - *deny* these pods
-
-- We could also *match* all pods, then *deny* with a condition
-
----
-
-.small[
```yaml
-@@INCLUDE[k8s/kyverno-pod-color-1.yaml]
+validate:
+ message: "CPU and memory resource requests and memory limits are required."
+ pattern:
+ spec:
+ containers:
+ - resources:
+ requests:
+ memory: "?*"
+ cpu: "?*"
+ limits:
+ memory: "?*"
```
-]
+
+(The full policy also has sections for `initContainers` and `ephemeralContainers`.)
+
+[kyverno-requests-limits]: https://kyverno.io/policies/best-practices/require-pod-requests-limits/require-pod-requests-limits/
---
-## Testing without the policy
+## Optional fields
-- First, let's create a pod with an "invalid" label
+Example: [disallow `NodePort` Services][kyverno-disallow-nodeports].
- (while we still can!)
-
-- We will use this later
-
-.lab[
-
-- Create a pod:
- ```bash
- kubectl run test-color-0 --image=nginx
- ```
-
-- Apply a color label:
- ```bash
- kubectl label pod test-color-0 color=purple
- ```
-
-]
-
----
-
-## Load and try the policy
-
-.lab[
-
-- Load the policy:
- ```bash
- kubectl apply -f ~/container.training/k8s/kyverno-pod-color-1.yaml
- ```
-
-- Create a pod:
- ```bash
- kubectl run test-color-1 --image=nginx
- ```
-
-- Try to apply a few color labels:
- ```bash
- kubectl label pod test-color-1 color=purple
- kubectl label pod test-color-1 color=red
- kubectl label pod test-color-1 color-
- ```
-
-]
-
----
-
-## Immutable primary colors, take 2
-
-- Next rule: once a `color` label has been added, it cannot be changed
-
- (i.e. if `color=red`, we can't change it to `color=blue`)
-
-- Our approach:
-
- - *match* all pods
-
- - add a *precondition* matching pods that have a `color` label
-
- (both in their "before" and "after" states)
-
- - *deny* these pods if their `color` label has changed
-
-- Again, other approaches are possible!
-
----
-
-.small[
```yaml
-@@INCLUDE[k8s/kyverno-pod-color-2.yaml]
+validate:
+ message: "Services of type NodePort are not allowed."
+ pattern:
+ spec:
+ =(type): "!NodePort"
```
-]
----
+`=(...):` means that the field is optional.
-## Comparing "old" and "new"
+`type: "!NodePort"` would *require* the field to exist, but be different from `NodePort`.
-- The fields of the webhook payload are available through `{{ request }}`
-
-- For UPDATE requests, we can access:
-
- `{{ request.oldObject }}` → the object as it is right now (before the request)
-
- `{{ request.object }}` → the object with the changes made by the request
-
----
-
-## Missing labels
-
-- We can access the `color` label through `{{ request.object.metadata.labels.color }}`
-
-- If we reference a label (or any field) that doesn't exist, the policy fails
-
- (with an error similar to `JMESPAth query failed: Unknown key ... in path`)
-
-- If a precondition fails, the policy will be skipped altogether (and ignored!)
-
-- To work around that, [use an OR expression][non-existence-checks]:
-
- `{{ requests.object.metadata.labels.color || '' }}`
-
-- Note that in older versions of Kyverno, this wasn't always necessary
-
- (e.g. in *preconditions*, a missing label would evalute to an empty string)
-
-[non-existence-checks]: https://kyverno.io/docs/policy-types/cluster-policy/jmespath/#non-existence-checks
-
----
-
-## Load and try the policy
-
-.lab[
-
-- Load the policy:
- ```bash
- kubectl apply -f ~/container.training/k8s/kyverno-pod-color-2.yaml
- ```
-
-- Create a pod:
- ```bash
- kubectl run test-color-2 --image=nginx
- ```
-
-- Try to apply a few color labels:
- ```bash
- kubectl label pod test-color-2 color=purple
- kubectl label pod test-color-2 color=red
- kubectl label pod test-color-2 color=blue --overwrite
- ```
-
-]
+[kyverno-disallow-nodeports]: https://kyverno.io/policies/best-practices/restrict-node-port/restrict-node-port/
---
@@ -354,7 +209,7 @@ The recommended [installation method][install-kyverno] is to use Helm charts.
(more on that later)
-- We need to change the `failureAction` to `Enforce`
+- We (very often) need to change the `failureAction` to `Enforce`
---
@@ -382,7 +237,7 @@ The recommended [installation method][install-kyverno] is to use Helm charts.
- Existing objects are not affected
- (e.g. if we have a pod with `color=pink` *before* installing our policy)
+ (e.g. if we create "invalid" objects *before* installing the policy)
- Kyvero can also run checks in the background, and report violations
@@ -390,128 +245,80 @@ The recommended [installation method][install-kyverno] is to use Helm charts.
- `background: true/false` controls that
-- When would we want to disabled it? 🤔
-
---
-## Accessing `AdmissionRequest` context
+## Loops
-- In some of our policies, we want to prevent an *update*
+Example: [require image tags][kyverno-disallow-latest].
- (as opposed to a mere *create* operation)
+This uses `request`, which gives access to the `AdmissionRequest` payload.
-- We want to compare the *old* and *new* version
+`request` has an `object` field containing the object that we're validating.
- (to check if a specific label was removed)
-
-- The `AdmissionRequest` object has `object` and `oldObject` fields
-
- (the `AdmissionRequest` object is the thing that gets submitted to the webhook)
-
-- We access the `AdmissionRequest` object through `{{ request }}`
-
----
-
-## `{{ request }}`
-
-- The `{{ request }}` context is only available when there is an `AdmissionRequest`
-
-- When a resource is "at rest", there is no `{{ request }}` (and no old/new)
-
-- Therefore, a policy that uses `{{ request }}` cannot validate existing objects
-
- (it can only be used when an object is actually created/updated/deleted)
-
---
-
-- *Well, actually...*
-
---
-
-- Kyverno exposes `{{ request.object }}` and `{{ request.namespace }}`
-
- (see [the documentation](https://kyverno.io/docs/policy-reports/background/) for details!)
-
----
-
-## Immutable primary colors, take 3
-
-- Last rule: once a `color` label has been added, it cannot be removed
-
-- Our approach is to match all pods that:
-
- - *had* a `color` label (in `request.oldObject`)
-
- - *don't have* a `color` label (in `request.Object`)
-
-- And *deny* these pods
-
-- Again, other approaches are possible!
-
----
-
-.small[
```yaml
-@@INCLUDE[k8s/kyverno-pod-color-3.yaml]
+validate:
+ message: "An image tag is required."
+ foreach:
+ - list: "request.object.spec.containers"
+ pattern:
+ image: "*:*"
```
-]
+
+Note: again, there should also be an entry for `initContainers` and `ephemeralContainers`.
+
+[kyverno-disallow-latest]: https://kyverno.io/policies/best-practices/disallow-latest-tag/disallow-latest-tag/
---
-## Load and try the policy
+class: extra-details
-.lab[
+## ...Or not to loop
-- Load the policy:
- ```bash
- kubectl apply -f ~/container.training/k8s/kyverno-pod-color-3.yaml
- ```
+Requiring image tags can also be achieved like this:
-- Create a pod:
- ```bash
- kubectl run test-color-3 --image=nginx
- ```
-
-- Try to apply a few color labels:
- ```bash
- kubectl label pod test-color-3 color=purple
- kubectl label pod test-color-3 color=red
- kubectl label pod test-color-3 color-
- ```
-
-]
+```yaml
+validate:
+ message: "An image tag is required."
+ pattern:
+ spec:
+ containers:
+ - image: "*:*"
+ =(initContainers):
+ - image: "*:*"
+ =(ephemeralContainers):
+ - image: "*:*"
+```
---
-## Background checks
+## `request` and other variables
-- What about the `test-color-0` pod that we create initially?
+- `request` gives us access to the `AdmissionRequest` payload
- (remember: we did set `color=purple`)
+- This gives us access to a bunch of interesting fields:
-- We can see the infringing Pod in a PolicyReport
+ `request.operation`: CREATE, UPDATE, DELETE, or CONNECT
-.lab[
+ `request.object`: the object being created or modified
-- Check that the pod still an "invalid" color:
- ```bash
- kubectl get pods -L color
- ```
+ `request.oldObject`: the object being modified (only for UPDATE)
-- List PolicyReports:
- ```bash
- kubectl get policyreports
- kubectl get polr
- ```
+ `request.userInfo`: information about the user making the API request
-]
+- `object` and `oldObject` are very convenient to block specific *modifications*
-(Sometimes it takes a little while for the infringement to show up, though.)
+ (e.g. making some labels or annotations immutable)
+
+(See [here][kyverno-request] for details.)
+
+[kyverno-request]: https://kyverno.io/docs/policy-types/cluster-policy/variables/#variables-from-admission-review-requests
---
## Generating objects
+- Let's review a fairly common use-case...
+
- When we create a Namespace, we also want to automatically create:
- a LimitRange (to set default CPU and RAM requests and limits)
@@ -552,13 +359,13 @@ Note: the `apiVersion` field appears to be optional.
- Excerpt:
```yaml
- generate:
- kind: LimitRange
- name: default-limitrange
- namespace: "{{request.object.metadata.name}}"
- data:
- spec:
- limits:
+ generate:
+ kind: LimitRange
+ name: default-limitrange
+ namespace: "{{request.object.metadata.name}}"
+ data:
+ spec:
+ limits:
```
- Note that we have to specify the `namespace`
@@ -567,11 +374,80 @@ Note: the `apiVersion` field appears to be optional.
---
+## Templates and JMESpath
+
+- We can use `{{ }}` templates in Kyverno policies
+
+ (when generating or validating resources; in conditions, pre-conditions...)
+
+- This lets us access `request` as well as [a few other variables][kyverno-variables]
+
+- We can also use JMESPath expressions, for instance:
+
+ `{{request.object.spec.containers[?name=='worker'].image}}`
+
+ `{{request.object.spec.[containers,initContainers][][].image}}`
+
+- To experiment with JMESPath, use e.g. [jmespath.org] or [install the kyverno CLI][kyverno-cli]
+
+ (then use `kubectl kyverno jp query < data.json ...expression... `)
+
+[jmespath.org]: https://jmespath.org/
+[kyverno-cli]: https://kyverno.io/docs/kyverno-cli/install/
+[kyverno-variables]: https://kyverno.io/docs/policy-types/cluster-policy/variables/#pre-defined-variables
+
+---
+
+## Data sources
+
+- It's also possible to access data in Kubernetes ConfigMaps:
+ ```yaml
+ context:
+ - name: ingressconfig
+ configMap:
+ name: ingressconfig
+ namespace: {{request.object.metadata.namespace}}
+ ```
+
+- And then use it e.g. in a policy generating or modifying Ingress resources:
+ ```yaml
+ ...
+ host: {{request.object.metadata.name}}.{{ingressconfig.data.domainsuffix}}
+ ...
+ ```
+
+---
+
+## Kubernetes API calls
+
+- It's also possible to access arbitrary Kubernetes resources through API calls:
+ ```yaml
+ context:
+ - name: dns
+ apiCall:
+ urlPath: "/api/v1/namespaces/kube-system/services/kube-dns"
+ jmesPath: "spec.clusterIP"
+ ```
+
+- And then use that e.g. in a mutating policy:
+ ```yaml
+ mutate:
+ patchStrategicMerge:
+ spec:
+ containers:
+ - (name): "*"
+ env:
+ - name: DNS
+ value: "{{dns}}"
+ ```
+
+---
+
## Lifecycle
- After generated objects have been created, we can change them
- (Kyverno won't update them)
+ (Kyverno won't automatically revert them)
- Except if we use `clone` together with the `synchronize` flag
@@ -579,8 +455,6 @@ Note: the `apiVersion` field appears to be optional.
- This is convenient for e.g. ConfigMaps shared between Namespaces
-- Objects are generated only at *creation* (not when updating an old object)
-
---
class: extra-details
@@ -599,12 +473,14 @@ class: extra-details
(in the generated object `metadata`)
-- See [Linking resources with ownerReferences][ownerref] for an example
+- See [Linking resources with ownerReferences][kyverno-ownerref] for an example
-[ownerref]: https://kyverno.io/docs/writing-policies/generate/#linking-trigger-with-downstream
+[kyverno-ownerref]: https://kyverno.io/docs/policy-types/cluster-policy/generate/#linking-trigger-with-downstream
---
+class: extra-details
+
## Asynchronous creation
- Kyverno creates resources asynchronously
@@ -621,6 +497,30 @@ class: extra-details
---
+class: extra-details
+
+## Autogen rules for Pod validating policies
+
+- In Kubernetes, we rarely create Pods directly
+
+ (instead, we create controllers like Deployments, DaemonSets, Jobs, etc)
+
+- As a result, Pod validating policies can be tricky to debug
+
+ (the policy blocks invalid Pods, but doesn't block their controller)
+
+- Kyverno helps us with "autogen rules"
+
+ (when we create a Pod policy, it will automatically create policies on Pod controllers)
+
+- This can be customized if needed; [see documentation for details][kyverno-autogen]
+
+ (it can be disabled, or extended to Custom Resources)
+
+[kyverno-autogen]: https://kyverno.io/docs/policy-types/cluster-policy/autogen/
+
+---
+
## Footprint (current versions)
- 14 CRDs
@@ -663,45 +563,7 @@ class: extra-details
- There is also a CLI tool (not discussed here)
----
-
-## Caveats
-
-- The `{{ request }}` context is powerful, but difficult to validate
-
- (Kyverno can't know ahead of time how it will be populated)
-
-- Advanced policies (with conditionals) have unique, exotic syntax:
- ```yaml
- spec:
- =(volumes):
- =(hostPath):
- path: "!/var/run/docker.sock"
- ```
-
-- Writing and validating policies can be difficult
-
----
-
-class: extra-details
-
-## Pods created by controllers
-
-- When e.g. a ReplicaSet or DaemonSet creates a pod, it "owns" it
-
- (the ReplicaSet or DaemonSet is listed in the Pod's `.metadata.ownerReferences`)
-
-- Kyverno treats these Pods differently
-
-- If my understanding of the code is correct (big *if*):
-
- - it skips validation for "owned" Pods
-
- - instead, it validates their controllers
-
- - this way, Kyverno can report errors on the controller instead of the pod
-
-- This can be a bit confusing when testing policies on such pods!
+- It continues to evolve and gain new features
???
diff --git a/slides/kube-adv.yml b/slides/kube-adv.yml
index 8ac89ca2..5c272d1d 100644
--- a/slides/kube-adv.yml
+++ b/slides/kube-adv.yml
@@ -77,6 +77,8 @@ content:
- #7
- k8s/admission.md
- k8s/kyverno.md
+ - k8s/kyverno-colors.md
+ - k8s/kyverno-ingress.md
- #8
- k8s/aggregation-layer.md
- k8s/metrics-server.md
diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml
index 4bf33300..99780c1a 100644
--- a/slides/kube-selfpaced.yml
+++ b/slides/kube-selfpaced.yml
@@ -156,6 +156,8 @@ content:
- k8s/kuik.md
- k8s/sealed-secrets.md
- k8s/kyverno.md
+ - k8s/kyverno-colors.md
+ - k8s/kyverno-ingress.md
- k8s/eck.md
- k8s/finalizers.md
- k8s/owners-and-dependents.md