From Fragile to Resilient: ValidatingAdmissionPolicies Strengthen Kubernetes

A presentation at Cloud Native Day Oslo in March 2025 in Oslo, Norway by Marcus Noble

Slide 1

Slide 1

From Fragile to Resilient: ValidatingAdmissionPolicies Strengthen Kubernetes Cloud Native Day Oslo March 13th 2025

Slide 2

Slide 2

Hi 👋, I’m Marcus Noble! I’m a platform engineer at working on release engineering, CI/CD and general Kubernetes development. I run a monthly newsletter - CloudNative.Now 7+ years experience running Kubernetes in production environments. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 3

Slide 3

Dynamic Admission Control ValidatingAdmission MutatingAdmission Webhook Webhook 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 4

Slide 4

Purpose / Use Cases Defaulting Policy Enforcement ● Injecting imagePullSecrets dynamically when pods are created ● Injecting sidecars into pods ● Injecting proxy environment variables into pods ● Prevent using latest image tag ● Require all pods to have resource limits set ● Block the use of deprecated Kubernetes APIs (e.g. batch/v1beta1) ● Require a PodDisruptionBudget ● Enforce a standard set of labels / annotations on all resources ● Replace all image registries with an in-house container image proxy / cache ● Block nodes joining the cluster with known CVEs based on the kernel version (e.g. CVE-2022-0185) ● Inject Log4Shell mitigation env var into all pods (CVE-2021-44228) ● Block binding to the cluster-admin role Best Practices Problem Mitigation 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 5

Slide 5

Webhooks in Kubernetes are ✨ POWERFUL ✨ But with that power comes Taken from my talk about this at K UK 😱 RISK 😱

Slide 6

Slide 6

Wouldn’t it be great if we had a safer alternative? Yes! Yes, it would! 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 7

Slide 7

Introducing ValidatingAdmissionPolicies Status: Alpha in v1.26, Beta in v1.28, GA in v1.30 Introduced in: KEP-3488 A declarative, in-process alternative to validating admission webhooks. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 8

Slide 8

Introducing ValidatingAdmissionPolicies Status: Alpha in v1.26, Beta in v1.28, GA in v1.30 Introduced in: KEP-3488 A declarative, in-process alternative to validating admission webhooks. Kubernetes manifests 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 9

Slide 9

Introducing ValidatingAdmissionPolicies Status: Alpha in v1.26, Beta in v1.28, GA in v1.30 Introduced in: KEP-3488 api-server A declarative, in-process alternative to validating admission webhooks. Kubernetes manifests 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 10

Slide 10

Introducing ValidatingAdmissionPolicies Status: Alpha in v1.26, Beta in v1.28, GA in v1.30 Introduced in: KEP-3488 api-server A declarative, in-process alternative to validating admission webhooks. Kubernetes manifests More on this shortly Uses the Common Expression Language (CEL) for the policy language. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 11

Slide 11

Introducing ValidatingAdmissionPolicies Status: Alpha in v1.26, Beta in v1.28, GA in v1.30 Introduced in: KEP-3488 api-server A declarative, in-process alternative to validating admission webhooks. Kubernetes manifests More on this shortly Uses the Common Expression Language (CEL) for the policy language. Consists of two main resources: ● ValidatingAdmissionPolicy describes the policy logic ● ValidatingAdmissionPolicyBinding links the above policy to the resources it applies to 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 12

Slide 12

Brief introduction to CEL ● Uses a similar syntax to the expressions in C-based languages, e.g. self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas ● Designed to be embedded into other applications with a focus on “one-liners” of code. ● Small number of built in functions (e.g. split, has) ● Functions expanded through custom libraries (Kubernetes includes several of these) References: ● ● Already used by Kyverno, Tekton, etc. ● Has a concept of “cost” per operation. https://kubernetes.io/docs/reference/using- This can be calculated ahead of running the expression api/cel/ and if needed, block its execution if too expensive. ● https://github.com/google/cel-go ● https://playcel.undistro.io/ (CEL playground) 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 13

Slide 13

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) More examples: github.com/AverageMarcus/comm on-admission-policies 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 14

