策略被拒绝,罪魁祸首是权限
目标
读取已安装的 Kyverno 的各个组件,在 generate、cleanup 被拒绝时通过聚合 ClusterRole 给控制器补充权限来修复, 并用真实请求区分 kyverno ConfigMap 中的 resourceFilters 和 webhooks 配置分别在哪里过滤请求。
为什么重要
Kyverno 不是单个程序,而是由准入、后台、清理、报告四个控制器各自使用自己的 ServiceAccount 运行的系统。 generate 规则创建的对象和清理策略删除的对象,是以对应控制器的权限处理的,而不是以提出请求的人的权限处理的,所以权限不足时, 一写下策略就会在准入阶段被拒绝。安装清单把默认权限压到最低,并设计成通过标签聚合 ClusterRole 来追加, 因此运维的基本做法是只补充需要的种类和动词,而不是授予通配符权限。反过来,想把某些请求排除在检查之外,有两个位置。 resourceFilters 是 API 服务器把请求发过来之后由 Kyverno 忽略,Webhook 的 namespaceSelector 则是 API 服务器根本不发送。 无论哪一种,只要删掉默认的排除项,就会出现 Kyverno 拦住自己或系统命名空间的事故。
步骤
- 在
/root/kca-install/inventory.json中写入version(kyverno-admission-controller 镜像的标签)、controllers(kyverno 命名空间中的 Deployment 名称 → spec.replicas 数字)、crd_count(API 组以 kyverno.io 结尾的 CRD 数量)、webhook_configs(名称中含 kyverno 的 ValidatingWebhookConfiguration、MutatingWebhookConfiguration 名称,排序后的数组)。 - 在
/root/kca-install/02-gen-pdb.yaml中编写并应用 ClusterPolicygen-pdb。规则default-pdb的作用是:当出现带标签kca.io/pdb=true的 Namespace 时,在其中以 synchronize true 生成 PodDisruptionBudgetdefault-pdb(minAvailable 1,selector app=web)。把应用命令的完整输出保存到/root/kca-install/gen-denied.txt。 - 在
/root/kca-install/03-role-bg.yaml中创建 ClusterRolekca-bg-pdb,添加标签rbac.kyverno.io/aggregate-to-background-controller: "true",并且只允许对 policy 组的 poddisruptionbudgets 执行 get、list、watch、create、update、delete(禁止通配符)。然后重新应用gen-pdb,创建带标签kca.io/pdb=true的命名空间kca-pdb-a,确认 PDB 被生成。记下生成的 PDB 的 UID,删除该 PDB 后,把重新生成的 PDB 的 UID 与之一起,分别以first_uid、second_uid写入/root/kca-install/pdb.json。 - 在
/root/kca-install/04-clean-short.yaml中编写并应用 ClusterCleanupPolicyclean-short。它每分钟(*/1 * * * *)删除命名空间kca-clean中带标签ttl=short的 Pod。把应用命令的完整输出保存到/root/kca-install/cleanup-denied.txt。 - 在
/root/kca-install/05-role-cleanup.yaml中应用 ClusterRolekca-cleanup-pods(标签rbac.kyverno.io/aggregate-to-cleanup-controller: "true",只对 core 的 pods 授予 get、list、watch、delete),然后重新应用clean-short。在命名空间kca-clean中创建带标签ttl=short的 Podshort和带标签ttl=long的 Podlong,等下一个整分钟过去、确认只有 short 消失之后,在/root/kca-install/cleanup.json中写入long_uid(留下的 long 的 UID)和last_execution(clean-short 的 status.lastExecutionTime)。 - 创建命名空间
kca-skip和kca-ctl,并在/root/kca-install/06-need-team.yaml中应用 ClusterPolicyneed-team(background false,规则team,要求这两个命名空间中的 Pod 带有 team 标签,Enforce)。然后在 kyverno 命名空间的 ConfigMapkyverno的resourceFilters中,保留原有所有条目,再追加[Pod,kca-skip,*]。kca-skip中没有标签的 Pod 必须被接受,kca-ctl中没有标签的 Pod 必须仍然被拒绝。 - 创建带标签
kca.io/webhook=skip的命名空间kca-sel,把kca-sel加入need-team的目标命名空间并重新应用。然后在 ConfigMapkyverno的webhooks值的 namespaceSelector 中,保留原有的 kube-system、kyverno 排除条件,再追加kca.io/webhookNotIn[skip]条件。Kyverno 更新后的kyverno-resource-validating-webhook-cfg中必须包含该条件,kca-sel中没有标签的 Pod 必须被接受,kca-ctl必须仍然被拒绝。不要把 kca-sel 放进 resourceFilters。 - 在
/root/kca-install/report.json中写入background_role(追加了 generate 权限的 ClusterRole 名称)、cleanup_role(追加了清理权限的 ClusterRole 名称)、pdb_regenerated(第 3 步的两个 UID 是否不同,布尔值)、skip_by(过滤掉 kca-skip 的配置键:resourceFilters或webhooks)、sel_by(过滤掉 kca-sel 的配置键)、default_exclusions(Webhook selector 中仍然保留的默认排除命名空间名称,排序后的数组)。
参考
- VM 中有 k3s 和 Kyverno v1.19.1(安装清单,每个控制器各 1 个)。
- 确认账号权限:
kubectl auth can-i create poddisruptionbudgets.policy --as system:serviceaccount:kyverno:kyverno-background-controller -n <ns> - 不保存、只查看判定结果:
kubectl -n <ns> run t --image=nginx:1.27-alpine --dry-run=server - 常见错误:把 resourceFilters 或 webhooks 的值整个替换成新值。默认的排除项会消失。
- 常见错误:给聚合 ClusterRole 授予
*动词。评分器会检查是否只有任务要求的动词。 - Customizing Permissions · Cleanup · Generate Rules · Kubernetes RBAC 聚合
已安装的 Kyverno 由什么组成
在 /root/kca-install/inventory.json 中写入 version(kyverno-admission-controller 镜像的标签)、controllers(kyverno 命名空间中的 Deployment 名称 → spec.replicas 数字)、crd_count(API 组以 kyverno.io 结尾的 CRD 数量)、webhook_configs(名称中含 kyverno 的 ValidatingWebhookConfiguration、MutatingWebhookConfiguration 名称,排序后的数组)。
CRD 的组在 spec.group 中。存在 kyverno.io、policies.kyverno.io、reports.kyverno.io 等多个组。Webhook 注册有时是 Kyverno 在创建策略时额外创建的,所以请读取当前实际存在的内容。
策略没问题,准入却拒绝了
在 /root/kca-install/02-gen-pdb.yaml 中编写并应用 ClusterPolicy gen-pdb。规则 default-pdb 的作用是:当出现带标签 kca.io/pdb=true 的 Namespace 时,在其中以 synchronize true 生成 PodDisruptionBudget default-pdb(minAvailable 1,selector app=web)。把应用命令的完整输出保存到 /root/kca-install/gen-denied.txt。
generate 规则的目标种类,是由 background controller 的 ServiceAccount 创建的,而不是由提出请求的人创建的。Kyverno 在接收策略时会预先检查该账号的权限。从拒绝信息中读出是谁缺少哪个动词。
用一个标签给控制器追加权限
在 /root/kca-install/03-role-bg.yaml 中创建 ClusterRole kca-bg-pdb,添加标签 rbac.kyverno.io/aggregate-to-background-controller: "true",并且只允许对 policy 组的 poddisruptionbudgets 执行 get、list、watch、create、update、delete(禁止通配符)。然后重新应用 gen-pdb,创建带标签 kca.io/pdb=true 的命名空间 kca-pdb-a,确认 PDB 被生成。记下生成的 PDB 的 UID,删除该 PDB 后,把重新生成的 PDB 的 UID 与之一起,分别以 first_uid、second_uid 写入 /root/kca-install/pdb.json。
Kyverno 安装的 kyverno:background-controller ClusterRole 带有 aggregationRule。不需要另外绑定新的 ClusterRole。启用了 synchronize 的生成物,即使删除也会被重新创建。
没有删除权限的清理策略
在 /root/kca-install/04-clean-short.yaml 中编写并应用 ClusterCleanupPolicy clean-short。它每分钟(*/1 * * * *)删除命名空间 kca-clean 中带标签 ttl=short 的 Pod。把应用命令的完整输出保存到 /root/kca-install/cleanup-denied.txt。
执行清理策略的是 cleanup controller。默认安装中该账号能删除哪些种类,可以在 kyverno:cleanup-controller ClusterRole 中看到。
只有短寿命的 Pod 在整分钟时消失了
在 /root/kca-install/05-role-cleanup.yaml 中应用 ClusterRole kca-cleanup-pods(标签 rbac.kyverno.io/aggregate-to-cleanup-controller: "true",只对 core 的 pods 授予 get、list、watch、delete),然后重新应用 clean-short。在命名空间 kca-clean 中创建带标签 ttl=short 的 Pod short 和带标签 ttl=long 的 Pod long,等下一个整分钟过去、确认只有 short 消失之后,在 /root/kca-install/cleanup.json 中写入 long_uid(留下的 long 的 UID)和 last_execution(clean-short 的 status.lastExecutionTime)。
cron 的调度以分钟为单位,所以最多要等 1 分钟。与 selector 不匹配的 Pod 必须原样保留。
Kyverno 收到了请求却装作没看见
创建命名空间 kca-skip 和 kca-ctl,并在 /root/kca-install/06-need-team.yaml 中应用 ClusterPolicy need-team(background false,规则 team,要求这两个命名空间中的 Pod 带有 team 标签,Enforce)。然后在 kyverno 命名空间的 ConfigMap kyverno 的 resourceFilters 中,保留原有所有条目,再追加 [Pod,kca-skip,*]。kca-skip 中没有标签的 Pod 必须被接受,kca-ctl 中没有标签的 Pod 必须仍然被拒绝。
resourceFilters 是把若干个 [종류,네임스페이스,이름](占位符依次为种类、命名空间、名称)条目用空格连接起来的字符串。默认值中包含 kube-system、kyverno 命名空间和 Event 等条目,所以整个替换会让 Kyverno 连自己的资源也去检查。Kyverno 无需重启就会重新读取这个 ConfigMap。
让 API 服务器根本不去询问
创建带标签 kca.io/webhook=skip 的命名空间 kca-sel,把 kca-sel 加入 need-team 的目标命名空间并重新应用。然后在 ConfigMap kyverno 的 webhooks 值的 namespaceSelector 中,保留原有的 kube-system、kyverno 排除条件,再追加 kca.io/webhook NotIn [skip] 条件。Kyverno 更新后的 kyverno-resource-validating-webhook-cfg 中必须包含该条件,kca-sel 中没有标签的 Pod 必须被接受,kca-ctl 必须仍然被拒绝。不要把 kca-sel 放进 resourceFilters。
webhooks 的值是 JSON 字符串。Kyverno 会把其中的 selector 复制到自己管理的 Webhook 注册对象上,API 服务器对 selector 不匹配的命名空间的请求,连发都不会发给 Kyverno。直接修改注册对象的话,Kyverno 会把它改回去。
报告权限和过滤器放在了哪里
在 /root/kca-install/report.json 中写入 background_role(追加了 generate 权限的 ClusterRole 名称)、cleanup_role(追加了清理权限的 ClusterRole 名称)、pdb_regenerated(第 3 步的两个 UID 是否不同,布尔值)、skip_by(过滤掉 kca-skip 的配置键:resourceFilters 或 webhooks)、sel_by(过滤掉 kca-sel 的配置键)、default_exclusions(Webhook selector 中仍然保留的默认排除命名空间名称,排序后的数组)。
依据前面步骤留下的文件,以及当前的 ConfigMap 和 Webhook 注册对象来写。评分器会在集群中重新计算同样的值。