👮

Kyverno と OPA/Gatekeeper の技術選定をした話

2023/08/26に公開

概要/選定背景

DMM.com で SRE をしているえるもです。

自分が所属しているチームでは 120 人規模の開発組織にマイクロサービスプラットフォームを提供しています。
マイクロサービスプラットフォームは k8s とその周辺のエコシステムにて構築されているアプリケーションプラットフォームで、弊チームが開発・運用を行い、プラットフォーム利用者はアプリケーション開発に集中できるようになっています。

しかし、現状のマイクロサービスプラットフォームは、利用者が「プラットフォームに影響を与えるような悪意のある k8s マニフェストを作成し、デプロイできてしまう」という状態だったため、ポリシーエンジンを導入し、悪意のある行動に対して制限をかけられるようにしようという話になりました。

ポリシーエンジンとしては以下の 2 つが候補に上がりました。

  • kyvenro
  • OPA/Gatekeeper

今回の記事ではこの 2 つのツールの紹介したのち、比較した結果や選定理由を紹介できればと思います。

先に結論を述べておくと、弊チームでは kyverno を採用しました。

ポリシーエンジンとは?

ポリシーエンジンは Admission Controller と呼ばれるポリシーエンジン専用のカスタムコントローラを用いて、k8s API リクエスト時に webhook を介入させて、デプロイできる k8s リソースを制御する仕組みのことを指します。

kyverno や OPA/Gatekeeper ではポリシーエンジンを制御するために、制御内容を記載したポリシーを CRD として事前にデプロイする必要があります。

その後、k8s リソースをクラスタ上にデプロイした際に、ポリシーの内容に一致するかを確認し、ポリシーに記載されたアクションを実行するといった挙動を行います。

ポリシーエンジンに求める要件

チームメンバーと要件を擦り合わせた際に、以下の機能を満たしているポリシーエンジンを採用したいという話になりました。

  1. ポリシーの定義がシンプルにできること
  2. マニフェストにデプロイに制限をかけることができるポリシーを記載できること
  3. ポリシーエンジンを運用する上で管理するものが極力少なること
  4. ポリシーをデプロイする前に Github Actions 等の CI ツールで以下のテストや動作確認ができること
    • ポリシー自体の作成・変更時のテスト
    • k8s マニフェストの作成・変更時にポリシーに引っ掛かっているかを確認できること

Kyverno

概要

https://kyverno.io/docs/introduction/

kyverno は k8s ネイティブに作成されたポリシーエンジンで CNCF の incubation プロジェクトです。
kyverno のポリシーは ClusterPolicy という CRD を用いて定義することができます。
このポリシーは yaml で定義することができ、CRD を事前にデプロイしておくで、k8s リソースの制御を行います。

適応できるポリシーの種類

前提として、ClusterPolicy を定義する際には、matchexcludeといったフィールドを用いて、ポリシーを適応/除外するリソースや Namespace 等を選択することができます。
その後、適応したいポリシーの種類と内容を記述する形になります。
また kyverno では以下の 5 つの種類のポリシーを定義できます。

  • Validate
  • Mutate
  • Generate
  • Image Verify
  • Clean Up

今回のこの中でもよく使いそうな Validate と Mutate の機能について説明できればと思います。

Validate

k8s リソースがデプロイされる前に、そのリソースが定義したポリシーに準拠しているかを検証することができます。
Validate の中でも 2 つのルールを適応できます。

Mutate

Mutate Rule はデプロイされる前もしくはされた後のリソースに対して、RFC 6902 JSON Patch か Patch Strategic Merge を用いて、任意の変更を加えるためのルールセットです。

以下の例では、特定のレジストリから Image を取得する際に指定した ImagePullSecret をマニフェストに追記するポリシーを記載しています。

https://kyverno.io/policies/other/a/add-imagepullsecrets/add-imagepullsecrets/

アーキテクチャ

kyverno のアーキテクチャは以下の図のようになります。


kyvernoのアーキテクチャ図(公式ドキュメント記載)