Slide 14

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) The name of our policy. We’ll reference this in our ValidatingAdmission PolicyBinding 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 15

Slide 15

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) We want our policy to block any incoming requests that don’t meet this policies requirements (default). The alternative is Ignore if you want to disable enforcement of this policy. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 16

Slide 16

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) We define what resources this policy applies to. Multiple resource types can be defined here but if they don’t have the same general API the CEL expression will quickly become very complex. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 17

Slide 17

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) We’ll add a human-friendly message to be shown when the API request has been blocked by this policy. If we don’t include this, a generic message that include the whole expression is shown instead. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 18

Slide 18

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) Finally we have our expression. This is the expression of allowed resources, not those to block. (I keep getting caught out by this 😅) 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 19

Slide 19

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) object is the incoming resource from the API call. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 20

Slide 20

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) First we check all containers don’t have the latest tag (or no tag specified). 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 21

Slide 21

Example Blocking the use of the ‘latest’ image tag prevent-latest-vap.yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: “prevent-latest-image-tag” spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: [“apps”] apiVersions: [“v1”] operations: [“CREATE”, “UPDATE”] resources: [“deployments”, “daemonsets”, “statefulsets”] validations: - message: “The use of the ‘latest’ tag is not allowed” expression: | object.spec.template.spec.containers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) && ( !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(container, !container.image.endsWith(“:latest”) && container.image.contains(“:”) ) ) Then we check if this resource defines initContainers and if so we check they also don’t use latest. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 22

Slide 22

Example Blocking the use of the ‘latest’ image tag prevent-latest-binding.yaml Our policy does nothing until we bind it to some conditions. apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: name: “prevent-latest-image-tag” spec: policyName: “prevent-latest-image-tag” validationActions: [Deny] matchResources: {} 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 23

Slide 23

Example Blocking the use of the ‘latest’ image tag prevent-latest-binding.yaml The name of the policy we have just created. apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: name: “prevent-latest-image-tag” spec: policyName: “prevent-latest-image-tag” validationActions: [Deny] matchResources: {} 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 24

Slide 24

Example Blocking the use of the ‘latest’ image tag prevent-latest-binding.yaml The action to take when a policy isn’t met. apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: name: “prevent-latest-image-tag” spec: policyName: “prevent-latest-image-tag” validationActions: [Deny] Note: this is an array as you matchResources: {} can specify both warn and audit together Available options are: Deny, Warn & Audit 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 25

Slide 25

Example Blocking the use of the ‘latest’ image tag prevent-latest-binding.yaml We’re not defining any filtering so this policy applies cluster-wide. apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: name: “prevent-latest-image-tag” spec: policyName: “prevent-latest-image-tag” validationActions: [Deny] matchResources: {} We could limit our policy to specific namespaces or labels, for example. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 26

Slide 26

Example Blocking the use of the ‘latest’ image tag prevent-latest-binding.yaml We’re not defining any filtering so this policy applies cluster-wide. We could limit our policy to specific namespaces or labels, for example. apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: name: “prevent-latest-image-tag” spec: policyName: “prevent-latest-image-tag” validationActions: [Deny] matchResources: namespaceSelector: matchLabels: environment: prod 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 27

Slide 27

Example Blocking the use of the ‘latest’ image tag deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-blocked labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest

kubectl apply -f deployment.yaml The deployments “nginx-blocked” is invalid: : ValidatingAdmissionPolicy ‘prevent-latest-image-tag’ with binding ‘prevent-latest-image-tag’ denied request: The use of the ‘latest’ tag is not allowed

Our friendly message ⛔ Blocked! ⛔ 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 28

Slide 28

More advanced features 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 29

Slide 29

More advanced features ● More context values - Along with object you also have oldObject, namespaceObject & request you can use in your expressions matchConditions: - name: “exclude-kubelet-requests” expression: “!(“system:nodes” in request.userInfo.groups)” 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 30

Slide 30

