TT Lab
はじめる
学ぶ 学習パス コース

KCA — Kyverno認定アソシエイト

ラベルのルールが緩められたのに誰も気づかなかった

TT Labで続きを見る

目標

Kyverno CLIのapply・test・jpで、ポリシーが何を拒否し、何を書き換え、何を作るのかを、クラスターに適用する前に判定し、その判定をテストスイートとCIスクリプトで固定します。

なぜ重要なのか

アドミッションポリシーは、間違っても静かです。検証ルールを緩く直してしまったミスは、どのリクエストも止めなくなるだけで、エラーは出ません。mutateルールのアンカー1つで、すでに値のあるリソースに触れるかどうかが変わります。本番クラスターでその違いを見つけたときには、もう手遅れです。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ができたら、そのネームスペースにResourceQuota default-quotaをspec.hard.pods: "10"で生成し、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.01つだけの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式を1つ書いてください。Podオブジェクトを入力として受け取り、イメージがregistry.lab/で始まらないコンテナの名前のリストを返す必要があります。/root/kca-cli/jp/external.yamlにはステップ6のPod externalを1つだけ置き、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の4つのディレクトリでそれぞれkyverno testを実行し、1つでも失敗したら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ができたら、そのネームスペースにResourceQuota default-quotaをspec.hard.pods: "10"で生成し、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.01つだけの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式を1つ書いてください。Podオブジェクトを入力として受け取り、イメージがregistry.lab/で始まらないコンテナの名前のリストを返す必要があります。/root/kca-cli/jp/external.yamlにはステップ6のPod externalを1つだけ置き、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の4つのディレクトリでそれぞれkyverno testを実行し、1つでも失敗したら0以外の値で終了する必要があります。ステップ3で見たように、終了コードが0でも、出力の表に期待と実際が食い違った行(REASONがWant ...のもの)があれば、失敗として扱う必要があります。regressedは実行しません。採点ツールは、コピーしたポリシーとテストをいろいろな方法で壊して、スクリプトが失敗するかを確認します。

出力を変数やファイルに受け取り、終了コードと食い違いの文言の両方を検査します。ループで最初の失敗で終わらせても、すべて実行してからまとめて終わらせても、結果のコードさえ正確であればかまいません。色のコードが混ざると文字列検索が外れるので、--remove-colorを使ってください。