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

KCA — Kyverno 认证助理

标签规则被放宽了,却没有人发现

在 TT Lab 中继续学习

目标

用 Kyverno CLI 的 apply、test、jp,在把策略部署到集群之前就判定它会拒绝什么、修改什么、生成什么, 并把这些判定固化为测试套件和 CI 脚本。

为什么重要

准入策略出错时是悄无声息的。把验证规则改松的失误,只是什么请求都不拦,却不会报任何错误; mutate 规则中的一个锚点,会改变它是否去动已经有值的资源。等到在生产集群里才发现这种差别,就已经晚了。 kyverno test 会先写下“这个资源通过、那个资源失败、这个资源会被这样改”的预期,再与引擎的实际判定比较。 这样,当修改策略的人无意中放宽了规则时,测试会最先亮红灯。集群中不存在的 ConfigMap 或 API 调用结果, 用 values 文件补上,条件表达式的 JMESPath 用 kyverno jp 单独运行,像对待代码一样验证策略仓库。

步骤

  1. 在 /root/kca-cli/validate/policy.yaml 中编写 ClusterPolicy require-team。规则 check-team 选中 Pod,要求 metadata.labels.team 必须是非空值,failureAction 为 Enforce。在 /root/kca-cli/validate/resources.yaml 中放入 Pod good(标签 team=pay)、没有标签的 Pod bad,以及 Pod 模板中没有 team 标签的 Deployment web(均在 namespace default 中)。运行 kyverno apply 并加上 --policy-report,把标准输出保存到 /root/kca-cli/apply-report.yaml。
  2. 在 /root/kca-cli/validate/kyverno-test.yaml 中编写 Test(apiVersion cli.kyverno.io/v1alpha1)。policies 为 policy.yaml,resources 为 resources.yaml,results 中期望 good 为 pass,bad 为 fail,Deployment web 以第 1 步报告中出现的自动生成规则名称为 fail。kyverno test /root/kca-cli/validate 必须让 3 条全部通过。
  3. 把 /root/kca-cli/validate 整个复制到 /root/kca-cli/regressed,在副本的 policy.yaml 中只把 team 键改成相等锚点 =(team)(标签不存在时反而会通过的常见错误)。把 kyverno test /root/kca-cli/regressed --remove-color 的完整输出保存到 /root/kca-cli/regression.txt,并确认退出码。不要只看汇总行和退出码,而要在表格的 RESULT、REASON 列中找出预期与实际不一致的行。原始的 validate 必须仍然通过。
  4. 在 /root/kca-cli/mutate/ 中放入 ClusterPolicy add-managed-by(规则 add-label,在 Pod 上添加 managed-by: kyverno 标签,但只在该标签不存在时添加,使用追加锚点)、resources.yaml(没有标签的 Pod plain,带有 managed-by: helm 标签的 Pod owned)、mutate 之后的预期样子 patched.yaml(plain 被加上标签的 Pod)以及 kyverno-test.yaml。测试要把 plain 与 patchedResources 比较并期望 pass,owned 期望 skip,kyverno test /root/kca-cli/mutate 必须通过。
  5. 在 /root/kca-cli/generate/ 中放入 ClusterPolicy ns-quota(规则 gen-quota,出现 Namespace 时,在该命名空间中以 spec.hard.pods: "10" 生成 ResourceQuota default-quota,synchronize false)、resources.yaml(Namespace team-a)、预期生成物 generated.yaml,以及 kyverno-test.yaml(针对 team-a 比较 generatedResource,pass),并让 kyverno test /root/kca-cli/generate 通过。
  6. 在 /root/kca-cli/context/ 中放入 ClusterPolicy allowed-registries。规则 check-registry 针对 Pod,以上下文 reg 读取 ConfigMap platform/registry-config,并用 foreach 遍历所有容器镜像,只要有镜像不符合 reg.data.allowed(以逗号分隔的模式列表)中的任何一个模式,就拒绝(Enforce)。resources.yaml 中放入只有一个镜像 registry.lab/web:1.0 的 Pod internal,以及在其基础上再加一个镜像为 docker.io/busybox:1.36 的容器 sidecar 的 Pod external。由于没有集群,用 values.yaml(kind Values)把 reg.data.allowed 填充为 registry.lab/*,再通过 kyverno-test.yaml 的 variables 关联起来,让 internal pass、external fail 通过测试。
  7. 在 /root/kca-cli/jp/query.txt 中写一个 JMESPath 表达式。它以 Pod 对象为输入,必须返回镜像不以 registry.lab/ 开头的容器的名称列表。在 /root/kca-cli/jp/external.yaml 中只放入第 6 步的 Pod external,并把 kyverno jp query -i jp/external.yaml -q jp/query.txt 的输出保存到 /root/kca-cli/jp/result.json。
  8. 把 /root/kca-cli/ci.sh 做成可执行脚本。以第一个参数接收根目录(没有则用 /root/kca-cli),在其下的 validate、mutate、generate、context 四个目录中各运行一次 kyverno test,只要有一个失败,就必须以非 0 的值退出。如第 3 步所见,即使退出码为 0,只要输出表中有预期与实际不一致的行(REASON 为 Want ...),也必须按失败处理。不要运行 regressed。评分器会用多种方式破坏副本中的策略和测试,检查脚本是否失败。

参考

在部署策略之前,先问问引擎

在 /root/kca-cli/validate/policy.yaml 中编写 ClusterPolicy require-team。规则 check-team 选中 Pod,要求 metadata.labels.team 必须是非空值,failureAction 为 Enforce。在 /root/kca-cli/validate/resources.yaml 中放入 Pod good(标签 team=pay)、没有标签的 Pod bad,以及 Pod 模板中没有 team 标签的 Deployment web(均在 namespace default 中)。运行 kyverno apply 并加上 --policy-report,把标准输出保存到 /root/kca-cli/apply-report.yaml。

格式为 kyverno apply <정책> --resource <리소스>(占位符依次为策略、资源)。到报告中确认针对 Pod 的规则是否也适用于 Deployment,如果适用,规则名称被报告成什么。有违反时退出码不是 0。

先写下预期结果的测试

在 /root/kca-cli/validate/kyverno-test.yaml 中编写 Test(apiVersion cli.kyverno.io/v1alpha1)。policies 为 policy.yaml,resources 为 resources.yaml,results 中期望 good 为 pass,bad 为 fail,Deployment web 以第 1 步报告中出现的自动生成规则名称为 fail。kyverno test /root/kca-cli/validate 必须让 3 条全部通过。

results 的每个条目都要写 policy、rule、resources、kind、result。由 Pod 规则自动生成的规则,名称前面会带一个前缀。

放宽了标签规则,测试最先发现了

把 /root/kca-cli/validate 整个复制到 /root/kca-cli/regressed,在副本的 policy.yaml 中只把 team 键改成相等锚点 =(team)(标签不存在时反而会通过的常见错误)。把 kyverno test /root/kca-cli/regressed --remove-color 的完整输出保存到 /root/kca-cli/regression.txt,并确认退出码。不要只看汇总行和退出码,而要在表格的 RESULT、REASON 列中找出预期与实际不一致的行。原始的 validate 必须仍然通过。

=(key) 只在键存在时才检查值。看看哪些资源有标签映射却没有 team。这个镜像中的 CLI(1.13.2)在预期为 fail 的资源被判定为 pass 时,虽然会在 REASON 中写下这种不一致,汇总和退出码却仍然显示通过(实测)。测试工具是否把所有不一致都计为失败,只有像这样故意弄坏一次才知道。

连变更后的结果也要比较的 mutate 测试

在 /root/kca-cli/mutate/ 中放入 ClusterPolicy add-managed-by(规则 add-label,在 Pod 上添加 managed-by: kyverno 标签,但只在该标签不存在时添加,使用追加锚点)、resources.yaml(没有标签的 Pod plain,带有 managed-by: helm 标签的 Pod owned)、mutate 之后的预期样子 patched.yaml(plain 被加上标签的 Pod)以及 kyverno-test.yaml。测试要把 plain 与 patchedResources 比较并期望 pass,owned 期望 skip,kyverno test /root/kca-cli/mutate 必须通过。

+(key): value 只在键不存在时才写入。对已经有标签的资源,规则什么都不会改,所以结果可能不是 pass。patchedResources 是文件名。

提前比较将要生成的对象的 generate 测试

在 /root/kca-cli/generate/ 中放入 ClusterPolicy ns-quota(规则 gen-quota,出现 Namespace 时,在该命名空间中以 spec.hard.pods: "10" 生成 ResourceQuota default-quota,synchronize false)、resources.yaml(Namespace team-a)、预期生成物 generated.yaml,以及 kyverno-test.yaml(针对 team-a 比较 generatedResource,pass),并让 kyverno test /root/kca-cli/generate 通过。

要生成的对象的 namespace 使用请求对象的名称作为变量。预期生成物中必须包含 metadata.namespace,比较才能对上。

没有集群也能填充 ConfigMap 上下文

在 /root/kca-cli/context/ 中放入 ClusterPolicy allowed-registries。规则 check-registry 针对 Pod,以上下文 reg 读取 ConfigMap platform/registry-config,并用 foreach 遍历所有容器镜像,只要有镜像不符合 reg.data.allowed(以逗号分隔的模式列表)中的任何一个模式,就拒绝(Enforce)。resources.yaml 中放入只有一个镜像 registry.lab/web:1.0 的 Pod internal,以及在其基础上再加一个镜像为 docker.io/busybox:1.36 的容器 sidecar 的 Pod external。由于没有集群,用 values.yaml(kind Values)把 reg.data.allowed 填充为 registry.lab/*,再通过 kyverno-test.yaml 的 variables 关联起来,让 internal pass、external fail 通过测试。

CLI 无法查询上下文中的 configMap,所以通过规则级的 values 提供变量值。有把逗号分隔的字符串转成列表的 JMESPath 函数。AnyNotIn 能理解值列表中的通配符。

先在策略之外运行条件表达式

在 /root/kca-cli/jp/query.txt 中写一个 JMESPath 表达式。它以 Pod 对象为输入,必须返回镜像不以 registry.lab/ 开头的容器的名称列表。在 /root/kca-cli/jp/external.yaml 中只放入第 6 步的 Pod external,并把 kyverno jp query -i jp/external.yaml -q jp/query.txt 的输出保存到 /root/kca-cli/jp/result.json。

使用过滤表达式 [?조건](占位符为条件)和字符串函数 starts_with。JMESPath 中字符串字面量用单引号(反引号是 JSON 字面量)。评分器也会用其他 Pod 来运行表达式。

策略仓库的 CI 守门人

把 /root/kca-cli/ci.sh 做成可执行脚本。以第一个参数接收根目录(没有则用 /root/kca-cli),在其下的 validate、mutate、generate、context 四个目录中各运行一次 kyverno test,只要有一个失败,就必须以非 0 的值退出。如第 3 步所见,即使退出码为 0,只要输出表中有预期与实际不一致的行(REASON 为 Want ...),也必须按失败处理。不要运行 regressed。评分器会用多种方式破坏副本中的策略和测试,检查脚本是否失败。

把输出放进变量或文件,同时检查退出码和不一致的字样。无论是在循环中遇到第一个失败就结束,还是全部运行完再汇总后结束,只要结果码正确即可。混入颜色码会让字符串搜索落空,所以要使用 --remove-color。