Skip to content

Commit cb8e5a7

Browse files
JefftreejpbetzTim Bannister
authored
KEP-3962: Mutating admission policy documentation (#48646)
* Introduce concept page for mutating admission policy * add example and documentation for MAP * fix MAP feature gate documentation * address comments * Apply suggestions from code review Co-authored-by: Tim Bannister <[email protected]> --------- Co-authored-by: Joe Betz <[email protected]> Co-authored-by: Tim Bannister <[email protected]>
1 parent 58c8107 commit cb8e5a7

File tree

4 files changed

+287
-3
lines changed

4 files changed

+287
-3
lines changed
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
---
2+
reviewers:
3+
- deads2k
4+
- sttts
5+
- cici37
6+
title: Mutating Admission Policy
7+
content_type: concept
8+
---
9+
10+
<!-- overview -->
11+
12+
{{< feature-state for_k8s_version="v1.32" state="alpha" >}}
13+
<!-- due to feature gate history, use manual version specification here -->
14+
15+
This page provides an overview of _MutatingAdmissionPolicies_.
16+
17+
<!-- body -->
18+
19+
## What are MutatingAdmissionPolicies?
20+
21+
Mutating admission policies offer a declarative, in-process alternative to mutating admission webhooks.
22+
23+
Mutating admission policies use the Common Expression Language (CEL) to declare mutations to resources.
24+
Mutations can be defined either with an *apply configuration* that is merged using the
25+
[server side apply merge strategy](/docs/reference/using-api/server-side-apply/#merge-strategy),
26+
or a [JSON patch](https://jsonpatch.com/).
27+
28+
Mutating admission policies are highly configurable, enabling policy authors to define policies
29+
that can be parameterized and scoped to resources as needed by cluster administrators.
30+
31+
## What resources make a policy
32+
33+
A policy is generally made up of three resources:
34+
35+
- The MutatingAdmissionPolicy describes the abstract logic of a policy
36+
(think: "this policy sets a particular label to a particular value").
37+
38+
- A _parameter resource_ provides information to a MutatingAdmissionPolicy to make it a concrete
39+
statement (think "set the `owner` label to something like `company.example.com`").
40+
Parameter resources refer to Kubernetes resources, available in the Kubernetes API. They can be built-in types or extensions,
41+
such as a {{< glossary_tooltip term_id="CustomResourceDefinition" text="CustomResourceDefinition" >}} (CRD). For example, you can use a ConfigMap as a parameter.
42+
- A MutatingAdmissionPolicyBinding links the above (MutatingAdmissionPolicy and parameter) resources together and provides scoping.
43+
If you only want to set an `owner` label for `Pods`, and not other API kinds, the binding is where you
44+
specify this mutation.
45+
46+
47+
48+
At least a MutatingAdmissionPolicy and a corresponding MutatingAdmissionPolicyBinding
49+
must be defined for a policy to have an effect.
50+
51+
If a MutatingAdmissionPolicy does not need to be configured via parameters, simply leave
52+
`spec.paramKind` in MutatingAdmissionPolicy not specified.
53+
54+
## Getting Started with MutatingAdmissionPolicies
55+
56+
Mutating admission policy is part of the cluster control-plane. You should write
57+
and deploy them with great caution. The following describes how to quickly
58+
experiment with Mutating admission policy.
59+
60+
### Create a MutatingAdmissionPolicy
61+
62+
The following is an example of a MutatingAdmissionPolicy. This policy mutates newly created Pods to have a sidecar container if it does not exist.
63+
64+
{{% code_sample language="yaml" file="mutatingadmissionpolicy/applyconfiguration-example.yaml" %}}
65+
66+
The `.spec.mutations` field consists of a list of expressions that evaluate to resource patches.
67+
The emitted patches may be either [apply configurations](#patch-type-apply-configuration) or [JSON Patch](#patch-type-json-patch)
68+
patches. You cannot specify an empty list of mutations. After evaluating all the
69+
expressions, the API server applies those changes to the resource that is
70+
passing through admission.
71+
72+
To configure a mutating admission policy for use in a cluster, a binding is
73+
required. The MutatingAdmissionPolicy will only be active if a corresponding
74+
binding exists with the referenced `spec.policyName` matching the `spec.name` of
75+
a policy.
76+
77+
Once the binding and policy are created, any resource request that matches the
78+
`spec.matchConditions` of a policy will trigger the set of mutations defined.
79+
80+
In the example above, creating a Pod will add the `mesh-proxy` initContainer mutation:
81+
82+
```yaml
83+
apiVersion: v1
84+
kind: Pod
85+
metadata:
86+
name: myapp
87+
namespace: default
88+
spec:
89+
...
90+
initContainers:
91+
- name: mesh-proxy
92+
image: mesh/proxy:v1.0.0
93+
args: ["proxy", "sidecar"]
94+
restartPolicy: Always
95+
- name: myapp-initializer
96+
image: example/initializer:v1.0.0
97+
...
98+
```
99+
100+
#### Parameter resources
101+
102+
Parameter resources allow a policy configuration to be separate from its
103+
definition. A policy can define `paramKind`, which outlines GVK of the parameter
104+
resource, and then a policy binding ties a policy by name (via `policyName`) to a
105+
particular parameter resource via `paramRef`.
106+
107+
Please refer to [parameter resources](/docs/reference/access-authn-authz/validating-admission-policy/#parameter-resources) for more information.
108+
109+
#### `ApplyConfiguration` {#patch-type-apply-configuration}
110+
111+
MutatingAdmissionPolicy expressions are always CEL. Each apply configuration
112+
`expression` must evaluate to a CEL object (declared using `Object()`
113+
initialization).
114+
115+
Apply configurations may not modify atomic structs, maps or arrays due to the risk of accidental deletion of
116+
values not included in the apply configuration.
117+
118+
CEL expressions have access to the object types needed to create apply configurations:
119+
120+
- `Object` - CEL type of the resource object.
121+
- `Object.<fieldName>` - CEL type of object field (such as `Object.spec`)
122+
- `Object.<fieldName1>.<fieldName2>...<fieldNameN>` - CEL type of nested field (such as `Object.spec.containers`)
123+
124+
CEL expressions have access to the contents of the API request, organized into CEL variables as well as some other useful variables:
125+
126+
- `object` - The object from the incoming request. The value is null for DELETE requests.
127+
- `oldObject` - The existing object. The value is null for CREATE requests.
128+
- `request` - Attributes of the API request.
129+
- `params` - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
130+
- `namespaceObject` - The namespace object that the incoming object belongs to. The value is null for cluster-scoped resources.
131+
- `variables` - Map of composited variables, from its name to its lazily evaluated value.
132+
For example, a variable named `foo` can be accessed as `variables.foo`.
133+
- `authorizer` - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
134+
See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
135+
- `authorizer.requestResource` - A CEL ResourceCheck constructed from the `authorizer` and configured with the
136+
request resource.
137+
138+
The `apiVersion`, `kind`, `metadata.name`, `metadata.generateName` and `metadata.labels` are always accessible from the root of the
139+
object. No other metadata properties are accessible.
140+
141+
#### `JSONPatch` {#patch-type-json-patch}
142+
143+
The same mutation can be written as a [JSON Patch](https://jsonpatch.com/) as follows:
144+
145+
{{% code_sample language="yaml" file="mutatingadmissionpolicy/json-patch-example.yaml" %}}
146+
147+
The expression will be evaluated by CEL to create a [JSON patch](https://jsonpatch.com/).
148+
ref: https://github.com/google/cel-spec
149+
150+
Each evaluated `expression` must return an array of `JSONPatch` values. The
151+
`JSONPatch` type represents one operation from a JSON patch.
152+
153+
For example, this CEL expression returns a JSON patch to conditionally modify a value:
154+
155+
```
156+
[
157+
JSONPatch{op: "test", path: "/spec/example", value: "Red"},
158+
JSONPatch{op: "replace", path: "/spec/example", value: "Green"}
159+
]
160+
```
161+
162+
To define a JSON object for the patch operation `value`, use CEL `Object` types. For example:
163+
164+
```
165+
[
166+
JSONPatch{
167+
op: "add",
168+
path: "/spec/selector",
169+
value: Object.spec.selector{matchLabels: {"environment": "test"}}
170+
}
171+
]
172+
```
173+
174+
To use strings containing '/' and '~' as JSONPatch path keys, use `jsonpatch.escapeKey()`. For example:
175+
176+
```
177+
[
178+
JSONPatch{
179+
op: "add",
180+
path: "/metadata/labels/" + jsonpatch.escapeKey("example.com/environment"),
181+
value: "test"
182+
},
183+
]
184+
```
185+
186+
CEL expressions have access to the types needed to create JSON patches and objects:
187+
188+
- `JSONPatch` - CEL type of JSON Patch operations. JSONPatch has the fields `op`, `from`, `path` and `value`.
189+
See [JSON patch](https://jsonpatch.com/) for more details. The `value` field may be set to any of: string,
190+
integer, array, map or object. If set, the `path` and `from` fields must be set to a
191+
[JSON pointer](https://datatracker.ietf.org/doc/html/rfc6901/) string, where the `jsonpatch.escapeKey()` CEL
192+
function may be used to escape path keys containing `/` and `~`.
193+
- `Object` - CEL type of the resource object.
194+
- `Object.<fieldName>` - CEL type of object field (such as `Object.spec`)
195+
- `Object.<fieldName1>.<fieldName2>...<fieldNameN>` - CEL type of nested field (such as `Object.spec.containers`)
196+
197+
CEL expressions have access to the contents of the API request, organized into CEL variables as well as some other useful variables:
198+
199+
- `object` - The object from the incoming request. The value is null for DELETE requests.
200+
- `oldObject` - The existing object. The value is null for CREATE requests.
201+
- `request` - Attributes of the API request.
202+
- `params` - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
203+
- `namespaceObject` - The namespace object that the incoming object belongs to. The value is null for cluster-scoped resources.
204+
- `variables` - Map of composited variables, from its name to its lazily evaluated value.
205+
For example, a variable named `foo` can be accessed as `variables.foo`.
206+
- `authorizer` - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
207+
See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
208+
- `authorizer.requestResource` - A CEL ResourceCheck constructed from the `authorizer` and configured with the
209+
request resource.
210+
211+
CEL expressions have access to [Kubernetes CEL function libraries](/docs/reference/using-api/cel/#cel-options-language-features-and-libraries)
212+
as well as:
213+
214+
- `jsonpatch.escapeKey` - Performs JSONPatch key escaping. `~` and `/` are escaped as `~0` and `~1` respectively.
215+
216+
Only property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.

content/en/docs/reference/command-line-tools-reference/feature-gates/mutating-admission-policy.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ stages:
1010
defaultValue: false
1111
fromVersion: "1.30"
1212
---
13-
In Kubernetes {{< skew currentVersion >}}, this feature gate has no effect.
14-
A future release of Kubernetes may use this feature gate to enable
15-
the MutatingAdmissionPolicy in admission chain.
1613

14+
Enable [MutatingAdmissionPolicy](/docs/reference/access-authn-authz/mutating-admission-policy/) support for [CEL](https://kubernetes.io/docs/reference/using-api/cel/) mutations be used in admission control.
15+
16+
For Kubernetes v1.30 and v1.31, this feature gate existed but had no effect.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
apiVersion: admissionregistration.k8s.io/v1alpha1
2+
kind: MutatingAdmissionPolicy
3+
metadata:
4+
name: "sidecar-policy.example.com"
5+
spec:
6+
paramKind:
7+
kind: Sidecar
8+
apiVersion: mutations.example.com/v1
9+
matchConstraints:
10+
resourceRules:
11+
- apiGroups: ["apps"]
12+
apiVersions: ["v1"]
13+
operations: ["CREATE"]
14+
resources: ["pods"]
15+
matchConditions:
16+
- name: does-not-already-have-sidecar
17+
expression: "!object.spec.initContainers.exists(ic, ic.name == \"mesh-proxy\")"
18+
failurePolicy: Fail
19+
reinvocationPolicy: IfNeeded
20+
mutations:
21+
- patchType: "ApplyConfiguration"
22+
applyConfiguration:
23+
expression: >
24+
Object{
25+
spec: Object.spec{
26+
initContainers: [
27+
Object.spec.initContainers{
28+
name: "mesh-proxy",
29+
image: "mesh/proxy:v1.0.0",
30+
args: ["proxy", "sidecar"],
31+
restartPolicy: "Always"
32+
}
33+
]
34+
}
35+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apiVersion: admissionregistration.k8s.io/v1alpha1
2+
kind: MutatingAdmissionPolicy
3+
metadata:
4+
name: "sidecar-policy.example.com"
5+
spec:
6+
paramKind:
7+
kind: Sidecar
8+
apiVersion: mutations.example.com/v1
9+
matchConstraints:
10+
resourceRules:
11+
- apiGroups: [""]
12+
apiVersions: ["v1"]
13+
operations: ["CREATE"]
14+
resources: ["pods"]
15+
matchConditions:
16+
- name: does-not-already-have-sidecar
17+
expression: "!object.spec.initContainers.exists(ic, ic.name == \"mesh-proxy\")"
18+
failurePolicy: Fail
19+
reinvocationPolicy: IfNeeded
20+
mutations:
21+
- patchType: "JSONPatch"
22+
jsonPatch:
23+
expression: >
24+
[
25+
JSONPatch{
26+
op: "add", path: "/spec/initContainers/-",
27+
value: Object.spec.initContainers{
28+
name: "mesh-proxy",
29+
image: "mesh-proxy/v1.0.0",
30+
restartPolicy: "Always"
31+
}
32+
}
33+
]

0 commit comments

Comments
 (0)