TT Lab
开始
学习 学习路径 课程

KCA — Kyverno 认证助理

策略被拒绝,罪魁祸首是权限

在 TT Lab 中继续学习

目标

读取已安装的 Kyverno 的各个组件,在 generate、cleanup 被拒绝时通过聚合 ClusterRole 给控制器补充权限来修复, 并用真实请求区分 kyverno ConfigMap 中的 resourceFilters 和 webhooks 配置分别在哪里过滤请求。

为什么重要

Kyverno 不是单个程序,而是由准入、后台、清理、报告四个控制器各自使用自己的 ServiceAccount 运行的系统。 generate 规则创建的对象和清理策略删除的对象,是以对应控制器的权限处理的,而不是以提出请求的人的权限处理的,所以权限不足时, 一写下策略就会在准入阶段被拒绝。安装清单把默认权限压到最低,并设计成通过标签聚合 ClusterRole 来追加, 因此运维的基本做法是只补充需要的种类和动词,而不是授予通配符权限。反过来,想把某些请求排除在检查之外,有两个位置。 resourceFilters 是 API 服务器把请求发过来之后由 Kyverno 忽略,Webhook 的 namespaceSelector 则是 API 服务器根本不发送。 无论哪一种,只要删掉默认的排除项,就会出现 Kyverno 拦住自己或系统命名空间的事故。

步骤

  1. 在 /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 名称,排序后的数组)。
  2. 在 /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。
  3. 在 /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。
  4. 在 /root/kca-install/04-clean-short.yaml 中编写并应用 ClusterCleanupPolicy clean-short。它每分钟(*/1 * * * *)删除命名空间 kca-clean 中带标签 ttl=short 的 Pod。把应用命令的完整输出保存到 /root/kca-install/cleanup-denied.txt。
  5. 在 /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)。
  6. 创建命名空间 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 必须仍然被拒绝。
  7. 创建带标签 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。
  8. 在 /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 中仍然保留的默认排除命名空间名称,排序后的数组)。

参考

已安装的 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 注册对象来写。评分器会在集群中重新计算同样的值。