本页面让你试用声明式准入策略,这类策略允许你使用通用表达式语言(CEL)验证或变更资源。
Kubernetes 1.37 支持两种准入策略:
本教程介绍这两种准入策略。
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
要定义准入策略,你必须是集群管理员。请确保你对用于学习的集群具有管理员访问权限。
对于 ValidatingAdmissionPolicy,你需要:
对于 MutatingAdmissionPolicy,你需要:
要检查版本,请运行 kubectl version。如果你运行的是较旧版本的 Kubernetes,请查阅对应版本的文档。
声明式准入策略为准入 Webhook 提供了一种声明式的、进程内的替代方案。
通过使用通用表达式语言(CEL)来声明策略规则,这些策略直接在 API 服务器内部进行评估。
这些策略高度可配置,使策略作者能够定义可参数化的逻辑,并根据集群管理员的需要限定到特定资源。
这两种策略的用途不同。
ValidatingAdmissionPolicy 用于强制执行约束。
MutatingAdmissionPolicy 用于在准入时修改资源。
每个被应用的策略都有一个策略对象(ValidatingAdmissionPolicy 或 MutatingAdmissionPolicy) 和一个独立的绑定对象(ValidatingAdmissionPolicyBinding 或 MutatingAdmissionPolicyBinding)。
你还可以使用参数,参数是可选的。要了解更多信息, 请参阅参数资源(ValidatingAdmissionPolicy 或参数资源(MutatingAdmissionPolicy)。
策略对象使用通用表达式语言(CEL)描述策略的抽象逻辑。 例如,ValidatingAdmissionPolicy 可以强制副本数量限制或确保存在特定的标签, 而 MutatingAdmissionPolicy 可以修改资源,例如为命名空间添加默认标签。
绑定对象将策略与你的集群关联并提供作用域。
ValidatingAdmissionPolicyBinding 或 MutatingAdmissionPolicyBinding 将策略连接到特定资源。
如果你只想对特定子集的资源强制执行策略,
那么绑定就是你缩小策略作用域的地方(使用 matchResources)。
参数允许将策略行为的配置与其定义分离。参数资源是指 API 中可用的 Kubernetes 资源。
它们可以是内置的 API 类型(例如 ConfigMap),
也可以是定制资源。
然后,策略绑定使用 spec.paramRef 引用实际的参数资源。
如果策略不需要参数,则不设置 spec.paramKind。
这两种策略都依赖一种称为通用表达式语言(CEL)的表达式语言。 阅读 Kubernetes 中的通用表达式语言以了解更多。
如果你刚接触 CEL,可以先练习编写一个非常简单的表达式,例如 false || true。
你可以在 CEL Playground 中测试 CEL 表达式。
每个准入策略绑定必须指定一个或多个操作,以声明策略如何被执行。
对于 ValidatingAdmissionPolicyBinding,支持的 validationActions 包括:
策略检查失败或发生错误时,会按这些操作执行。
只有当 failurePolicy 设为 Fail(或未指定)时,
由 failurePolicy 定义的失败才会按这些操作执行。
有关策略审计日志的更多细节, 请参见审计注解:验证失败。
不能同时使用 Deny 和 Warn,因为这种组合会在 API 响应体和 HTTP Warning: 头中重复验证失败信息。
对于 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,也请一并清理。