テナントのオンボーディングを自動化する生成ポリシー
目標
新しいテナントのネームスペースが作られた瞬間に、デフォルトのセキュリティ・リソースのリソースが一緒に生まれるようにする生成ポリシーを書き、そのポリシーが実際のクラスターで動作するために、ほかに何が必要かまで備えます。
なぜ重要なのか
新しいチームにネームスペースを1つ渡す作業は、普通、チェックリストで回っています。NetworkPolicyをかけ、ResourceQuotaをかぶせ、LimitRangeを入れ、レジストリのSecretをコピーします。人がやると、いつかどこかの手順を抜かし、抜かしたのがよりによってデフォルトの遮断ポリシーだった場合、そのネームスペースだけが何か月も開いたままになります。生成ルールは、このチェックリストを、ネームスペースの作成イベントに結び付けてしまいます。ここで、3つのことを学びます。1つ目は、トリガーの範囲をラベルで絞らないと、システムのネームスペースまで対象になることです。2つ目は、Secretのように、内容をポリシーファイルに書いてはいけないものは、cloneで元のものだけを指すことです。ポリシーはgitに入るからです。3つ目は、バックグラウンドコントローラーは最小権限でインストールされるため、作るリソースの種類の権限を別に付ける必要があり、権限がないと、リクエストは成功するのに、リソースだけが黙って作られないことです。この環境では、コントローラーが動いていないため、実際のリソースは作られず、kyverno CLIでローカルに評価した結果と、ポリシーのYAMLの構造で採点します。
ステップ
/root/policy/generate/add-networkpolicy.yamlにkind: ClusterPolicyを作成してください。spec.rules[0].generateに、apiVersion: networking.k8s.io/v1、kind: NetworkPolicy、name: default-denyを書き、generate.data.specに、podSelector: {}とpolicyTypesを置きますが、Ingressを必ず含めてください。- 同じルールの
match.any[0].resources.kindsをNamespaceにして、selector.matchLabelsにlabhub.io/tenant: "true"を書いて、テナントのネームスペースにだけ反応するようにしてください。 generate.synchronizeをtrueにしてください。そして、/root/policy/generate/out/sync-note.txtに、オンにしたときの動作(誰かが手で直すと元に戻す)と、オフにしたときの違い(作るときに一度だけ関与する)の両方を書いてください。/root/policy/generate/clone-registry-secret.yamlを作成してください。generate.kindはSecret、generate.clone.namespaceはpol-lab、generate.clone.nameはregistry-credsです。generate.dataは書いてはいけず、ポリシーファイルの中にSecretの内容が入っていてもいけません。add-networkpolicy.yamlのgenerate.namespaceを"{{ request.object.metadata.name }}"に、generate.nameを明示してください。そして、/root/policy/generate/out/vars-note.txtに、ポリシーで使える変数を整理しますが、request.objectと、リクエスト元の情報を持つ変数(request.operationまたはrequest.userInfo)を必ず含めてください。/root/policy/generate/background-rbac.yamlにkind: ClusterRoleを作成してください。rulesにnetworkpoliciesを含め、verbsにcreateとupdateを入れてください(同期をオンにしたので、更新も必要です)。metadata.labelsに、rbac.kyverno.io/aggregate-to-background-controller: "true"のような集約ラベルを付けてください。kyverno apply /root/policy/generate/add-networkpolicy.yaml --resource /opt/lab/fixtures/policy/resources/target-ns.yamlを実行して、生成されたNetworkPolicyのマニフェストだけを/root/policy/generate/out/generated.txtに保存してください。実行の要約行(pass: ..., error: 0 ...)は入れないでください。/root/policy/generate/tenant-onboarding.yamlに、ルール3つのポリシーを作成してください。各ルールがそれぞれNetworkPolicy・ResourceQuota・LimitRangeを生成し、ルール名は互いに異なる必要があり、3つのルールすべてがgenerate.synchronize: trueである必要があります。そして、/root/policy/generate/out/onboarding-report.jsonに、trigger_namespaceをtenant-alphaとして、generated配列に、生成されるリソースを3件以上、kindキーを含めて入れてください(ResourceQuotaが必ず入っている必要があります)。
参考
- トリガーのフィクスチャは
/opt/lab/fixtures/policy/resources/target-ns.yamlで、名前はtenant-alpha、ラベルはlabhub.io/tenant: "true"です。 - ステップ7の結果ファイルに
errorという文字が入っていると、採点に失敗します。要約行にはerror: 0が含まれるため、生成されたマニフェストだけを残してください。 generate.cloneとgenerate.dataは、どちらか一方しか使えません。- よくあるミス1: ステップ4のポリシーファイルに、Secretの値を真似して書いてしまうことです。
password・tokenのような文字列がファイルにあると、cloneを使う理由がなくなります。 - よくあるミス2: ステップ8で、ルール名をコピーしたまま変えないことです。名前が重複すると、ポリシーが拒否されます。
デフォルトのNetworkPolicyを作るルールを書く
/root/policy/generate/add-networkpolicy.yamlにkind: ClusterPolicyを作成してください。spec.rules[0].generateに、apiVersion: networking.k8s.io/v1、kind: NetworkPolicy、name: default-denyを書き、generate.data.specに、podSelector: {}とpolicyTypesを置きますが、Ingressを必ず含めてください。
生成ルールは、作るリソースのapiVersion・kind・name・namespaceを最初に書きます。内容をポリシーに直接書くキーが、別にあります。
トリガーと対象セレクターを絞る
同じルールのmatch.any[0].resources.kindsをNamespaceにして、selector.matchLabelsにlabhub.io/tenant: "true"を書いて、テナントのネームスペースにだけ反応するようにしてください。
何が作られたときに反応するかを、matchに書きます。すべてのネームスペースに作ると、システムのネームスペースまで対象になるので、ラベルで絞ってください。
同期をオンにして、その意味を整理する
generate.synchronizeをtrueにしてください。そして、/root/policy/generate/out/sync-note.txtに、オンにしたときの動作(誰かが手で直すと元に戻す)と、オフにしたときの違い(作るときに一度だけ関与する)の両方を書いてください。
オンにすると、作った後も見守り続けます。オフにしたときと何が変わるかを、ファイルに書いて、はじめて通過します。
元のものをコピーするcloneルールを書く
/root/policy/generate/clone-registry-secret.yamlを作成してください。generate.kindはSecret、generate.clone.namespaceはpol-lab、generate.clone.nameはregistry-credsです。generate.dataは書いてはいけず、ポリシーファイルの中にSecretの内容が入っていてもいけません。
Secretの内容をポリシーファイルに書くと、ポリシーがそのまま流出経路になります。元のものがどこにあるかだけを伝える方式があり、内容を直接書くキーとは、一緒に使えません。
リクエストコンテキストの変数で対象を決める
add-networkpolicy.yamlのgenerate.namespaceを"{{ request.object.metadata.name }}"に、generate.nameを明示してください。そして、/root/policy/generate/out/vars-note.txtに、ポリシーで使える変数を整理しますが、request.objectと、リクエスト元の情報を持つ変数(request.operationまたはrequest.userInfo)を必ず含めてください。
作られたネームスペースの名前は、リクエストのオブジェクトから取り出します。ポリシーで使える変数を3つ整理して、はじめて通過します。
生成権限のためのClusterRoleを書く
/root/policy/generate/background-rbac.yamlにkind: ClusterRoleを作成してください。rulesにnetworkpoliciesを含め、verbsにcreateとupdateを入れてください(同期をオンにしたので、更新も必要です)。metadata.labelsに、rbac.kyverno.io/aggregate-to-background-controller: "true"のような集約ラベルを付けてください。
バックグラウンドコントローラーは、もともと何の権限も持っていません。同期をオンにしたなら、作ることだけでなく、更新も必要です。権限を集めていく集約ラベルも、忘れないでください。
トリガーのリソースで実行して、生成結果を保存する
kyverno apply /root/policy/generate/add-networkpolicy.yaml --resource /opt/lab/fixtures/policy/resources/target-ns.yamlを実行して、生成されたNetworkPolicyのマニフェストだけを/root/policy/generate/out/generated.txtに保存してください。実行の要約行(pass: ..., error: 0 ...)は入れないでください。
保存するのは、生成されたリソースのマニフェストです。実行の要約行は入れないでください。採点がエラーの文字列を探します。
オンボーディングのポリシーのセットと、結果レポートを作る
/root/policy/generate/tenant-onboarding.yamlに、ルール3つのポリシーを作成してください。各ルールがそれぞれNetworkPolicy・ResourceQuota・LimitRangeを生成し、ルール名は互いに異なる必要があり、3つのルールすべてがgenerate.synchronize: trueである必要があります。そして、/root/policy/generate/out/onboarding-report.jsonに、trigger_namespaceをtenant-alphaとして、generated配列に、生成されるリソースを3件以上、kindキーを含めて入れてください(ResourceQuotaが必ず入っている必要があります)。
ネットワーク・クォータ・デフォルトのリソースの3つを、1つのポリシーの中の別々のルールとして作ります。ルール名が重複してはならず、すべてで同期がオンになっている必要があります。