More advanced features ● More context values - Along with object you also have oldObject, namespaceObject & request you can use in your expressions ● Parameters - make your policies configurable by allowing parameter resources to be applied by the binding policy.yaml policy-binding.yaml apiVersion: admissionregistration.k8s… kind: ValidatingAdmissionPolicy metadata: name: “param-example” spec: paramKind: apiVersion: rules.example.com/v1 kind: ReplicaLimit validations: - expression: object.spec.replicas <= params.maxReplicas apiVersion: admissionregistration.k8s… kind: ValidatingAdmissionPolicyBinding metadata: name: “param-example” spec: policyName: “param-example” paramRef: name: “my-parameters” namespace: “default” params.yaml apiVersion: rules.example.com/v1 kind: ReplicaLimit metadata: name: “my-parameters” namespace: “default” maxReplicas: 3 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 31

Slide 31

More advanced features ● More context values - Along with object you also have oldObject, namespaceObject & request you can use in your expressions ● Parameters - make your policies configurable by allowing parameter resources to be applied by the binding ● Variables - reusable CEL expressions to simplify your validation expressions. spec: Must be a valid CEL identifier (e.g. no -) variables: - name: teamLabel expression: “‘team’ in object.spec.metadata.labels ? object.spec.metadata.labels[‘team’] : ‘no-team’” validations: - expression: variables.teamLabel == ‘rel-eng’ 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 32

Slide 32