kyverno を導入する際は、以下のコントローラ群の中から選択してデプロイする形になります。
適応するポリシーの種類ごとに必要なコントローラ群が異なり、運用する上で必要なコントローラーのみをデプロイすることができます。

コントローラ 必須 説明
Admission Controller Required Validate/Mutate Ruleを使う際に必要なコントローラ。APIにWebhookを介入させる役割も持つ。
Background Controller Optional Generate Ruleを使う際に必要なコントローラ
CleanUp Controller Optional Cleanup Ruleを使う際に必要なコントローラ
Reports Controller Optional Policy Reportsという定義したポリシーの引っ掛かっているかを確認することができる機能を管理するコントローラ

テスト/動作確認

kyverno は kyverno CLI を使うことで、要件で述べた以下の 2 種類のテストを CI ツール上で行うことができます。

  1. ポリシー自体の作成・変更時のテスト
  2. k8s マニフェストの作成・変更時にポリシーに引っ掛かっているかを確認できること

kyverno のドキュメントには、Github Actions でのワークフローと実行例を記載しています。

https://kyverno.io/docs/testing-policies/

それぞれについて具体的にどのように行うのかを説明します。

ポリシー自体の作成・変更時のテスト

kyverno testコマンドを実行することで実現することができます。

以下の URL に記載されている形で事前にテストケースを記載しておき、そのテストファイルをコマンド実行時に指定し、テストを行います。

https://kyverno.io/docs/kyverno-cli/#examples

実行コマンドは以下のような形になります。

kyverno test /path/to/policy_test.yaml

テストファイルには、テストしたいポリシーと動作確認を行うために k8s マニフェスト、期待する結果(pass/fail)を指定する形になります。

また、以下のコマンドを使用することで、既存のクラスタ上にあるリソースに対して、ポリシーを適応した際にポリシーに引っ掛かっているものがあるかを確認することもできます。

kyverno apply /path/to/policy.yaml --cluster

k8s マニフェストの作成・変更時にポリシーに引っ掛かっているかを確認できること

k8s マニフェスト作成・変更時の動作確認は、先ほど述べたkyverno applyコマンドを実行することが実現できます。
具体的には以下のようになります。

kyverno apply /path/to/policy.yaml --resource /path/to/resource.yaml

ポリシーを指定する箇所はディレクトリでも問題なく、その場合はディレクトリ内に存在するすべてのポリシーに引っ掛かっているかのテストを行うことができます。
(マニフェストもディレクトリで指定可能)

OPA/Gatekeeper

概要

https://www.openpolicyagent.org/docs/latest/

Gatekeeper は CNCF の graduated project である Open Policy Agent(以下、OPA)をベースとしたポリシーエンジンです。
kyverno とは違い、ポリシーは OPA を使用するために必要な Rego 言語で記載することになります。

Rego 言語自体は、k8s だけでなく、ざまざまなプラットフォームで汎用的に利用することができます。Gatekeeper は、Rego 言語を利用した k8s 特化のポリシーエンジンとなっています。

マニフェストの定義方法

Gatekeeper にてポリシーを扱うためには、以下の 2 種類の CRD を定義する必要があります。

  • ConstraintTemplate : 使用されるパラメータやポリシーのテンプレートを定義する。
  • Constraint : 定義した ConstraintTemplate を元に作成する実際のポリシー。
    ConstraintTemplate で定義したポリシーを反映させたい k8s リソースや NS を指定する。

これらの具体的な定義方法を説明します。

ConstraintTemplate

まず、ConstraintTemplate では、k8s リソースの特定フィールドを評価するポリシーを Rego 言語で記述します。このマニフェストは Constraint にて、再利用することが可能です。

以下は、Privileged コンテナのデプロイを禁止するポリシー例です。

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: BlockPrivilegedContainer
spec:
  crd:
    spec:
      names:
        kind: BlockPrivilegedContainer
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8spspprivileged

        violation[{"msg": msg, "details": {}}] {
            c := input_containers[_]
            c.securityContext.privileged
            msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
        }
        input_containers[c] {
            c := input.review.object.spec.containers[_]
        }
        input_containers[c] {
            c := input.review.object.spec.initContainers[_]
        }

