Rego不要!KyvernoによるKubernetes Policy as Code実践ガイド

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

クイックスタート:5分でKyvernoをデプロイする

6か月前、私たちのプラットフォームチームは、約600のマイクロサービスが稼働する14の本番クラスタ全体にアドミッションコントロールを導入する必要に迫られていました。エンジニアたちはすでにHelm、Kustomize、Terraformの運用に追われており、Open Policy Agent(OPA)のRego DSLの習得を求めることは強い反発を招きました。そこで私たちが採用したのが、ポリシーを標準的なKubernetesマニフェストとして扱えるオープンソースのポリシーエンジン「Kyverno」です。

Helmを使用すれば、Kyvernoは2分足らずでデプロイできます。

# 公式のKyverno Helmリポジトリを追加
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update

# 高可用性アドミッションコントローラーをインストール
helm install kyverno kyverno/kyverno \
  --namespace kyverno \
  --create-namespace \
  --set admissionController.replicas=3

3つのコントローラーPodがすべてRunningになったら、最初のセキュリティルールを適用できます。以下は、rootコンテナをブロックするクラスタ全体(cluster-wide)のポリシーです。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-run-as-non-root
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: check-run-as-non-root
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "ルートコンテナは禁止されています。securityContext.runAsNonRoot を true に設定してください。"
      pattern:
        spec:
          securityContext:
            runAsNonRoot: true

このマニフェストを require-non-root.yaml として保存し、kubectl apply -f require-non-root.yaml で適用します。該当フィールドが設定されていないDeploymentは、APIサーバーのアドミッションフェーズでKyvernoによって即座に拒否されます。

アーキテクチャ:なぜYAMLネイティブなポリシーが優れているのか

GatekeeperはKubernetesリソースを任意のJSONドキュメントとして扱い、Regoクエリでパースします。一方Kyvernoは異なるアプローチを取り、標準的なワークロードスキーマに準拠したKubernetesカスタムリソース定義(CRD)としてポリシーをモデル化します。

アドミッションWebhookの仕組み

Kyvernoは動的Webhookを介してkube-apiserverのアドミッションライフサイクルに直接フックします。

  • Mutating Webhook(変更Webhook):スキーマ検証の前にデフォルト値を挿入します。必須ラベルの付与、サイドカープロキシの注入、安全なセキュリティコンテキストの自動設定などを行えます。
  • Validating Webhook(検証Webhook):受信したペイロードをアクティブなルールと照合して評価します。準拠しているオブジェクトを承認するか、開発者が対処しやすいエラーメッセージを直接返します。
  • Background Controller(バックグラウンドコントローラー):既存のクラスタ状態を新しく適用されたルールと照合して60分ごとにスキャンします。PolicyReportオブジェクトを生成するため、稼働中のワークロードを壊すことなくセキュリティチームが設定のドリフトを監査できます。

4つのコアなルール種別

Kyvernoは4つの異なる運用シナリオに対応しています。

  1. Validate(検証):オーバーレイパターンまたはCommon Expression Language(CEL)の式を使用してリソースのフィールドを検証します。
  2. Mutate(変更):Strategic Merge PatchまたはRFC 6902 JSONパッチを使用してオブジェクトを変更します。
  3. Generate(生成):トリガーとなるリソースが作成された際に、デフォルトのNetworkPolicyやResourceQuotaマニフェストなどの関連リソースを複製または新規作成します。
  4. Verify Images(イメージ検証):コンテナランタイムがイメージをプルする前に、信頼された公開鍵を用いてCosignおよびSigstoreのサプライチェーン署名を検証します。

高度な自動化:変更(Mutation)とリソース生成(Generation)

不備のあるマニフェストをブロックするのは対策の半分にすぎません。優れたプラットフォームエンジニアリングでは、軽微な設定ミスを自動修正し、開発者が摩擦なくデプロイできるようにします。

安全なデフォルト値の自動注入

