validate規則と組み込みポリシーを比べる
目標
Kyverno ClusterPolicyでラベル必須とタグ禁止のルールを作成し、同じ検査をKubernetes組み込みのValidatingAdmissionPolicyでも実際のクラスターに載せて、2つのアプローチの違いを確認します。
なぜ重要なのか
ポリシーエンジンを導入するかどうかを判断するには、組み込み機能の射程を知っておく必要があります。フィールド1つをCELで検査するだけならValidatingAdmissionPolicyで十分で、そうすれば運用するコンポーネントが1つ減り、Webhook証明書の更新を気にする場面も1つ減ります。Kyvernoを正当化するのは、generateやmutate、イメージ検証のように、組み込みポリシーにできないことです。このラボで2つのマニフェストを並べて作ってみると、その境界がどこにあるかが手に残ります。もう1つ、ルールごとに強制レベルを変える練習は、実際の導入手順の核心です。
ステップ
/root/kca-validate/ディレクトリを作成し、policy.yamlにapiVersionとしてkyverno.io/v1、kindとしてClusterPolicy、metadata.nameとしてkca-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は使わないでください。- 2つ目のルール
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は使わないでください。
ポリシーレベルのフィールドはdeprecatedなので、残しておいてはいけません。強制レベルはvalidateブロックの中に下りてきており、指定しなければデフォルトは観察モードです。
deny.conditionsで2つ目のルールを追加する
2つ目のルールdeny-latest-tagを追加してください。validate.messageを埋め、validate.deny.conditions.all[0]にコンテナイメージを指すkey、operator、valueを入れたうえで、このルールのvalidate.failureActionはAuditにしてください。
条件はkey、operator、valueの3つの欄です。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を入れてください。
組み込みポリシーは、ポリシーとバインディングの2つのオブジェクトが1組です。バインディングがなければ、ポリシーは存在するだけで、どのリクエストも処理せず、どんな動作をするかもバインディングが決めます。