把 validate 规则和内置策略比一比
目标
使用 Kyverno ClusterPolicy 编写强制标签和禁止标签规则,并在真实集群中用 Kubernetes 内置的 ValidatingAdmissionPolicy 实现相同检查,以比较这两种方法的差异。
为什么重要
要判断是否需要引入策略引擎,必须了解内置功能的能力边界。如果全部需求只是用 CEL 检查一个字段,那么 ValidatingAdmissionPolicy 就已足够;这样既能减少一个需要运维的组件,也少了一项管理 Webhook 证书续期的工作。真正能够证明 Kyverno 价值的,是 generate、mutate、镜像验证等内置策略无法完成的任务。在本实验中并排创建两份清单,可以切身体会这条边界在哪里。此外,练习为每条规则设置不同的强制级别,也是实际引入流程的关键。
步骤
- 创建
/root/kca-validate/目录,在policy.yaml中填写 apiVersionkyverno.io/v1、kindClusterPolicy、metadata.namekca-require-app-label,并将spec.rules[0].name设为check-app-label。 - 在
spec.rules[0].match.any[0].resources.kinds中加入Pod和Deployment,并在同一位置的namespaces中加入kca-app。 - 填写
spec.rules[0].validate.message,并通过validate.pattern要求metadata.labels中的app.kubernetes.io/name必须是非空值。 - 将
spec.rules[0].validate.failureAction设为Enforce。不要使用策略级别的spec.validationFailureAction。 - 添加第二条规则
deny-latest-tag。填写validate.message,在validate.deny.conditions.all[0]中加入指向容器镜像的 key、operator、value,并将该规则的validate.failureAction设为Audit。 - 在
spec.rules[0].exclude.any[0].resources.namespaces中加入kube-system和kyverno。 - 在集群中实际创建 ValidatingAdmissionPolicy
kca-require-app-label。spec.failurePolicy设为Fail;spec.matchConstraints应匹配apps组中的deployments,操作为 CREATE、UPDATE;spec.validations[0].expression必须是检查app.kubernetes.io/name标签是否存在的 CEL 表达式。 - 在集群中创建命名空间
kca-app并添加标签kca=enforced,再实际创建 ValidatingAdmissionPolicyBindingkca-require-app-label-binding。spec.policyName设为kca-require-app-label,spec.validationActions中加入Deny,spec.matchResources.namespaceSelector.matchLabels中加入kca: enforced。
参考
- 常见的 CEL 写法会组合使用
has(object.metadata.labels)和索引访问。kubectl explain validatingadmissionpolicy.spec.validations会有所帮助。 - 检查文件时,可以像
yq '.spec.rules[1].validate' /root/kca-validate/policy.yaml这样只提取局部内容。 - 常见错误 1:保留
spec.validationFailureAction的同时又添加规则级字段。本实验必须删除策略级字段才能通过。 - 常见错误 2:只创建 ValidatingAdmissionPolicy,却没有绑定。即使策略存在,也不会有任何请求经过它。
搭建 ClusterPolicy 骨架
创建 /root/kca-validate/ 目录,在 policy.yaml 中填写 apiVersion kyverno.io/v1、kind ClusterPolicy、metadata.name kca-require-app-label,并将 spec.rules[0].name 设为 check-app-label。
ClusterPolicy 是 kyverno.io 组中的集群范围资源。rules 是数组,每条规则都必须有名称。
选择要匹配的对象
在 spec.rules[0].match.any[0].resources.kinds 中加入 Pod 和 Deployment,并在同一位置的 namespaces 中加入 kca-app。
match.any 是数组,每个元素的 resources 中可以放入 kinds、namespaces、names、selector。同时缩小资源类型和命名空间范围,可以减少经过 Webhook 的请求数量。
通过模式匹配要求必备标签
填写 spec.rules[0].validate.message,并通过 validate.pattern 要求 metadata.labels 中的 app.kubernetes.io/name 必须是非空值。
pattern 按照待检查对象的形状原样绘制,并在值的位置填入运算符。请回想要求非空值应使用哪个运算符。
按规则设置强制级别
将 spec.rules[0].validate.failureAction 设为 Enforce。不要使用策略级别的 spec.validationFailureAction。
策略级字段已被弃用,因此不能保留。强制级别已下移到 validate 块中;未指定时默认为观察模式。
用 deny.conditions 添加第二条规则
添加第二条规则 deny-latest-tag。填写 validate.message,在 validate.deny.conditions.all[0] 中加入指向容器镜像的 key、operator、value,并将该规则的 validate.failureAction 设为 Audit。
条件包含 key、operator、value 三部分。使用 all 还是 any 来组合多个条件,会改变其含义。只将这条规则设为观察模式。
排除系统命名空间
在 spec.rules[0].exclude.any[0].resources.namespaces 中加入 kube-system 和 kyverno。
exclude 的结构与 match 相同。如果策略阻止了 Kyverno 自身或控制平面组件,恢复会非常困难。
用内置策略实现相同规则
在集群中实际创建 ValidatingAdmissionPolicy kca-require-app-label。spec.failurePolicy 设为 Fail;spec.matchConstraints 应匹配 apps 组中的 deployments,操作为 CREATE、UPDATE;spec.validations[0].expression 必须是检查 app.kubernetes.io/name 标签是否存在的 CEL 表达式。
ValidatingAdmissionPolicy 不是 CRD,而是 Kubernetes 内置资源,可以直接 apply。表达式使用 CEL,并通过 object 访问检查对象。
绑定策略并选择目标命名空间
在集群中创建命名空间 kca-app 并添加标签 kca=enforced,再实际创建 ValidatingAdmissionPolicyBinding kca-require-app-label-binding。spec.policyName 设为 kca-require-app-label,spec.validationActions 中加入 Deny,spec.matchResources.namespaceSelector.matchLabels 中加入 kca: enforced。
内置策略中的策略与绑定是一对资源。没有绑定时,策略只会存在,却不会作用于任何请求;具体执行什么动作也由绑定决定。