開発者にすべてのLinuxケーパビリティフラグを覚えさせる必要はありません。変更ルール(Mutation Rule)を使用して、危険な権限を自動的に削除します。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: mutate-pod-security-defaults
spec:
  rules:
  - name: drop-all-capabilities
    match:
      any:
      - resources:
          kinds:
          - Pod
    mutate:
      patchStrategicMerge:
        spec:
          containers:
          - (name): "*"
            securityContext:
              capabilities:
                drop:
                - ALL

マルチテナント分離の自動化

エンジニアが新しいNamespaceを作成するたびに、クラスタは自動的に分離バリアをプロビジョニングします。KyvernoはカスタムGoオペレーターを必要とせず、デフォルト拒否(default-deny)のNetworkPolicyを自動生成します。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: auto-generate-namespace-security
spec:
  rules:
  - name: generate-default-deny-networkpolicy
    match:
      any:
      - resources:
          kinds:
          - Namespace
    generate:
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      name: default-deny-ingress
      namespace: "{{request.object.metadata.name}}"
      synchronize: true
      data:
        spec:
          podSelector: {}
          policyTypes:
          - Ingress

synchronize: trueが有効になっているため、Kyvernoは自己修復コントローラーとして機能します。生成されたこのNetworkPolicyが誰かに削除または編集された場合でも、Kyvernoは数秒以内にベースラインの状態へと復元します。

本番運用から得た4つの貴重な教訓

稼働中のクラスタへのアドミッションコントローラーの導入は、慎重に行わなければ大規模な障害を引き起こす可能性があります。私たちが14のクラスタに45のポリシーを安全に展開した方法は以下の通りです。

1. 最初の14日間はAuditモードで運用する

初日からいきなりEnforceモードでポリシーを適用してはいけません。必ず最初はvalidationFailureAction: Auditに設定してください。

# バックグラウンドスキャナーによって検出された違反を確認
kubectl get clusterpolicyreport -o wide
kubectl describe clusterpolicyreport cpolr-4bf9a

Auditモードにしておくことで、Kyvernoが本番のCI/CDパイプラインをブロックする前に、プロダクトチームがマニフェストを修正するための2スプリント分の猶予を確保できます。

2. システム用のNamespaceを保護する

kube-systemにおけるポリシーエラーは、ノードの初期化、CNIプラグイン、CoreDNSなどを停止させてしまう恐れがあります。重要なNamespaceは必ず明示的にポリシーから除外してください。

spec:
  rules:
  - name: require-labels
    match:
      any:
      - resources:
          kinds:
          - Pod
    exclude:
      any:
      - resources:
          namespaces:
          - kube-system
          - kyverno
          - monitoring
          - ingress-nginx

3. Kyverno CLIでシフトレフトを実現する

クラスタにデプロイする段階になって初めてポリシー違反を検知されるのは、エンジニアにとってストレスです。スタンドアロンCLIを使用して、ポリシー評価をpre-commitフックやGitHub Actionsへ前倒し(シフトレフト)しましょう。

# ローカルのマニフェストに対してポリシーを実行
kyverno apply ./policies/ --resource ./deployments/service.yaml

私たちのCIパイプラインでは、このチェックは400ミリ秒未満で完了し、コードがmainブランチにマージされる前にポリシー違反の95%を捕捉できています。

4. Webhookのオーバーヘッドを綿密に監視する

アドミッションリクエストが発生するたびに、APIサーバーとの間でネットワークのラウンドトリップが追加されます。私たちのクラスタでは、リクエストごとのベースラインのアドミッションレイテンシは15ミリ秒未満に収まっています。Prometheusでkyverno_admission_review_duration_secondsに対するアラートを設定しておきましょう。p99レイテンシが200ミリ秒を超えて急上昇した場合は、最適化されていないワイルドカードのパターンマッチがないか確認し、アドミッションのレプリカ数をスケールさせてください。

Share: