This is a stable feature in Kubernetes, and has been since version 1.36. It was first available in the v1.30 release.
本页概要介绍 MutatingAdmissionPolicy(变更性准入策略)。 MutatingAdmissionPolicies 允许你在有人向 Kubernetes API 写入变更时修改发生的操作。 如果你只想使用声明式策略来阻止对资源的某种更改(例如:保护平台命名空间不被删除), ValidatingAdmissionPolicy 是更简单且更有效的替代方案。
变更性准入策略(Mutating Admission Policy)提供了一种声明式的、进程内的方案, 可以用来替代变更性准入 Webhook。
变更性准入策略使用通用表达语言(Common Expression Language,CEL)来声明对资源的变更。 变更操作可以通过使用服务器端应用合并策略所合并的应用配置来定义, 也可以使用 JSON 补丁来定义。
变更性准入策略的可配置能力很强,策略的编写者可以根据集群管理员的需要, 定义参数化的策略以及限定到某类资源的策略。
策略通常由三种资源组成:
为 MutatingAdmissionPolicy 提供信息的一个参数资源(parameter resource),
有了参数之后,策略成为一条具体的语句(假想:“将 owner 标签设置为类似 company.example.com 的值”)。
参数资源引用 Kubernetes API 中可用的 Kubernetes 某种资源。被引用的资源可以是内置类别或类似
CustomResourceDefinition(CRD)这种扩展资源。
例如,你可以使用 ConfigMap 作为参数。
MutatingAdmissionPolicyBinding 将上述两种资源(MutatingAdmissionPolicy 和参数)关联在一起,
并提供作用域限定。如果你只想为 Pods 设置 owner 标签,而不想为其他 API 类别设置标签,
则绑定就是你用来限定此变更的地方。
你必须定义至少一个 MutatingAdmissionPolicy 和一个相应的 MutatingAdmissionPolicyBinding, 才能使策略生效。
以 .static.k8s.io
结尾的名称保留给基于清单的准入控制使用,
不能用于基于 API 的策略或绑定。当启用 ManifestBasedAdmissionControlConfig
特性门控时,
将强制执行此保留规则。
如果 MutatingAdmissionPolicy 不需要通过参数进行配置,在
MutatingAdmissionPolicy 中不指定 spec.paramKind 即可。
变更性准入策略是集群控制平面的一部分。你在编写和部署这些策略时要非常谨慎。 下文描述如何快速试用变更性准入策略。
以下是一个 MutatingAdmissionPolicy 的示例。 此策略会将变更新建的 Pod,为其添加一个边车容器(如果以前没有边车容器的话)。
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: "sidecar-policy.example.com"
spec:
paramKind:
kind: Sidecar
apiVersion: mutations.example.com/v1
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
matchConditions:
- name: does-not-already-have-sidecar
expression: "!object.spec.initContainers.exists(ic, ic.name == \"mesh-proxy\")"
failurePolicy: Fail
reinvocationPolicy: IfNeeded
mutations:
- patchType: "ApplyConfiguration"
applyConfiguration:
expression: >
Object{
spec: Object.spec{
initContainers: [
Object.spec.initContainers{
name: "mesh-proxy",
image: "mesh/proxy:v1.0.0",
args: ["proxy", "sidecar"],
restartPolicy: "Always"
}
]
}
}
.spec.mutations 字段由一系列表达式组成,这些表达式求值后将形成资源补丁。
所生成的补丁可以是应用配置或 JSON 补丁。
你不能将 mutations 设置为空列表。在对所有表达式求值后,API 服务器将所得到的变更应用到正在通过准入阶段的资源。
要配置变更准入策略以便用于某个集群中,需要先创建绑定。
只有存在 spec.policyName 字段值与某策略的 spec.name 相匹配的绑定时,该策略才会生效。
一旦创建了绑定和策略,策略的 spec.matchConditions 相匹配的所有资源请求都会触发已定义的所有变更集合。
在上面的示例中,创建 Pod 将触发添加 mesh-proxy initContainer 这一变更:
apiVersion: v1
kind: Pod
metadata:
name: myapp
namespace: default
spec:
...
initContainers:
- name: mesh-proxy
image: mesh/proxy:v1.0.0
args: ["proxy", "sidecar"]
restartPolicy: Always
- name: myapp-initializer
image: example/initializer:v1.0.0
...
使用参数资源,我们可以将策略配置与其定义分隔开。策略可以定义 paramKind,划定参数资源的 GVK,
随后的策略绑定操作会通过 paramRef 按名称(通过 policyName)将某个策略绑定到特定参数资源。
有关细节参阅参数资源。
ApplyConfigurationMutatingAdmissionPolicy 表达式始终是 CEL 格式的。
每个应用配置 expression 必须求值为(使用 Object() 初始化声明的)CEL 对象。
这些应用配置不能修改原子结构、映射或数组,因为这类修改可能导致意外删除未包含在应用配置中的值。
CEL 表达式可以访问以 CEL 变量组织起来的 API 请求内容及一些其他有用变量:
Object - 资源对象的 CEL 类型。Object.<fieldName> - 对象字段的 CEL 类型(例如 Object.spec)Object.<fieldName1>.<fieldName2>...<fieldNameN> - 嵌套字段的 CEL 类型(例如 Object.spec.containers)CEL 表达式可以访问以 CEL 变量组织起来的 API 请求内容及一些其他有用变量:
object - 来自传入请求的对象。对于 DELETE 请求,取值为 null。oldObject - 现有对象。对于 CREATE 请求,取值为 null。request - API 请求的属性。params - 正被评估的策略绑定所引用到的参数资源。仅在策略设置了 paramKind 时填充。namespaceObject - 传入对象所属的命名空间对象。对于集群范围的资源,取值为 null。variables - 组合变量的映射,包含从变量名称到其惰性评估值的映射。
例如,名为 foo 的变量可以以 variables.foo 的方式访问。authorizer - 一个 CEL 鉴权器。可用于对请求的主体(用户或服务账户)进行鉴权检查。
参阅 https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authzauthorizer.requestResource - 从 authorizer 构建并使用请求资源配置的 CEL ResourceCheck。apiVersion、kind、metadata.name、metadata.generateName 和
metadata.labels 始终可以从对象的根进行访问。不可访问其他元数据属性。
JSONPatch相同的变更可以被写成以下的 JSON 补丁:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: "sidecar-policy.example.com"
spec:
paramKind:
kind: Sidecar
apiVersion: mutations.example.com/v1
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
matchConditions:
- name: does-not-already-have-sidecar
expression: "!object.spec.initContainers.exists(ic, ic.name == \"mesh-proxy\")"
failurePolicy: Fail
reinvocationPolicy: IfNeeded
mutations:
- patchType: "JSONPatch"
jsonPatch:
expression: >
[
JSONPatch{
op: "add", path: "/spec/initContainers/-",
value: Object.spec.initContainers{
name: "mesh-proxy",
image: "mesh-proxy/v1.0.0",
restartPolicy: "Always"
}
}
]
表达式将通过 CEL 求值,以创建 JSON 补丁。 参阅 https://github.com/google/cel-spec
每个被求值的 expression 必须返回 JSONPatch 值形成的数组。
JSONPatch 类型表示 JSON 补丁中的一个操作。
例如,下面的 CEL 表达式返回 JSON 补丁,用于有条件地修改某个值:
[
JSONPatch{op: "test", path: "/spec/example", value: "Red"},
JSONPatch{op: "replace", path: "/spec/example", value: "Green"}
]
要为补丁操作 value 定义 JSON 对象,可以使用 CEL Object 类型。例如:
[
JSONPatch{
op: "add",
path: "/spec/selector",
value: Object.spec.selector{matchLabels: {"environment": "test"}}
}
]
要使用包含 '/' 和 '~' 的字符串作为 JSONPatch 路径键,可以使用 jsonpatch.escapeKey()。例如:
[
JSONPatch{
op: "add",
path: "/metadata/labels/" + jsonpatch.escapeKey("example.com/environment"),
value: "test"
},
]
CEL 表达式可以访问创建 JSON 补丁和对象所需的类型:
JSONPatch - JSON 补丁操作的 CEL 类型。JSONPatch 具有 op、from、path 和 value 字段。
有关细节参阅 JSON 补丁。value 字段可以设置为字符串、整数、数组、映射或对象。
如果设置,path 和 from 字段的值必须为
JSON 指针字符串,
可以在指针中使用 jsonpatch.escapeKey() CEL 函数来转义包含 / 和 ~ 的路径键。Object - 资源对象的 CEL 类型。Object.<fieldName> - 对象字段的 CEL 类型(例如 Object.spec)Object.<fieldName1>.<fieldName2>...<fieldNameN> - 嵌套字段的 CEL 类型(例如 Object.spec.containers)CEL 表达式可以访问以 CEL 变量组织的 API 请求的内容及一些其他有用变量:
object - 来自传入请求的对象。对于 DELETE 请求,取值为 null。oldObject - 现有对象。对于 CREATE 请求,取值为 null。request - API 请求的属性。params - 正被评估的策略绑定所引用的参数资源。仅在策略具有 paramKind 时填充。namespaceObject - 传入对象所属的命名空间对象。对于集群范围的资源,取值为 null。variables - 组合变量的映射,包含从变量名称到其惰性评估值的映射。
例如,名为 foo 的变量可以以 variables.foo 的形式访问。authorizer - 一个 CEL 鉴权组件。可用于对请求的主体(用户或服务账户)执行鉴权检查。
参阅 https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authzauthorizer.requestResource - 从 authorizer 构建并以请求资源配置的 CEL ResourceCheck。CEL 表达式可以访问 Kubernetes CEL 函数库以及:
jsonpatch.escapeKey - 执行 JSONPatch 键转义。~ 和 / 分别被转义为 ~0 和 ~1。只有格式为 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 的属性名称是可访问的。
某些 API 类别可以豁免准入时变更。例如,你无法创建更改 MutatingAdmissionPolicy 的 MutatingAdmissionPolicy。
以下准入配置类别豁免通过 REST API 创建的策略,以避免循环依赖:
当通过基于清单的准入控制配置时, MutatingAdmissionPolicy 可以拦截上述准入配置类别。这绕过了通常应用于通过 REST API 创建的策略的限制,允许你变更甚至准入配置和安全敏感资源。与 REST API 不同, 拦截这些资源的不当基于清单的准入策略不会导致不可恢复的后果,因为它是通过磁盘而非通过 API 定义的。
此外,以下非持久化(虚拟)的认证和授权 API 类别豁免所有准入策略,包括基于清单的策略, 因为拦截它们可能导致集群被锁定在自身的认证和授权路径之外:
从 Kubernetes v1.37 开始, 准入 Webhook 也默认排除这些虚拟资源。