修改性准入策略

特性状态: Kubernetes v1.34 [beta]

此页面概述了 **MutatingAdmissionPolicy**。变更准入策略允许你更改当用户向 Kubernetes API 写入更改时所发生的事情。如果你只想使用声明性策略来阻止特定类型的资源更改(例如:保护平台命名空间免遭删除),则 ValidatingAdmissionPolicy 是一种更简单、更有效的替代方案。

要使用此功能,请启用 `MutatingAdmissionPolicy` 特性门控(默认关闭),并在 kube-apiserver 上设置 `--runtime-config=admissionregistration.k8s.io/v1beta1=true`。

什么是变更准入策略?

变更准入策略提供了一种声明式的、进程内的、替代变更准入 Webhook 的方案。

变更准入策略使用通用表达式语言 (CEL) 来声明资源的变更。变更可以通过使用 Server Side Apply 合并策略 合并的 **应用配置** 来定义,或者通过 JSON Patch 来定义。

变更准入策略具有高度可配置性,使策略作者能够根据集群管理员的需要定义可参数化并作用于特定资源的策略。

策略由哪些资源组成

策略通常由三个资源组成:

  • MutatingAdmissionPolicy 描述了策略的抽象逻辑(例如:“此策略将特定标签设置为特定值”)。

  • **参数资源** 为 MutatingAdmissionPolicy 提供信息,使其成为具体声明(例如,“将 `owner` 标签设置为 `company.example.com` 之类的值”)。参数资源引用 Kubernetes API 中可用的 Kubernetes 资源。它们可以是内置类型或扩展,例如 CustomResourceDefinition (CRD)。例如,你可以使用 ConfigMap 作为参数。

  • MutatingAdmissionPolicyBinding 将上述资源(MutatingAdmissionPolicy 和参数)关联在一起并提供作用域。如果你只想为 `Pods` 设置 `owner` 标签,而不是其他 API 类型,则可以在绑定中指定此变更。

要使策略生效,至少必须定义一个 MutatingAdmissionPolicy 和一个相应的 MutatingAdmissionPolicyBinding。

如果 MutatingAdmissionPolicy 无需通过参数配置,只需将 MutatingAdmissionPolicy 中的 `spec.paramKind` 留空即可。

变更准入策略入门

变更准入策略是集群控制平面的一部分。你应该非常谨慎地编写和部署它们。下面描述了如何快速尝试变更准入策略。

创建 MutatingAdmissionPolicy

下面是一个 MutatingAdmissionPolicy 的示例。此策略会变更新创建的 Pod,如果不存在,则添加一个 sidecar 容器。

apiVersion: admissionregistration.k8s.io/v1beta1
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 Patch 补丁。你不能指定空的变更列表。在评估所有表达式之后,API 服务器将这些更改应用于正在通过准入的资源。

为了在集群中使用变更准入策略,需要进行绑定。MutatingAdmissionPolicy 仅在存在相应的绑定,且其引用的 `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,然后策略绑定通过名称(通过 `policyName`)将策略与特定的参数资源(通过 `paramRef`)关联起来。

有关更多信息,请参阅参数资源

ApplyConfiguration

MutatingAdmissionPolicy 表达式始终是 CEL。每个应用配置 `expression` 必须求值为一个 CEL 对象(使用 `Object()` 初始化声明)。

应用配置不能修改原子结构体、映射或数组,因为这有意外删除未包含在应用配置中的值的风险。

CEL 表达式可以访问创建应用配置所需的以下对象类型:

  • `Object` - 资源对象的 CEL 类型。
  • `Object.` - 对象字段的 CEL 类型(例如 `Object.spec`)。
  • `Object.....` - 嵌套字段的 CEL 类型(例如 `Object.spec.containers`)。

CEL 表达式可以访问 API 请求的内容,这些内容被组织成 CEL 变量以及其他一些有用的变量:

  • `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#Authz
  • `authorizer.requestResource` - 从 `authorizer` 构造并使用请求资源配置的 CEL ResourceCheck。

`apiVersion`、`kind`、`metadata.name`、`metadata.generateName` 和 `metadata.labels` 始终可以从对象的根访问。
其他元数据属性均不可访问。

JSONPatch

相同的变更可以表示为如下的 JSON Patch

apiVersion: admissionregistration.k8s.io/v1beta1
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.` - 对象字段的 CEL 类型(例如 `Object.spec`)。
  • `Object.....` - 嵌套字段的 CEL 类型(例如 `Object.spec.containers`)。

CEL 表达式可以访问 API 请求的内容,这些内容被组织成 CEL 变量以及其他一些有用的变量:

  • `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#Authz
  • `authorizer.requestResource` - 从 `authorizer` 构造并使用请求资源配置的 CEL ResourceCheck。

CEL 表达式可以访问 Kubernetes CEL 函数库 以及

  • `jsonpatch.escapeKey` - 执行 JSONPatch 键转义。`~` 和 `/` 分别转义为 `~0` 和 `~1`。

只有格式为 `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` 的属性名称才可访问。

免于变更准入的 API 种类

某些 API 种类免于准入时变更。例如,你不能创建变更 MutatingAdmissionPolicy 的 MutatingAdmissionPolicy。

免于变更准入的 API 种类列表如下:

上次修改时间为太平洋标准时间 2025 年 8 月 6 日下午 6:42:KEP-3962 文档 (e65e744407)