Constraint

先ほど記述した ConstraintTemplate を元に、Constraint を作成します。
Constraint は ConstraintTemplate の metadata.name を kind に指定すろ必要があります。
ConstraintTemplate で定義したポリシーをどのリソースや Namespace に適応させるかや ConstraintTemplate に渡すパラメータ等を指定することができます。

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
  name: block-privileged-container
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]

適応できるポリシーの種類

Validate

kyverno の Validate と同様に、k8s リソースがデプロイされる前に、そのリソースが定義したポリシーに準拠しているかを検証することができます。
Validate Rule を使うには、先ほど紹介した ConstraintTemplate と Constraint を使用する必要があり、ConstraintTemplate を事前にクラスタ上にデプロイしておき、その後 Constraint を適応することで、ポリシーによる制御を実現する形になります。
ポリシーの内容は Rego 言語で柔軟に定義することができます。

Mutate

Mutate Rule を使うには、Rego 言語ではなく、yaml を使う必要があります。
Validate Rule とは別の以下の 4 つの CRD を定義して、Mutate を扱う形になります。

  1. AssignMetadata: リソースのメタデータ・セクションへの変更を定義
  2. Assign: メタデータ・セクション以外の変更
  3. ModifySet: コンテナの引数など、リストにエントリの追加・削除が可能
  4. AssignImage: イメージ文字列の構成要素に対する変更を定義

アーキテクチャ


Gatekeeperのアーキテクチャ図(公式ドキュメント記載)

以下のドキュメントでも述べているように、Gatekeeper を扱う上で必要なカスタムコントローラーは、6 つの機能群で構成されます。これらの機能をモノリシックに 1 つの Pod で管理することも可能ですし、分割して管理することも可能そうです。
https://open-policy-agent.github.io/gatekeeper/website/docs/operations/
ただ、どのように分割するのかは実際に触ってみないと分かりにくそうではありました。

テスト/動作確認

Gatekeeper は gator CLI を使うことで、kyveno と同様に要件で述べた以下の 2 種類のテストを CI ツール上で行うことができます。

https://open-policy-agent.github.io/gatekeeper/website/docs/gator/

gator CLI はブログ投稿時点で beta 版です。

ポリシー作成・変更時のテスト

テストを行うには、Suite という CRD を定義し、テストケースを記載する必要があります。
Suite には、ConstraintTemplate と Constraint、テスト対象のマニフェスト、期待する結果を記載します。

kind: Suite
apiVersion: test.gatekeeper.sh/v1alpha1
tests:
  - name: block-privileged-container
    template: template.yaml
    constraint: constraint.yaml
    cases:
      - name: block-privileged-container
        object: "./tests/privileged-container.yaml"
        assertions:
          - violations: no

作成した Suite のテストファイルを指定することで、ポリシー作成・更新時のテストを実現することができます。

gator verify suite.yaml

作成・変更した k8s マニフェストを指定し、ポリシーに引っ掛かっているかのテスト

k8s マニフェスト作成・変更時の動作確認は、以下のコマンド例で実現することができます。

cat my-manifest.yaml | gator test --filename=template-and-constraints/

ポリシーを指定する箇所はディレクトリでも問題なく、その場合はディレクトリ内に存在するすべてのポリシーに引っ掛かっているかのテストを行うことができできます。

ポリシーエンジン比較表

これまでの紹介した内容とそれ以外の項目も含めて、kyvernoとGatekeeperの比較表を載せておきます。