More advanced features ● More context values - Along with object you also have oldObject, namespaceObject & request you can use in your expressions ● Parameters - make your policies configurable by allowing parameter resources to be applied by the binding ● Variables - reusable CEL expressions to simplify your validation expressions. ● Audit annotations - Add extra metadata to the audit logs, dynamic values using CEL auditAnnotations: - key: “replica-count” valueExpression: | “‘Deployment spec.replicas set to ’ + string(object.spec.replicas)” { “kind”: “Event”, “apiVersion”: “audit.k8s.io/v1”, “annotations”: { “demo-policy.example.com/replica-count”: “Deployment spec.replicas set to 128” … } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 33

Slide 33

More advanced features ● More context values - Along with object you also have oldObject, namespaceObject & request you can use in your expressions ● Parameters - make your policies configurable by allowing parameter resources to be applied by the binding ● Variables - reusable CEL expressions to simplify your validation expressions. ● Audit annotations - Add extra metadata to the audit logs, dynamic values using CEL ● Message expressions - Leverage some CEL in your message to have dynamic validation messages messageExpression: “‘object.spec.replicas must be no greater than ’ + string(params.maxReplicas)” 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 34

Slide 34

So that’s validation covered, what about mutating? Well… 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 35

Slide 35

MutatingAdmissionPolicies Status: Alpha in v1.32 Introduced in: KEP-3962 ● Follows the same idea as ValidatingAdmissionPolicies. ● Introduces MutatingAdmissionPolicy and MutatingAdmissionPolicyBinding ● Supports two patch strategies: ApplyConfiguration and JSONPatch ● Not yet enabled by default, you must enable the following: ○ MutatingAdmissionPolicy feature gate ○ admissionregistration.k8s.io/v1alpha1 API kind-config.yaml kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 name: kind-cluster featureGates: “MutatingAdmissionPolicy”: true runtimeConfig: “admissionregistration.k8s.io/v1alpha1”: true Create a Kind cluster with: kind create cluster —config kind-config.yaml 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 36

Slide 36

Example proxy-vars-map.yaml Injecting proxy values as env vars apiVersion: admissionregistration.k8s.io/v1alpha1 kind: MutatingAdmissionPolicy metadata: name: “proxy-values” spec: failurePolicy: Fail reinvocationPolicy: IfNeeded matchConstraints: resourceRules: - apiGroups: [“”] apiVersions: [“v1”] operations: [“CREATE”] resources: [“pods”] mutations: - patchType: “ApplyConfiguration” applyConfiguration: > expression: > Object{ spec: Object.spec{ containers: object.spec.containers.map(c, Object.spec.containers.item{ name: c.name, env: [ Object.spec.containers.env{ name: “HTTP_PROXY”, value: “http://proxy.proxy.svc:3128” } ] } ) } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 37

Slide 37

Example proxy-vars-map.yaml Injecting proxy values as env vars Same as ValidatingAdmissionPolicies apiVersion: admissionregistration.k8s.io/v1alpha1 kind: MutatingAdmissionPolicy metadata: name: “proxy-values” spec: failurePolicy: Fail reinvocationPolicy: IfNeeded matchConstraints: resourceRules: - apiGroups: [“”] apiVersions: [“v1”] operations: [“CREATE”] resources: [“pods”] mutations: - patchType: “ApplyConfiguration” applyConfiguration: > expression: > Object{ spec: Object.spec{ containers: object.spec.containers.map(c, Object.spec.containers.item{ name: c.name, env: [ Object.spec.containers.env{ name: “HTTP_PROXY”, value: “http://proxy.proxy.svc:3128” } ] } ) } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 38

Slide 38

Example proxy-vars-map.yaml Injecting proxy values as env vars Should the policy be re-applied if other mutations are made to the resource? Allowed values are “Never” and “IfNeeded” apiVersion: admissionregistration.k8s.io/v1alpha1 kind: MutatingAdmissionPolicy metadata: name: “proxy-values” spec: failurePolicy: Fail reinvocationPolicy: IfNeeded matchConstraints: resourceRules: - apiGroups: [“”] apiVersions: [“v1”] operations: [“CREATE”] resources: [“pods”] mutations: - patchType: “ApplyConfiguration” applyConfiguration: > expression: > Object{ spec: Object.spec{ containers: object.spec.containers.map(c, Object.spec.containers.item{ name: c.name, env: [ Object.spec.containers.env{ name: “HTTP_PROXY”, value: “http://proxy.proxy.svc:3128” } ] } ) } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 39

Slide 39

Example proxy-vars-map.yaml Injecting proxy values as env vars Replace validations with mutations apiVersion: admissionregistration.k8s.io/v1alpha1 kind: MutatingAdmissionPolicy metadata: name: “proxy-values” spec: failurePolicy: Fail reinvocationPolicy: IfNeeded matchConstraints: resourceRules: - apiGroups: [“”] apiVersions: [“v1”] operations: [“CREATE”] resources: [“pods”] mutations: - patchType: “ApplyConfiguration” applyConfiguration: > expression: > Object{ spec: Object.spec{ containers: object.spec.containers.map(c, Object.spec.containers.item{ name: c.name, env: [ Object.spec.containers.env{ name: “HTTP_PROXY”, value: “http://proxy.proxy.svc:3128” } ] } ) } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 40

Slide 40

Example proxy-vars-map.yaml Injecting proxy values as env vars Indicates the patch strategy Possible values: ● ApplyConfiguration ● JSONPatch apiVersion: admissionregistration.k8s.io/v1alpha1 kind: MutatingAdmissionPolicy metadata: name: “proxy-values” spec: failurePolicy: Fail reinvocationPolicy: IfNeeded matchConstraints: resourceRules: - apiGroups: [“”] apiVersions: [“v1”] operations: [“CREATE”] resources: [“pods”] mutations: - patchType: “ApplyConfiguration” applyConfiguration: > expression: > Object{ spec: Object.spec{ containers: object.spec.containers.map(c, Object.spec.containers.item{ name: c.name, env: [ Object.spec.containers.env{ name: “HTTP_PROXY”, value: “http://proxy.proxy.svc:3128” } ] } ) } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 41

Slide 41

Example proxy-vars-map.yaml Injecting proxy values as env vars Either applyConfiguration or jsonPatch depending on the patchType. apiVersion: admissionregistration.k8s.io/v1alpha1 kind: MutatingAdmissionPolicy metadata: name: “proxy-values” spec: failurePolicy: Fail reinvocationPolicy: IfNeeded matchConstraints: resourceRules: - apiGroups: [“”] apiVersions: [“v1”] operations: [“CREATE”] resources: [“pods”] mutations: - patchType: “ApplyConfiguration” applyConfiguration: > expression: > Object{ spec: Object.spec{ containers: object.spec.containers.map(c, Object.spec.containers.item{ name: c.name, env: [ Object.spec.containers.env{ name: “HTTP_PROXY”, value: “http://proxy.proxy.svc:3128” } ] } ) } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 42

Slide 42

Example proxy-vars-map.yaml Injecting proxy values as env vars Introduction of named types The Object refers to the type of the incoming resource (Pod in this example). Child fields can be used by using Object.[fieldname] and can go multiple levels down as see here with the Object.spec.containers.env apiVersion: admissionregistration.k8s.io/v1alpha1 kind: MutatingAdmissionPolicy metadata: name: “proxy-values” spec: failurePolicy: Fail reinvocationPolicy: IfNeeded matchConstraints: resourceRules: - apiGroups: [“”] apiVersions: [“v1”] operations: [“CREATE”] resources: [“pods”] mutations: - patchType: “ApplyConfiguration” applyConfiguration: > expression: > Object{ spec: Object.spec{ containers: object.spec.containers.map(c, Object.spec.containers.item{ name: c.name, env: [ Object.spec.containers.env{ name: “HTTP_PROXY”, value: “http://proxy.proxy.svc:3128” } ] } ) } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 43

Slide 43

Example proxy-vars-map.yaml Injecting proxy values as env vars Merge our proxy vars with any existing vars in all containers. As this is an ApplyConfiguration patch type the existing env vars on the object remain untouched. Note: We’re missing initContainers and ephermeralContainers here due to limited space. apiVersion: admissionregistration.k8s.io/v1alpha1 kind: MutatingAdmissionPolicy metadata: name: “proxy-values” spec: failurePolicy: Fail reinvocationPolicy: IfNeeded matchConstraints: resourceRules: - apiGroups: [“”] apiVersions: [“v1”] operations: [“CREATE”] resources: [“pods”] mutations: - patchType: “ApplyConfiguration” applyConfiguration: > expression: > Object{ spec: Object.spec{ containers: object.spec.containers.map(c, Object.spec.containers.item{ name: c.name, env: [ Object.spec.containers.env{ name: “HTTP_PROXY”, value: “http://proxy.proxy.svc:3128” } ] } ) } } 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 44

Slide 44

Example Injecting proxy values as env vars proxy-vars-binding.yaml apiVersion: admissionregistration.k8s.io/v1beta1 kind: MutatingAdmissionPolicyBinding metadata: name: “proxy-values” spec: policyName: “proxy-values” matchResources: {} paramRef: {} Nothing too surprising about our binding resource. All properties are the same as Validating except for the lack of a validationActions property. 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 45

Slide 45

Advanced features ● More context values - Along with object you also have oldObject, namespaceObject & request you can use in your expressions ● Parameters - make your policies configurable by allowing parameter resources to be applied by the binding ● Variables - reusable CEL expressions to simplify your validation expressions. ● Audit annotations - Add extra metadata to the audit logs, dynamic values using CEL ● Message expressions - Leverage some CEL in your message to have dynamic validation messages 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 46

Slide 46

What about the future beyond that? 🔮 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 47

Slide 47

  • All these are speculative / wishful thinking right now ● Generative policies - create new resources based on API requests ● Resource lookup - get the current state of other resources within the cluster ● Policy exceptions - disable policies under certain circumstances ● Policy reports - user friendly view of validation failures, etc. ● More abstractions - e.g. Kyverno already working on this with VAP 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 48

Slide 48

Summary ● Time to start replacing those risky webhooks with in-process policies. ● ValidatingAdmissionPolicies generally available for use from v1.30 for safe validation logic. ● v1.32 for alpha release of MutatingAdmissionPolicies. (Must be enabled) ● More abstractions, generative policies and API lookups hopefully coming in the future. ● Examples of common use-case policies: github.com/AverageMarcus/common-admission-policies Contributions welcome 🐘 @Marcus@k8s.social | 🌐 MarcusNoble.com | 🦋 @averagemarcus.bsky.social

Slide 49

Slide 49

Wrap-up Slides and resources available at: https://go-get.link/cndoslo Thoughts, comments and feedback: feedback@marcusnoble.co.uk https://k8s.social/@Marcus https://bsky.app/profile/averagemarcus.bsky.social Thank you