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

CKS — Kubernetesセキュリティスペシャリスト

CELで書くアドミッションポリシー

TT Labで続きを見る

目標

外部のWebhookサーバーなしで、APIサーバーの中でCELによって動作するValidatingAdmissionPolicyを自分で作成し、バインディングで適用範囲を絞ったうえで、従来のValidatingWebhookConfigurationオブジェクトも併せて書き、2つの方式の違いを手で確認します。

なぜ重要なのか

RBACが答えるのは「Podを作成できるか」までです。そのPodがprivilegedかどうか、許可されたレジストリのイメージかどうかは、RBACでは表現できません。このギャップを埋めるのが、アドミッションポリシーです。

従来の方法は、ValidatingWebhookConfigurationで外部サーバーを登録することですが、ここには可用性の落とし穴があります。failurePolicy: Failで登録されたWebhookのサーバーが落ちると、対象リソースの作成がすべて拒否され、デプロイが丸ごと止まります。ポリシーを守ろうとして、クラスターを止めてしまうのです。ValidatingAdmissionPolicyは、APIサーバーの中でCEL式を評価するため、ネットワークのホップと外部プロセスがなくなり、この障害モード自体が減ります。

ポリシーとバインディングが分離されているのも、意図された設計です。ポリシーは「何が違反か」だけを定義し、バインディングが「どこに、どれだけ強く適用するか」を決めます。同じポリシーを、ステージングではWarnで、本番ではDenyで有効にできます。

ステップ

  1. ネームスペースcks-admissionを作成し、ラベルcks-policy=enforceを付けてください。
  2. ValidatingAdmissionPolicycks-no-latest-tagを作成してください。spec.matchConstraints.resourceRules[0]は、apiGroupsがapps、apiVersionsがv1、operationsがCREATEとUPDATE、resourcesがdeploymentsです。(APIサーバーは、validationsもauditAnnotationsもないポリシーを拒否します。このステップでは、expression: "true"のような仮の検証式を1つ入れておき、ステップ3で本物の式に置き換えます。)
  3. 同じポリシーにspec.validations[0]を入れてください。expressionは、Deploymentのすべてのコンテナイメージが:latestで終わっていないかを検査するCELで(object.spec.template.spec.containersとallが含まれている必要があります)、messageは空であってはいけません。
  4. 同じポリシーのspec.failurePolicyをFailに設定してください。
  5. ValidatingAdmissionPolicyBindingcks-no-latest-tag-bindingを作成してください。policyNameはcks-no-latest-tag、validationActionsはDeny、matchResources.namespaceSelector.matchLabelsはcks-policy: enforceです。
  6. ValidatingAdmissionPolicycks-no-privilegedを作成してください。matchConstraintsはコアグループ(apiGroupsは空文字列)、apiVersionsはv1、operationsはCREATE・UPDATE、resourcesはpodsで、validations[0].expressionにはprivilegedという単語とobject.spec.containersが含まれている必要があり、messageも必要です。
  7. ValidatingWebhookConfigurationcks-image-verifyを作成してください。Webhookを1つ(nameはverify.images.cks.local)置き、failurePolicyはIgnore、sideEffectsはNone、admissionReviewVersionsはv1、clientConfig.serviceはネームスペースcks-admissionのServiceimage-verifier、rulesはコアグループv1のpodsに対するCREATEにします。
  8. cks-admissionにDeploymentcheckoutを作成してください。replicasは2、セレクターとPodラベルはapp=checkoutとし、Podテンプレートにラベルapp.kubernetes.io/name=checkoutも入れます。コンテナ名はapp、イメージは@sha256:で固定して:latestは使わず、コンテナのsecurityContextにprivileged: falseとallowPrivilegeEscalation: falseを明示します。

参考

ポリシーを適用するネームスペース

ネームスペースcks-admissionを作成し、ラベルcks-policy=enforceを付けてください。

バインディングのnamespaceSelectorが、このラベルを見て対象を選びます。ラベルの名前と値を正確に合わせてください。

ValidatingAdmissionPolicyの骨組み

ValidatingAdmissionPolicycks-no-latest-tagを作成してください。spec.matchConstraints.resourceRules[0]は、apiGroupsがapps、apiVersionsがv1、operationsがCREATEとUPDATE、resourcesがdeploymentsです。(APIサーバーは、validationsもauditAnnotationsもないポリシーを拒否します。このステップでは、expression: "true"のような仮の検証式を1つ入れておき、ステップ3で本物の式に置き換えます。)

matchConstraints.resourceRulesは、アドミッションWebhookのrulesと同じ形です。apiGroups・apiVersions・operations・resourcesの4つをすべて埋める必要があります。また、APIサーバーはvalidationsもauditAnnotationsもないポリシーは作成そのものを拒否するため、このステップでは仮の検証式を1つ入れておき、次のステップで実際の式に置き換えてください。

CEL式で検証ルールを書く

同じポリシーにspec.validations[0]を入れてください。expressionは、Deploymentのすべてのコンテナイメージが:latestで終わっていないかを検査するCELで(object.spec.template.spec.containersとallが含まれている必要があります)、messageは空であってはいけません。

objectは検査対象のリソースです。Deploymentなら、コンテナはobject.spec.template.spec.containersにあり、CELのall()マクロですべてを検査できます。

failurePolicyを決める

同じポリシーのspec.failurePolicyをFailに設定してください。

式の評価が失敗したときにどうするかのポリシーです。セキュリティポリシーなら、開けておく側ではなく、塞ぐ側です。

バインディングでポリシーを有効にする

ValidatingAdmissionPolicyBindingcks-no-latest-tag-bindingを作成してください。policyNameはcks-no-latest-tag、validationActionsはDeny、matchResources.namespaceSelector.matchLabelsはcks-policy: enforceです。

ポリシーは、バインディングがなければ何もしません。validationActionsに何を入れるかが、拒否なのか警告なのかを分けます。

privileged禁止ポリシーを追加する

ValidatingAdmissionPolicycks-no-privilegedを作成してください。matchConstraintsはコアグループ(apiGroupsは空文字列)、apiVersionsはv1、operationsはCREATE・UPDATE、resourcesはpodsで、validations[0].expressionにはprivilegedという単語とobject.spec.containersが含まれている必要があり、messageも必要です。

対象がPodなので、コンテナのパスがDeploymentとは違います。securityContextがないコンテナもあるので、CELで存在するかどうかを先に確認してください。

ValidatingWebhookConfigurationを書く

ValidatingWebhookConfigurationcks-image-verifyを作成してください。Webhookを1つ(nameはverify.images.cks.local)置き、failurePolicyはIgnore、sideEffectsはNone、admissionReviewVersionsはv1、clientConfig.serviceはネームスペースcks-admissionのServiceimage-verifier、rulesはコアグループv1のpodsに対するCREATEにします。

sideEffectsとadmissionReviewVersionsは必須フィールドなので、抜けると作成が拒否されます。また、Webhookサーバーがない状態なので、失敗ポリシーを誤って選ぶとクラスターが止まります。

総合: ポリシーを通過するデプロイ

cks-admissionにDeploymentcheckoutを作成してください。replicasは2、セレクターとPodラベルはapp=checkoutとし、Podテンプレートにラベルapp.kubernetes.io/name=checkoutも入れます。コンテナ名はapp、イメージは@sha256:で固定して:latestは使わず、コンテナのsecurityContextにprivileged: falseとallowPrivilegeEscalation: falseを明示します。

先に作成した2つのポリシーを、どちらも満たす必要があります。タグではなくダイジェストを使い、コンテナのsecurityContextで権限を下げておいてください。