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

KCA — Kyverno認定アソシエイト

ポリシーはデプロイするものではなく昇格させるものだ

TT Labで続きを見る

一言でいうと

ポリシー運用の核心となる手順は1つです。Auditで数日間観察し、引っかかる一覧が予想と一致していることを確認してからEnforceに上げます。そして、その判断を人の記憶ではなく、PolicyReportとCIゲートに任せます。

なぜ必要なのか

開発クラスターに最初からEnforceでポリシーを適用する失敗パターンは、いつも同じ姿をしています。署名パイプラインやラベルのルールがまだすべてのチームに行き渡っていない状態で強制モードを有効にすると、デプロイが丸ごと止まり、結局ポリシーではなく、ポリシーを作った人がボトルネックになります。そのあとに起きることのほうが、もっと悪いものです。急いでいるので例外を広く開け、その例外が恒久化されます。

逆に、すべてをAuditのままにしておくと、誰もレポートを見ません。そこで必要になるのが、プロモーションの手順です。何を見て、どんな条件が満たされたら、どのルールを上げるのかを、あらかじめ決めておくのです。

どう動くのか

観察のための道具が、PolicyReportとClusterPolicyReportです。ネームスペース単位のレポートには、results配列とsummary(pass/fail/warn/error/skipの件数)が入っています。失敗だけを抜き出して見る習慣が重要で、ポリシー名とメッセージを一緒に見てはじめて、どのルールが何のために引っかかったのかを判断できます。

差をつけて適用する方法は2つあります。ルール単位のfailureActionは「どのルールを」を決め、validationFailureActionOverridesは「どのネームスペースで」を決めます。開発ネームスペースはAuditのままにして、本番環境だけをEnforceに上げる構成が代表的で、2つの軸を組み合わせれば、ルールごとにもネームスペースごとにも違う運用ができます。

正当な例外は、PolicyExceptionで管理します。ポリシー名とルール名を指定し、matchで対象を絞ります。ここでの核心は、狭く書くことです。ネームスペースを1つ丸ごと除外する代わりに名前のパターンまで書く必要があり、例外のリストが実際に動いている対象の半分を超えると、そのポリシーはセキュリティを与えるのではなく、セキュリティがあるという錯覚を与えます。

ポリシーレベルのフラグ3つも知っておく必要があります。backgroundは、既存のリソースを走査してレポートを作る動作のオン・オフで、デフォルト値はtrueです。admissionは、アドミッション段階でルールを適用するかどうかで、デフォルトはtrueであり、falseにするとbackground専用のポリシーになります。applyRulesは、マッチしたリソースにルールをいくつ適用するかで、Oneなら最初のマッチで止まり、Allがデフォルト値です。ルールを順番に並べたのに後ろのルールがなぜ動かないのかを探しているなら、このフィールドから確認します。

CIゲートは、kyverno applyで作ります。ポリシーファイルと、--resourceで検査するマニフェストを渡すと、ローカルで突き合わせて実行され、失敗やエラーがあれば終了コードが1になるので、そのままCIジョブに引っかけられます。この習慣1つで防げる事故は大きいものです。matchブロックを間違えて書いて何にもマッチしないポリシーを作ってしまい、通過したと勘違いする、という事故です。何にもマッチしないポリシーは、クラスターでは黙って通過させるだけなので、人の目では、よく動いているポリシーと区別がつきません。

そして、繰り返し言っておくべき原則が1つあります。ワイルドカードですべてのリソースにマッチするポリシーは、クラスターのすべてのリクエストに遅延の負担を課します。デフォルトのfailurePolicyがFailなので、タイムアウトを超えた瞬間にリクエストが拒否されます。マッチを必要な種類とネームスペースに絞る作業は、パフォーマンスチューニングではなく、可用性の作業です。

最後に、いつ使わないのかです。検査したいのがフィールド1つの値で、それ以上何も必要ないなら、Kubernetes組み込みのValidatingAdmissionPolicyとCELで十分です。コンポーネントを1つ減らして運用するというのは、アップグレードの対象が1つ減り、障害時に疑う場所が1つ減り、Webhook証明書の更新を気にする必要が1つ減るという意味です。さらに、Podのセキュリティレベルのように標準化された検査なら、ポリシーエンジンなしで、ネームスペースのラベル2行(Pod Security Admission)で済みます。Kyvernoを正当化するのは、generateとmutate、そしてイメージ検証のように、組み込みの機能にはできない仕事です。

現場での姿

著者がイメージスキャンのレポートを扱いながら立てたルールは、ポリシー運用にもそのまま当てはまります。例外には必ず有効期限を入れる、というものです。スキャンの例外ファイルには理由と有効期限を一緒に書き、期限が過ぎるとスキャナーがもう一度失敗させます。例外が静かに恒久化するのを防ぐ唯一の方法が、これでした。PolicyExceptionには有効期限のフィールドがないので、同じ効果を出すには人が管理しなければなりません。例外オブジェクトのアノテーションに有効期限と理由を書き、定期的に見直す手順を入れておくほうが適切です。

もう1つ、スキャンで身につけた優先順位の感覚が、ここでも通用します。レポートに1,247件あっても、今日対処するものは、たいてい一桁です。修正版があるものだけを残し、実際に悪用が確認されたものを先に見ます。ポリシーレポートも同じです。failが数百件あっても、すべて今日直す対象ではなく、ルールごとにまとめて見て、どのルールが最も多くの失敗を生んでいるかから確認します。そのルール1つが、たいていは間違って書かれたルールか、組織がまだ準備できていないルールです。

著者のホームラボは、7ノードにCilium eBPF、ArgoCD、Harbor、Giteaが載った構成で、この程度の規模でも、ポリシーを全面的にEnforceにすると、KubeVirtやGPU Operatorのようなシステムコンポーネントが先に引っかかります。そのため、excludeでシステムネームスペースを除外することは、選択ではなく標準になります。

次のラボですること

/root/kca-ops/に、ネームスペースごとに差をつけた適用とwebhookConfiguration、ポリシーフラグを含むポリシーファイル、PolicyExceptionファイル、そしてプロモーションのチェックリストを、実行可能なシェルスクリプトとして書きます。そのあと、PSAラベルだけでPodのセキュリティレベルを強制するネームスペースを実際に作成し、ポリシーエンジンなしでどこまでできるのかを手で確認します。