探索验证与变更准入策略

探索验证与变更准入策略

使用声明式准入策略,在准入时通过通用表达式语言(CEL)验证或变更资源。

本页面让你试用声明式准入策略,这类策略允许你使用通用表达式语言(CEL)验证或变更资源。

Kubernetes 1.37 支持两种准入策略:

本教程介绍这两种准入策略。

准备开始

你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

要定义准入策略,你必须是集群管理员。请确保你对用于学习的集群具有管理员访问权限。

对于 ValidatingAdmissionPolicy,你需要:

  • 运行 1.30 或更高版本的集群。

对于 MutatingAdmissionPolicy,你需要:

  • 运行 1.36 或更高版本的集群。

要检查版本,请运行 kubectl version。如果你运行的是较旧版本的 Kubernetes,请查阅对应版本的文档。

什么是声明式准入策略?

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

通过使用通用表达式语言(CEL)来声明策略规则,这些策略直接在 API 服务器内部进行评估。

这些策略高度可配置,使策略作者能够定义可参数化的逻辑,并根据集群管理员的需要限定到特定资源。

准入策略的 API 类型

这两种策略的用途不同。

ValidatingAdmissionPolicy 用于强制执行约束。

MutatingAdmissionPolicy 用于在准入时修改资源。

策略要素

每个被应用的策略都有一个策略对象(ValidatingAdmissionPolicy 或 MutatingAdmissionPolicy) 和一个独立的绑定对象(ValidatingAdmissionPolicyBinding 或 MutatingAdmissionPolicyBinding)。

