写必填标签与仓库限制的策略
目标
亲自编写要求必需标签的验证策略和限制镜像仓库的拒绝规则,在本地确认通过、失败判定及策略报告。最后,用狭窄的范围表达合理的例外。
为什么重要
验证策略中导致事故的通常不是条件表达式,而是匹配范围。条件错误会在测试中被发现,但匹配错误时,策略会悄无声息地什么也不做,因此无人察觉。所以,同时放入应当通过和应当被阻止的资源进行测试是编写策略的基本功。还有一点,拒绝消息是策略的一半。部署受阻的人只能看到 kubectl apply 输出的一行文字;如果这一行不够友好,策略的全部运维成本都会变成咨询请求。最后,本环境中没有运行策略引擎控制器,因此集群不会实际阻止错误的 Pod。判定通过 kyverno apply 在本地进行,评分也会检查策略 YAML 的结构和执行结果。
步骤
- 创建
/root/policy/validate/require-labels.yaml。apiVersion: kyverno.io/v1、kind: ClusterPolicy,metadata.name为require-labels。将spec.validationFailureAction设为Enforce(或将规则的validate.failureAction设为Enforce),将spec.background设为true,并把spec.rules[0].name命名为check-required-labels。 - 在同一规则中,通过
match.any[0].resources.kinds指定Pod,通过namespaces指定pol-lab。然后在规则的exclude.any[0].resources.namespaces中排除kube-system。 - 在
validate.pattern.metadata.labels下,要求app.kubernetes.io/name和team两个标签的值均为"?*"。在同一个validate中加入message,内容至少 15 个字符,并让人能够知道该修正什么。 - 运行
kyverno apply /root/policy/validate/require-labels.yaml --resource /opt/lab/fixtures/policy/resources/good-pod.yaml,将结果保存为/root/policy/validate/out/pass.txt。结果中必须显示名为require-labels的策略和通过标记,失败数必须为 0。 - 对
/opt/lab/fixtures/policy/resources/bad-pod.yaml运行同一策略,并保存为/root/policy/validate/out/fail.txt(失败数至少为 1,并包含策略名称)。然后在/root/policy/validate/out/fail-note.txt中用韩文写明缺少哪个标签而触发策略。 - 在
/root/policy/validate/restrict-registry.yaml中创建第二个ClusterPolicy。在规则中使用validate.deny.conditions.all(或any),将registry.labhub.io设为允许的镜像仓库,拒绝来自其他仓库的镜像。然后对/opt/lab/fixtures/policy/resources/bad-registry.yaml运行它,并将结果保存为/root/policy/validate/out/registry.txt(必须显示拒绝或失败)。 - 在
restrict-registry.yaml的规则中添加preconditions。使用类似{{ request.operation }}的请求上下文变量,使规则仅在CREATE、UPDATE时运行。然后在/root/policy/validate/out/precondition-note.txt中写明 preconditions 与deny的区别——必须同时说明 preconditions 为假时规则会被跳过,而deny是规则执行结果所产生的拒绝。 - 对
require-labels.yaml,将 good-pod 和 bad-pod 两者都通过--resource传入,使用--policy-report运行并保存为/root/policy/validate/out/policy-report.yaml(summary.pass至少为 1,summary.fail至少为 1)。然后在/root/policy/validate/exception.yaml中创建kind: PolicyException,将spec.exceptions[0].policyName设为require-labels、ruleNames设为check-required-labels,并将spec.match.any[0].resources.names像legacy-batch-*一样缩小到名称级别。
参考
- 基本运行形式是
kyverno apply <정책> --resource <매니페스트>。-t以表格输出,--detailed-results输出详细信息,--policy-report以报告形式输出。 - 如果结果文件中没有显示策略名称,请添加
-t或--detailed-results。如果只保存摘要行,策略名称会缺失。 - 只要存在一个失败,
kyverno apply的退出码就是 1。如果不是在流水线中,可以保持原样。 - 如果
--policy-report输出前混入进度消息,yq将无法读取。请从apiVersion:行开始截取并保存(sed -n '/^apiVersion:/,$p')。 - 常见错误 1:遗漏
exclude。如果系统命名空间也成为目标,集群会面临风险。 - 常见错误 2:为整个策略设置例外。未使用
ruleNames和资源names缩小范围的例外,等同于关闭策略。
确定策略骨架和强制模式
创建 /root/policy/validate/require-labels.yaml。apiVersion: kyverno.io/v1、kind: ClusterPolicy,metadata.name 为 require-labels。将 spec.validationFailureAction 设为 Enforce(或将规则的 validate.failureAction 设为 Enforce),将 spec.background 设为 true,并把 spec.rules[0].name 命名为 check-required-labels。
ClusterPolicy 是 kyverno.io 组的 CRD。在 spec 中决定失败时是阻止还是只记录,以及是否将现有资源也视为检查对象。
缩小适用对象和排除对象
在同一规则中,通过 match.any[0].resources.kinds 指定 Pod,通过 namespaces 指定 pol-lab。然后在规则的 exclude.any[0].resources.namespaces 中排除 kube-system。
在 match 的 any 下,通过 resources 写入类型和命名空间。如果遗漏配套的 exclude,系统命名空间也会成为目标。
编写必需标签模式和拒绝消息
在 validate.pattern.metadata.labels 下,要求 app.kubernetes.io/name 和 team 两个标签的值均为 "?*"。在同一个 validate 中加入 message,内容至少 15 个字符,并让人能够知道该修正什么。
pattern 按照与要检查的对象相同的形状编写。有一个专门表示“只要存在即可”的运算符。消息是被拒绝者能够阅读的唯一文档。
用应当通过的 Pod 运行测试
运行 kyverno apply /root/policy/validate/require-labels.yaml --resource /opt/lab/fixtures/policy/resources/good-pod.yaml,将结果保存为 /root/policy/validate/out/pass.txt。结果中必须显示名为 require-labels 的策略和通过标记,失败数必须为 0。
kyverno CLI 会在本地评估策略文件和通过 --resource 接收的清单。请选择能让结果显示策略名称的输出格式。
用违规 Pod 运行并写明原因
对 /opt/lab/fixtures/policy/resources/bad-pod.yaml 运行同一策略,并保存为 /root/policy/validate/out/fail.txt(失败数至少为 1,并包含策略名称)。然后在 /root/policy/validate/out/fail-note.txt 中用韩文写明缺少哪个标签而触发策略。
如果只确认通过,就无法将该策略与完全没有匹配任何内容的策略区分开。还要记住,存在失败时退出码为 1。
编写限制镜像仓库的 deny 规则
在 /root/policy/validate/restrict-registry.yaml 中创建第二个 ClusterPolicy。在规则中使用 validate.deny.conditions.all(或 any),将 registry.labhub.io 设为允许的镜像仓库,拒绝来自其他仓库的镜像。然后对 /opt/lab/fixtures/policy/resources/bad-registry.yaml 运行它,并将结果保存为 /root/policy/validate/out/registry.txt(必须显示拒绝或失败)。
像“如果不在列表中就拒绝”这样的条件很难用 pattern 表达。请思考 conditions 下 any 和 all 中哪一个合适。
使用 preconditions 缩小规则适用范围
在 restrict-registry.yaml 的规则中添加 preconditions。使用类似 {{ request.operation }} 的请求上下文变量,使规则仅在 CREATE、UPDATE 时运行。然后在 /root/policy/validate/out/precondition-note.txt 中写明 preconditions 与 deny 的区别——必须同时说明 preconditions 为假时规则会被跳过,而 deny 是规则执行结果所产生的拒绝。
preconditions 为假时,规则甚至不会被评估。必须将它与 deny 的区别整理到文件中才能通过。
创建策略报告和狭窄例外
对 require-labels.yaml,将 good-pod 和 bad-pod 两者都通过 --resource 传入,使用 --policy-report 运行并保存为 /root/policy/validate/out/policy-report.yaml(summary.pass 至少为 1,summary.fail 至少为 1)。然后在 /root/policy/validate/exception.yaml 中创建 kind: PolicyException,将 spec.exceptions[0].policyName 设为 require-labels、ruleNames 设为 check-required-labels,并将 spec.match.any[0].resources.names 像 legacy-batch-* 一样缩小到名称级别。
要让一条通过记录和一条失败记录同时出现在一个报告摘要中,必须同时加入两个资源。请将例外缩小到策略名称、规则名称和资源名称。