本页面概述了基于清单的准入控制配置。 基于清单的准入控制允许你从磁盘上的静态文件而非 Kubernetes API 加载准入 Webhook 和基于 CEL 的准入策略。这些策略从 API 服务器启动时就处于活跃状态, 独立于 etcd 运行, 并且可以保护基于 API 的准入资源免受修改。
要使用此功能,请在通过 --admission-control-config-file 传递给 kube-apiserver 的
AdmissionConfiguration
文件中配置 staticManifestsDir 字段。ManifestBasedAdmissionControlConfig
特性门控必须启用;
自 Kubernetes v1.37 起该门控默认启用。
通过 Kubernetes API 注册的准入策略和 Webhook(如 ValidatingAdmissionPolicy、 MutatingAdmissionPolicy、ValidatingWebhookConfiguration 和 MutatingWebhookConfiguration)有几个固有局限性:
基于清单的准入控制通过从磁盘上的文件加载配置来解决这些局限性。这些配置具有以下特点:
你可以在清单文件中包含以下资源类型。仅支持
admissionregistration.k8s.io/v1 API 版本。
| 插件名称 | 支持的资源类型 |
|---|---|
ValidatingAdmissionWebhook | ValidatingWebhookConfiguration |
MutatingAdmissionWebhook | MutatingWebhookConfiguration |
ValidatingAdmissionPolicy | ValidatingAdmissionPolicy、ValidatingAdmissionPolicyBinding |
MutatingAdmissionPolicy | MutatingAdmissionPolicy、MutatingAdmissionPolicyBinding |
你也可以使用 v1.List 将同一插件类型的多个资源包装在单个文档中。
每个准入插件的 staticManifestsDir 只能包含该插件允许的资源类型。
例如,为 ValidatingAdmissionPolicy 插件配置的目录只能包含
ValidatingAdmissionPolicy 和 ValidatingAdmissionPolicyBinding 资源。
要启用基于清单的准入控制,你需要:
ManifestBasedAdmissionControlConfig
特性门控(自 Kubernetes v1.37 起默认启用)。AdmissionConfiguration 文件,其 staticManifestsDir
字段指向包含清单文件的目录。为每个需要从磁盘加载清单的准入插件在插件配置中添加 staticManifestsDir。
每个插件都需要自己的目录。
# 这是一个示例 AdmissionConfiguration,将全部四个准入插件配置为
# 从磁盘上的静态文件加载基于清单的准入控制。
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: WebhookAdmissionConfiguration
kubeConfigFile: "<path-to-kubeconfig>"
staticManifestsDir: "/etc/kubernetes/admission/validating-webhooks/"
- name: MutatingAdmissionWebhook
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: WebhookAdmissionConfiguration
kubeConfigFile: "<path-to-kubeconfig>"
staticManifestsDir: "/etc/kubernetes/admission/mutating-webhooks/"
- name: ValidatingAdmissionPolicy
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: ValidatingAdmissionPolicyConfiguration
staticManifestsDir: "/etc/kubernetes/admission/validating-policies/"
- name: MutatingAdmissionPolicy
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: MutatingAdmissionPolicyConfiguration
staticManifestsDir: "/etc/kubernetes/admission/mutating-policies/"
staticManifestsDir 字段接受目录的绝对路径。目录中所有带 .yaml、.yml
或 .json 扩展名的直接子文件都会被加载。子目录和其他扩展名的文件会被忽略。
不支持 Glob 模式和相对路径。
使用 --admission-control-config-file 标志将此文件传递给 kube-apiserver。
每个准入插件使用特定配置类型:
| 插件 | apiVersion | kind |
|---|---|---|
ValidatingAdmissionWebhook | apiserver.config.k8s.io/v1 | WebhookAdmissionConfiguration |
MutatingAdmissionWebhook | apiserver.config.k8s.io/v1 | WebhookAdmissionConfiguration |
ValidatingAdmissionPolicy | apiserver.config.k8s.io/v1 | ValidatingAdmissionPolicyConfiguration |
MutatingAdmissionPolicy | apiserver.config.k8s.io/v1 | MutatingAdmissionPolicyConfiguration |
清单文件包含标准的 Kubernetes 资源定义。你可以使用 YAML 文档分隔符(---)
在单个文件中包含多个资源。
清单文件中的所有对象名称必须以 .static.k8s.io 后缀结尾。
例如:deny-privileged.static.k8s.io。
当 ManifestBasedAdmissionControlConfig 特性门控被启用时,
阻止创建名称以 .static.k8s.io 结尾的基于 API 的准入对象。
当特性门控被禁用时,则返回一个警告。
如果两个清单文件定义了相同类型的对象,且对象名称相同,则 API 服务器启动失败,显示描述性错误。
基于清单的准入配置独立存在,不能引用 API 资源。适用以下限制:
clientConfig.url。不允许使用 clientConfig.service
字段,因为服务网络可能在 API 服务器启动时不可用。spec.paramKind 字段。策略不能引用 ConfigMap
或其他集群对象作为参数。spec.paramRef 字段。spec.policyName
必须引用在同一清单文件集中定义的策略,且必须以 .static.k8s.io 结尾。清单文件使用严格解码器进行解码,该解码器会拒绝包含重复字段或未知字段的文件。 每个对象都会经历与 REST API 相同的默认设置和验证。
基于清单的准入控制的一个关键能力是能够拦截对准入配置资源本身的操作 (ValidatingAdmissionPolicy、MutatingAdmissionPolicy、 ValidatingWebhookConfiguration、MutatingWebhookConfiguration 及其绑定)。 基于 REST 的准入 Webhook 和策略不会对这些资源类型调用,以防止循环依赖, 但基于清单的策略可以对其执行规则,因为它们没有这种循环依赖。
以下示例防止删除或修改带有 platform.example.com/protected: "true" 标签的准入资源:
# 这是一个示例 ValidatingAdmissionPolicy,用于阻止删除或修改
# 带有 "platform.example.com/protected: true" 标签的
# 基于 API 的准入资源。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "example-protect-admission-resources.static.k8s.io"
annotations:
kubernetes.io/description: "Prevent modification or deletion of protected admission resources"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["admissionregistration.k8s.io"]
apiVersions: ["*"]
operations: ["DELETE", "UPDATE"]
resources:
- "validatingadmissionpolicies"
- "validatingadmissionpolicybindings"
- "mutatingadmissionpolicies"
- "mutatingadmissionpolicybindings"
- "validatingwebhookconfigurations"
- "mutatingwebhookconfigurations"
validations:
- expression: >-
!has(oldObject.metadata.labels) ||
!('platform.example.com/protected' in oldObject.metadata.labels) ||
oldObject.metadata.labels['platform.example.com/protected'] != 'true'
message: "Protected admission resources cannot be modified or deleted"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "example-protect-admission-resources-binding.static.k8s.io"
annotations:
kubernetes.io/description: "Bind protect-admission-resources policy to all admission resources"
spec:
policyName: "example-protect-admission-resources.static.k8s.io"
validationActions:
- Deny
以下示例定义了一个策略,除 kube-system 外拒绝所有名字空间中的特权容器:
# 这是一个示例 ValidatingAdmissionPolicy,除 kube-system 外
# 拒绝所有名字空间中的特权容器。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "example-deny-privileged.static.k8s.io"
annotations:
kubernetes.io/description: "Deny privileged containers outside kube-system"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
variables:
- name: allContainers
expression: >-
object.spec.containers +
(has(object.spec.initContainers) ? object.spec.initContainers : []) +
(has(object.spec.ephemeralContainers) ? object.spec.ephemeralContainers : [])
validations:
- expression: >-
!variables.allContainers.exists(c,
has(c.securityContext) && has(c.securityContext.privileged) &&
c.securityContext.privileged == true)
message: "Privileged containers are not allowed"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "example-deny-privileged-binding.static.k8s.io"
annotations:
kubernetes.io/description: "Bind deny-privileged policy to all namespaces except kube-system"
spec:
policyName: "example-deny-privileged.static.k8s.io"
validationActions:
- Deny
matchResources:
namespaceSelector:
matchExpressions:
- key: "kubernetes.io/metadata.name"
operator: NotIn
values: ["kube-system"]
将此文件放入为 ValidatingAdmissionPolicy 插件配置的
staticManifestsDir 目录中。策略及其绑定会被一起原子性地加载。
以下示例配置了一个调用外部 URL 的验证 Webhook:
# 这是一个示例 ValidatingWebhookConfiguration,调用一个外部的
# 基于 URL 的 Webhook 端点,以验证 Pod 的创建和更新。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: "example-security-webhook.static.k8s.io"
annotations:
kubernetes.io/description: "Validate pod creation and updates via external webhook"
webhooks:
- name: "security.platform.example.com"
clientConfig:
url: "https://security-webhook.platform.example.com:443/validate"
caBundle: "<base64-encoded-CA-bundle>"
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: Fail
Webhook URL 必须在启动时从 kube-apiserver 可达。仅支持基于 URL 的端点; 在基于清单的 Webhook 配置中不允许使用服务引用。
你可以使用 v1.List 将相关资源分组在单个文档中:
# 这是使用 v1.List 格式将 ValidatingAdmissionPolicy
# 及其绑定分组到单个文档中的示例。
apiVersion: v1
kind: List
items:
- apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "example-require-labels.static.k8s.io"
annotations:
kubernetes.io/description: "Require app.kubernetes.io/name label on all pods"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
validations:
- expression: >-
has(object.metadata.labels) &&
'app.kubernetes.io/name' in object.metadata.labels
message: "All pods must have the 'app.kubernetes.io/name' label"
- apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "example-require-labels-binding.static.k8s.io"
annotations:
kubernetes.io/description: "Bind require-labels policy to all namespaces except kube-system"
spec:
policyName: "example-require-labels.static.k8s.io"
validationActions:
- Deny
matchResources:
namespaceSelector:
matchExpressions:
- key: "kubernetes.io/metadata.name"
operator: NotIn
values: ["kube-system"]
基于清单的配置在基于 API 的配置之前被评估。 这确保了通过静态配置强制执行的平台级策略优先于基于 API 的策略。
对于准入配置资源本身(ValidatingAdmissionPolicy、 MutatingAdmissionPolicy、ValidatingAdmissionPolicyBinding、 MutatingAdmissionPolicyBinding、ValidatingWebhookConfiguration、 MutatingWebhookConfiguration),只评估基于清单的准入钩子。 为防止循环依赖,这些资源类型会跳过基于 API 的钩子。
非持久化的(虚拟)身份认证和授权资源(例如 TokenReview 和 SubjectAccessReview) 会被同时排除在准入 Webhook 和准入策略之外。基于清单的配置也无法拦截它们,因为这样做可能会使集群无法访问自己的身份认证和授权路径。
kube-apiserver 监视配置的目录以获取变更:
如果启动时存在无效的清单文件,API 服务器不会启动。 如果在运行时重新加载失败,会保留之前的有效配置并记录错误。
基于清单的准入控制提供以下指标用于监控重新加载健康状态:
| 类型 | 描述 | 指标 |
|---|---|---|
| Counter | 重新加载尝试总数,带有 status(success 或 failure)、plugin 和 apiserver_id_hash 标签。 | apiserver_manifest_admission_config_controller_automatic_reloads_total |
| Gauge | 最后一次重新加载尝试的时间戳,带有 status、plugin 和 apiserver_id_hash 标签。 | apiserver_manifest_admission_config_controller_automatic_reload_last_timestamp_seconds |
| Gauge | 当前配置信息(值始终为 1),带有 plugin、apiserver_id_hash 和 hash 标签。使用 hash 标签可以检测跨 API 服务器的配置漂移。 | apiserver_manifest_admission_config_controller_last_config_info |
plugin 标签标识指标适用的准入插件:
ValidatingAdmissionWebhook、MutatingAdmissionWebhook、
ValidatingAdmissionPolicy 或 MutatingAdmissionPolicy。
由于基于清单的对象名称以 .static.k8s.io 结尾,
现有的准入指标(如 apiserver_admission_webhook_rejection_count)
可以通过过滤 name 标签来识别基于清单的决策。
现有审计注解(如 validation.policy.admission.k8s.io/validation_failure
和 mutation.webhook.admission.k8s.io/round_0_index_0)包含对象名称。
你可以通过过滤以 .static.k8s.io 结尾的名称来识别基于清单的准入决策。
每个 kube-apiserver 实例独立加载自己的清单文件。 在具有多个 API 服务器实例的高可用性设置中:
apiserver_manifest_admission_config_controller_last_config_info
指标暴露了一个 hash 标签,你可以使用它来检测跨 API 服务器实例的配置漂移。此行为类似于其他基于文件的 kube-apiserver 配置, 如静态加密 和身份验证。
升级:启用此功能并提供清单配置是可选的。 没有清单配置的现有集群不会看到行为变化。
降级:在降级到没有此功能的版本之前:
AdmissionConfiguration 文件中移除 staticManifestsDir 条目。降级时,如果不移除 staticManifestsDir 配置,API 服务器会启动失败,
因为存在未知的配置字段。
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
| API 服务器启动失败 | 启动时存在无效的清单文件 | 检查 API 服务器日志中的验证错误。修复清单文件并重启。 |
| API 服务器启动失败 | 清单文件之间存在重复的对象名称 | 确保插件的 staticManifestsDir 内的所有对象名称唯一。 |
| 文件更新后策略未执行 | 重新加载验证失败 | 检查 automatic_reloads_total{status="failure"} 指标和 API 服务器日志。修复清单并等待下一个重新加载周期。 |
| Webhook 请求失败 | Webhook URL 不可达 | 验证 clientConfig.url 中指定的 URL 从 kube-apiserver 可访问。 |
无法创建带有 .static.k8s.io 后缀的 API 对象 | 名称后缀被特性门控保留 | 当特性门控启用时,.static.k8s.io 后缀保留用于基于清单的配置。为基于 API 的对象使用其他名称。 |