你还可以使用参数,参数是可选的。要了解更多信息, 请参阅参数资源(ValidatingAdmissionPolicy 或参数资源(MutatingAdmissionPolicy)。

策略对象使用通用表达式语言(CEL)描述策略的抽象逻辑。 例如,ValidatingAdmissionPolicy 可以强制副本数量限制或确保存在特定的标签, 而 MutatingAdmissionPolicy 可以修改资源,例如为命名空间添加默认标签。

绑定对象将策略与你的集群关联并提供作用域。 ValidatingAdmissionPolicyBinding 或 MutatingAdmissionPolicyBinding 将策略连接到特定资源。 如果你只想对特定子集的资源强制执行策略, 那么绑定就是你缩小策略作用域的地方(使用 matchResources)。

参数允许将策略行为的配置与其定义分离。参数资源是指 API 中可用的 Kubernetes 资源。 它们可以是内置的 API 类型(例如 ConfigMap), 也可以是定制资源。 然后,策略绑定使用 spec.paramRef 引用实际的参数资源。

如果策略不需要参数,则不设置 spec.paramKind。

CEL 表达式

这两种策略都依赖一种称为通用表达式语言(CEL)的表达式语言。 阅读 Kubernetes 中的通用表达式语言以了解更多。

如果你刚接触 CEL,可以先练习编写一个非常简单的表达式,例如 false || true。 你可以在 CEL Playground 中测试 CEL 表达式。

策略操作

每个准入策略绑定必须指定一个或多个操作,以声明策略如何被执行。

ValidatingAdmissionPolicyBinding

对于 ValidatingAdmissionPolicyBinding,支持的 validationActions 包括:

Audit
验证失败会记录在 API 请求的审计事件中。
Warn
验证失败会作为警告报告给请求客户端。
Deny
验证失败会导致请求被拒绝。

策略检查失败或发生错误时,会按这些操作执行。 只有当 failurePolicy 设为 Fail(或未指定)时, 由 failurePolicy 定义的失败才会按这些操作执行。

有关策略审计日志的更多细节, 请参见审计注解:验证失败。

不能同时使用 Deny 和 Warn,因为这种组合会在 API 响应体和 HTTP Warning: 头中重复验证失败信息。

MutatingAdmissionPolicyBinding

对于 MutatingAdmissionPolicyBinding,操作始终是变更对象。

你可以使用 JSON Patch 或 Kubernetes 的应用配置(apply configuration)。

通过验证强制执行

现在,试着定义一个 ValidatingAdmissionPolicy。

下面是一个 ValidatingAdmissionPolicy 示例,它要求任何 Deployment 都有多个副本。

---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "enforce-multiple-replicas-deployments"
  annotations:
    kubernetes.io/description: "Require that matching Deployments have multiple replicas"
spec:
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      resources:   ["deployments"]      # Deployment API
      operations:  ["CREATE", "UPDATE"] # API 动词,会被转换为大写
  validations:
    - expression: "object.spec.replicas >= 2"

spec.validations 包含 CEL 表达式, 这些表达式使用通用表达式语言(CEL)验证请求。 如果某个表达式求值为 false,则根据 spec.failurePolicy 字段执行验证检查。

编写一个这样的策略并应用它。

或者,如果你想应用一个现成的清单:

kubectl apply --server-side -f https://k8s.io/examples/access/manifest-admission-control/vap-min-replicas.yaml

单独来看,这个策略不会起任何作用。

你可以试着创建一个 0 个或 1 个副本的 Deployment;这会成功(除非有其他策略阻止)。


要让它生效,你需要定义一个 ValidatingAdmissionPolicyBinding。

选择一个你要在其中执行新策略的命名空间。

下面是你所创建策略的一个示例 ValidatingAdmissionPolicyBinding:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: enforce-multiple-replicas-deployments-binding
spec:
  policyName: "enforce-multiple-replicas-deployments"
  validationActions: [Deny]
  matchResources:
    namespaceSelector:
      matchLabels:
        kubernetes.io/metadata.name: default # 改成你正在使用的命名空间

注意:

任何对命名空间有完全/管理员访问权限的人都可以写入其标签。这包括删除命名空间中的标签。

kubernetes.io/metadata.name 标签是受保护的,但如果你使用其他标签, 请注意确保只有可信用户能够删除或编辑你所选的标签。

基于该示例 YAML 编写一个清单(如果你使用的是 default 命名空间,可以直接使用而无需任何改动)。 使用 kubectl apply 应用该清单。

测试策略

现在测试该策略。 试着创建一个 Deployment, 然后用 kubectl scale 将其缩容到 0 个副本。会发生什么?

你可以把 ValidatingAdmissionPolicyBinding 的验证操作改为 Deny 之外的其他操作。 如果你选择 Warning 验证操作,然后试着把 Deployment 缩容到 0 个副本,会发生什么?

说明:

如果你确实把 ValidatingAdmissionPolicyBinding 改成只警告用户,就会有一个问题 …

名字不对!如果你修改 ValidatingAdmissionPolicyBinding 或关联的 ValidatingAdmissionPolicy, 使其只警告用户,你应该检查是否也需要修改策略的名称。 你需要修改名称,以确保命名不会误导他人。

现有资源不受影响

如果你有一个 0 个或 1 个副本的 Deployment, 并把 ValidatingAdmissionPolicyBinding 改回 Deny 模式,它不会影响任何现有资源。

(如果你想尝试把 Deployment 扩容到至少 2 个副本,可以用其他方式实现 —— 例如使用 控制器)。

ValidatingAdmissionPolicy 就介绍到这里。接下来你将了解 MutatingAdmissionPolicy。

在资源创建或变更时修改资源

在这个例子中,假设你想使用 Pod 安全性准入, 确保除系统命名空间之外的命名空间都执行某一 Pod 安全性标准。

与验证类似,你可以创建 MutatingAdmissionPolicy 在准入时修改资源。 你需要修改的 API 类型是 Namespace。

下面是一个实现其中部分功能的 MutatingAdmissionPolicy:

---
# 注意:在应用 default-pod-security-baseline 策略之前,请阅读相关说明,
# 以便理解该策略在信息安全方面的影响。
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
  name: "default-pod-security-baseline"
  annotations:
    kubernetes.io/description: "Default new namespaces to enforce the Baseline pod security standard"
spec:
  reinvocationPolicy: IfNeeded
  matchConstraints:
    resourceRules:
    - apiGroups:   [""]
      apiVersions: ["v1"]
      operations:  ["CREATE"]
      resources:   ["namespaces"]
  matchConditions:
    - name: "exclude-system-namespaces"
      # 如果名称以 "kube-" 开头,它就是系统命名空间。
      # 假定 API 服务器或集群管理工具会应用相关默认值。
      expression: "!object.metadata.name.startsWith('kube-')"
    - name: "no-existing-pod-security-label"
      expression: "!('pod-security.kubernetes.io/enforce' in object.metadata.labels)"
  mutations:
    - patchType: "ApplyConfiguration"
      applyConfiguration:
        expression: "Object{metadata: Object.metadata{labels: {'pod-security.kubernetes.io/enforce': 'baseline'}}}"

注意:

此策略设置的是默认值。 有能力更新命名空间的人可以移除命名空间上的 pod-security.kubernetes.io/enforce 标签。

如果你不确定这意味着什么,请阅读安全文档, 或征求外部的信息安全建议。

要应用该策略:

kubectl apply --server-side -f https://k8s.io/examples/access/manifest-admission-control/default-pod-security-baseline.yaml

需要 MutatingAdmissionPolicyBinding 来激活此策略;例如:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicyBinding
metadata:
  name: default-pod-security-baseline
spec:
  # 要应用的 MutatingAdmissionPolicy 名称
  policyName: default-pod-security-baseline

测试策略

试着创建一个名为 example 的新命名空间:

kubectl create ns example

检查它的标签:

kubectl describe ns example

尽管你没有指定 Pod 安全性准入的执行级别,该标签已被设置。

接下来,看看你能否找到绕过这些安全设置的办法。 为另一个 Namespace 创建一个 YAML 清单:

apiVersion: v1
kind: Namespace
metadata:
  name: another-example
  labels:
    pod-security.kubernetes.io/enforce: privileged

你可以使用 kubectl apply --server-side 从本地清单创建该命名空间。这样能行吗?

可以,而且新命名空间允许运行特权 Pod。

这个准入策略并未设置为验证或限制。它提供默认值,但你可以设置自己的值。 不过,你可以把变更准入与验证准入策略结合起来,这样既能强制执行某些要求,又能让人容易遵守。 (本教程不解释这一点,但你可以这样做)。

提供一个有用的默认值意味着,当人们什么都没设置时, 他们得到的结果比只看到一条错误消息要好。 设想你确实有一条验证规则,确保所有命名空间都必须至少执行 baseline 标准。 不知道这条规则的人可能会尝试部署某些东西, 然后在创建命名空间时立刻看到一条错误消息。

使用参数资源

参数资源允许策略配置与其定义分离。 策略可以定义 paramKind,它概述参数资源的组、版本和类别(也称为 GVK)。 然后,策略绑定将该策略关联到其绑定的作用域,并由特定的参数资源配置。

---
# 注意:在应用 default-pod-security-configurable 策略之前,请阅读相关说明,
# 以便理解该策略在信息安全方面的影响。
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
  name: "default-pod-security-configurable"
  annotations:
    kubernetes.io/description: "Default new namespaces to enforce the chosen pod security standard"
spec:
  reinvocationPolicy: IfNeeded
  paramKind:
    apiVersion: v1
    kind: ConfigMap
  matchConstraints:
    resourceRules:
    - apiGroups:   [""]
      apiVersions: ["v1"]
      operations:  ["CREATE"]
      resources:   ["namespaces"]
  matchConditions:
    - name: "exclude-system-namespaces"
      # 如果名称以 "kube-" 开头,它就是系统命名空间。
      # 假定 API 服务器或集群管理工具会应用相关默认值。
      expression: "!object.metadata.name.startsWith('kube-')"
    - name: "no-existing-pod-security-label"
      expression: "!('pod-security.kubernetes.io/enforce' in object.metadata.labels)"
  mutations:
    - patchType: "ApplyConfiguration"
      applyConfiguration:
        expression: "Object{metadata: Object.metadata{labels: {'pod-security.kubernetes.io/enforce': params.data.default}}}"

下面是一个 MutatingAdmissionPolicyBinding 示例:

---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicyBinding
metadata:
  name: default-pod-security-configurable
spec:
  # 要应用的 MutatingAdmissionPolicy 名称
  policyName: default-pod-security-configurable

  # 要使用的参数
  paramRef:
    # 如果 ConfigMap 不存在或为空,则不设置默认值
    # (但允许创建命名空间)
    parameterNotFoundAction: Allow

    # 参数所在的位置
    namespace: kube-system
    name: default-pod-security-standard

下面是放入 kube-system 命名空间的一个示例 ConfigMap:

---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: kube-system
  name: default-pod-security-standard
data:
  default: baseline # 也可以是 "restricted"

定义这两个对象。你应先创建 ConfigMap; 绑定要求参数资源已经存在(即使你计划之后修改它)。

现在,删除之前的 MutatingAdmissionPolicyBinding:

kubectl delete mutatingadmissionpolicybindings/default-pod-security-baseline

并创建一个新的命名空间:

kubectl create ns yet-another-example
kubectl describe ns yet-another-example

标签是否被设为默认值了?

修改参数

# 这会启动一个编辑器,让你修改该参数的 .data.default
kubectl --namespace kube-system edit configmap default-pod-security-standard

修改之后,再试着创建一个命名空间。会发生什么?

清理

要删除所创建的资源,请运行以下命令:

kubectl delete validatingadmissionpolices/enforce-multiple-replicas-deployments \
               validatingadmissionpolicybindings/enforce-multiple-replicas-deployments

kubectl delete mutatingadmissionpolicies/default-pod-security-baseline \
               mutatingadmissionpolicybindings/default-pod-security-baseline

kubectl delete mutatingadmissionpolicies/default-pod-security-configurable \
               mutatingadmissionpolicybindings/default-pod-security-configurable

kubectl --namespace kube-system delete configmaps/default-pod-security-standard

kubectl delete namespaces/example namespaces/another-example namespaces/yet-another-example

如果你创建了任何测试 Pod 或测试 Namespace,也请一并清理。

最后修改 October 20, 2026 at 9:33 AM PST: [zh] Add admission-policies.md and its yaml (9f322dde2a)