項目 kyverno Gatekeeper
採用言語 yaml Rego言語
定義できるポリシー - Validate
- Mutate
- Generate
- CleanUp
- Image Verify
- Validate
- Mutate
マニフェストの記載方法 ClusterPolicyにて、適応したいリソースやNamespaceやポリシーの内容を記載する。 ConstraintTemplateを使い、ポリシーの内容を記載する。
その後、Constraintを定義し、ポリシーを適応したいリソースを定義する。
ポリシーの適用 ClusterPolicyをデプロイするだけでできる ConstraintTemplateをデプロイしておく必要がある。
その後、Constraintをデプロイを行い、ポリシーを適応することができる。
CLIの機能 kyverno CLIを使用して、動作確認を行う。
gator CLIを使用して動作確認を行う。gator CLIは現時点でbeta版。
kyvernoと比較すると、クラスタ上に既にあるリソースに影響があるかを確認できない。
High Availability HA用のマニフェストが用意されている
https://kyverno.io/docs/high-availability/
サポートしてはいるが、HA modeみたいなオプションはない
https://open-policy-agent.github.io/gatekeeper/website/docs/operations
モニタリング OpenTelemetry経由でメトリクスを取得できる
https://kyverno.io/docs/monitoring/#disabling-metrics
Gatekeeeper Integrationが存在する
https://docs.datadoghq.com/ja/integrations/gatekeeper/
PlayGroundの有無 あり
https://playground.kyverno.io/
なし(OPAには存在する)
リリース頻度 月に1、2回
https://github.com/kyverno/kyverno/releases
月に1、2回
https://github.com/open-policy-agent/gatekeeper/releases

選定結果と理由

上記の調査結果から弊チームでは kyverno を採用したほうが良いとなりました。
kyverno や Gatekeeper のどちらを採用してもポリシーエンジンに求める要件は両者満たしていましたが、Gatekeeper は以下の点で運用上で考慮する点が増えそうであると判断しました。

  • Rego 言語に慣れるのに時間がかかりそう。宣言型の言語であれば、yaml の方が馴染みやすそう。
  • Rego 言語はポリシーを定義するプログラミング言語なので、コーディング方法や関数に分ける粒度が人によってばらつきが出てきそう。運用する上で考慮する箇所が増えそう。
    • package の命名規則
    • 変数名や関数名を sneak case/camel case のどちらかを採用するか
  • ポリシーの定義を ConstraintTemplate と Constraint に分けるため、ポリシーをクラスタ上に適応するためのフローが複雑になる
  • gator CLI が beta 版なので少し不安がある。

kyverno を採用するメリットとしては、上記の懸念点を克服できるだけでなく、以下の点が挙げられます。

  • kyverrno も Gatekeeper も k8s API にリクエストに Webhook を介入させる関係上、カスタムコントローラが落ちたらクラスタ操作や k8s API を叩くオペレータ群に影響が出る可能性がある。
    なので、HA は担保しておきたいが、kyverno のほうが管理しやすそう。
  • kyverno CLI が gator CLI よりも高機能でできることが多い。
  • kyverno の方が Gatekeepor のよりもポリシーの種類が豊富である。kyverno を選択することで今後の選択肢を増やすことができる。
  • Playground が存在する。ポリシーのテストも kyverno CLI があればできるが、コミットするまで問題なく動作するかを確認できないのは地味に辛い。Playground を使えば、動作確認は幾分か行いやすい。

まとめ

今回の記事では kyverno と Gatekeeper の 2 つのツールの紹介したのち、比較した結果や選定理由を紹介しました。弊チームでは、k8s 上のリソースのデプロイに制限をかけたいとなったことから、ポリシーエンジン選定をおこない、kyverno を採用する運びになりました。

ただ、Gatekeeper で使用されている OPA は汎用的なポリシーエンジンであり、k8s 以外にもポリシーを適応したいというユースケースが出てきた場合、Gatekeeper を採用するとポリシーの記載を Rego 言語で統一できるメリットがあります。(Rego 言語から konstraint というツールを用いて、Gatekeeper で使用すると ConstraintTemplate を生成することも可能です。)

この記事を読んで、ユースケースに適切なポリシーエンジン選定の参考になれば幸いです。

Discussion