必須ラベルとレジストリ制限のポリシーを書く
目標
必須ラベルを要求する検証ポリシーと、レジストリを制限する拒否ルールを自分で書き、通過・失敗の判定とポリシーレポートをローカルで確認します。最後に、正当な例外を狭い範囲で表現します。
なぜ重要なのか
検証ポリシーで事故を起こすのは、条件式ではなくマッチ範囲です。条件が間違っていればテストで引っかかりますが、マッチが間違っていると、ポリシーが黙って何もしないため、誰も気づきません。そのため、通過すべきリソースとブロックされるべきリソースの両方を入れてみることが、ポリシー作成の基本です。もう1つ、拒否メッセージはポリシーの半分です。デプロイをブロックされた人が見られるのは、kubectl applyが出力した1行だけで、その1行が不親切だと、ポリシーの運用コストがすべて問い合わせとして返ってきます。最後に、この環境にはポリシーエンジンのコントローラーが起動していないため、クラスターは誤った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の2つのラベルを、それぞれ"?*"で要求してください。同じ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に、2つ目の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を付けてください。要約の行だけを保存すると、ポリシー名が抜けます。 - 失敗が1つでもあると、
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の2つのラベルを、それぞれ"?*"で要求してください。同じ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に、2つ目の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-*のように名前単位で絞ってください。
通過1件と失敗1件が、1つのレポートの要約に一緒に入るには、リソースを両方入れる必要があります。例外は、ポリシー名・ルール名・リソース名まで絞ってください。