戻る

2026/11/10

Kyverno CVE-2026-124440:ポリシーエンジンがクラスター管理者権限への抜け道になるとき

Kyverno 1.19.1では、1つのnamespaceでポリシー作成を許可されたテナントがクラスター管理者権限に到達できます。回避の仕組みと、アップグレード前に確認すべき点を解説します。

Kubernetesのテナントを分離するためにKyvernoを運用しているチームは多くあります。Kyvernoは、APIサーバーがオブジェクトを保存する前にすべてのオブジェクトをポリシーと照合します。また、namespaceの所有者が自分のnamespace向けのポリシーを自分で書けるようにすることもできます。2026年11月8日に公開されたCVE-2026-124440は、この権限委譲が破綻しうることを示しています。Kyverno 1.19.1では、自分のnamespace内でのポリシー作成しか許可されていないテナントが、Kyvernoにそのnamespaceの外で処理を実行させ、段階的にクラスター管理者権限に到達できます。

修正は10月20日からKyverno 1.19.2で提供されています。このリリースを適用していないクラスターは、現在も影響を受けます。本記事では、何が問題だったのか、権限昇格がどのように成立するのか、そして自社のクラスターで何を確認すべきかを説明します。

要点

  • CVE-2026-124440(GitHubアドバイザリGHSA-39w8-hv5g-f73w)はKyverno 1.19.1に影響し、1.19.2で修正されています。CVEレコードでの評価は、CVSS 4.0で9.4、CVSS 3.1で9.9です。
  • 攻撃者に必要なのは、自分のnamespace内でnamespacedなKyverno Policyを作成する権限だけです。
  • ポリシーのapiCallはKyvernoのadmission-controller用ServiceAccountとして実行されます。このServiceAccountは他のnamespaceを読み取ることができ、クラスター全体に及ぶアドミッションWebhookを作成できます。この経路がクラスター管理者権限につながります。
  • アドバイザリには回避策が記載されていません。1.19.2にアップグレードしたうえで、ポリシーを作成できるのは誰か、どのWebhookが存在するかを見直してください。
  • 今回確認した情報源では、実環境での悪用は報告されていません。

namespace clampが本来果たすべき役割

Kyvernoのポリシーは、リクエストを評価する際にKubernetes APIからデータを取得できます。この機能はapiCallと呼ばれます。ルールでurlPathを指定すると、KyvernoがAPIサーバーを呼び出し、その結果がルールに渡されます。この呼び出しは、ポリシーを書いた人の権限ではなく、Kyverno自身のServiceAccountで実行されます。

プラットフォーム管理者が書くクラスター全体のClusterPolicyであれば、これは想定どおりの動作です。しかし、テナントに作成を許可できるnamespacedなPolicyにとってはリスクになります。過去のアドバイザリ(GHSA-c5qq-7g2q-cpqpおよびGHSA-5qq8-67g6-4h2w)では、namespacedなPolicyがapiCallを使って他のnamespaceを読み取ったり、クラスタースコープのオブジェクトを書き込んだりできることが示されました。2026年9月10日にリリースされたKyverno 1.19.1では「namespace clamp」が追加されました。呼び出しを実行する前に、パスがポリシー自身のnamespace内にとどまっているかをKyvernoが確認する仕組みです。

チェックと実際の動作が食い違う箇所

1.19.1のclampは、実際に送信する文字列をチェックしていませんでした。アドバイザリによると、clampはurlPathのパーセントエンコーディングをデコードし、その結果を正規化し(..セグメントを解決し)、整形後のコピーでnamespaceを確認していました。一方、リクエスト自体は元の未加工のパスのまま送信されていました。

これはデコードが成功する限りは問題なく機能します。しかし、末尾のクエリ文字列に単独の%があるなど、パスに不正なパーセントエスケープが含まれているとデコードは失敗します。その場合、1.19.1のコードは未加工でエンコードされたままの文字列をチェックする処理にフォールバックしていました。APIサーバーにはそのようなフォールバックはありません。パスを完全にデコードしてリクエストをルーティングします。その結果、clampにはテナント自身のnamespaceが見えている一方で、APIサーバーにはそこから抜け出すエンコード済みの..セグメントが見え、リクエストは別のnamespaceやクラスタースコープのコレクションに到達します。

アドバイザリは、これが単一のペイロードではなく、ある種類の入力全体に関わる問題であることを強調しています。特定のリテラル文字列をブロックしても塞ぐことはできません。根本的な修正は、検証するパスと実行するパスをまったく同じものにすることです。

1.19.2が行っているのはまさにそれです。当社が1.19.2のソースを読んだ限りでは、Kyvernoはパスの正規形を1つだけ生成し、正規化できないパスは拒否し、その正規形でnamespaceを確認したうえで、同じものをAPIサーバーに送信するようになっています。同じリリースで修正された関連する問題であるCVE-2026-122310(GHSA-3xfv-vq8x-qhg4)は、../セグメントを#や?の後ろに隠すことで、整形後のチェックと未加工のリクエストとの同じずれを悪用していました。こちらは独自のレコードを持つ別のCVEです。

権限昇格の流れ(ステップごと)

報告者は、Kubernetes 1.31上に標準のHelmチャートとデフォルト値でKyverno 1.19.1をインストールした環境で、この一連の攻撃を実証しました。

  1. namespacedなPolicyを作成する。テナントが持つ唯一の権限は、自分のnamespace内でpolicies.kyverno.ioを作成することです。ポリシーには、clampを通過しつつ別の場所にルーティングされるパスを持つapiCallを含めます。
  2. ポリシーを発火させる。テナントのnamespace内のConfigMapなど、ポリシーに一致するごく普通のオブジェクトを作成すると、Kyvernoはルールを評価し、admission-controller用ServiceAccountとして呼び出しを実行します。
  3. namespaceをまたいで読み取る。同じ手法で他のnamespaceのオブジェクトを読み取れます。Kyvernoのデプロイメントに読み取りが許可されていれば、Secretsも対象になります。
  4. クラスター全体に及ぶWebhookを書き込む。このServiceAccountはMutatingWebhookConfigurationとValidatingWebhookConfigurationを作成できるため、テナントは独自のWebhookを登録できます。このWebhookは、すべてのnamespaceでアドミッションリクエストを傍受して書き換えます。
  5. クラスター管理者権限に到達する。アドバイザリでは、このようなWebhookを使ってkube-systemで作成されるPodを改変し、そこからテナントにcluster-admin権限を付与する方法が説明されています。その結果、機密性・完全性・可用性がすべて失われます。

ユーザーの操作は不要で、Webhookは通常のクラスターの動作によって発火します。

影響を受ける環境

CVEレコードでは、Kyverno 1.19.1が影響あり、1.19.2が影響なしとされています。1.19.1より前のバージョンにはclamp自体が存在しないため、この回避手法の影響は受けません。ただし、clampが修正しようとした過去のアドバイザリの影響を受けるため、古いリリースにとどまることは安全な代替策にはなりません。リスクが最も高いのは、namespaceの所有者、CIパイプライン、セルフサービスポータルにKyverno Policyの作成が許可されている環境です。

Kyverno 1.19.2は2026年10月20日にリリースされ、そのリリースノートには24件のGitHubセキュリティアドバイザリ(重大9件、高14件、中1件)が記載されています。そのうちいくつかは、APIパスの検証とnamespaceへの閉じ込めに関するものです。本アドバイザリは3日後の10月23日に公開されましたが、アドバイザリとCVEレコードのいずれも修正済みバージョンとして1.19.2を挙げています。アドバイザリはそれぞれ固有の識別子を持つため、まとめて1つのCVEとして扱わず、1件ずつ照合してください。

自社のクラスターで確認すべきこと

まず、Gitリポジトリ上のバージョンではなく、実際に稼働しているKyvernoのバージョンを確認します。

kubectl get deployments -n kyverno -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.containers[*].image}{"\n"}{end}'

イメージタグにv1.19.1が含まれていれば、すぐに1.19.2へのアップグレードを計画してください。そのうえで、次の4点を確認します。

  • namespacedなPolicyを作成できるのは誰か。テナントのIDで直接テストし、policies.kyverno.ioを付与しているロールを探します。組み込みのadminロールやeditロールに権限を追加する集約ClusterRoleも含めて確認してください。
  • apiCallを使用している既存のポリシー。一覧を出し、すべてのurlPathを確認します。特に%、..、?、#を含むパスに注意してください。
  • Webhookの設定。一覧を想定どおりの内容と比較します。Kyvernoは自身のWebhookを作成するため、見覚えのない名前や最近の作成日時に注目してください。
  • 監査ログ。Kyvernoのadmission-controller用ServiceAccountからのリクエストのうち、通常のパターンから外れてWebhook設定を作成したりSecretsを読み取ったりしているものを探します。
kubectl auth can-i create policies.kyverno.io -n team-a --as=system:serviceaccount:team-a:default
kubectl get policies.kyverno.io -A -o json | jq -r '.items[] | select([.spec.rules[]?.context[]? | select(.apiCall)] | length > 0) | "\(.metadata.namespace)/\(.metadata.name)"'
kubectl get mutatingwebhookconfigurations,validatingwebhookconfigurations -o custom-columns=NAME:.metadata.name,CREATED:.metadata.creationTimestamp

team-aは、自社のテナントのnamespaceとIDに置き換えてください。1.19.1を稼働させていたクラスターで説明のつかないWebhookが見つかった場合は、インシデントの可能性があるものとして扱い、何かを削除する前に監査ログを保全してください。

より広い教訓:ルールを強制する側も監査する

アドミッションコントローラー、ポリシーエンジン、オペレーターは、クラスター内で最も強い権限を持つコンポーネントであることが少なくありません。役割を果たすために広い権限を必要とし、テナントも日常的にこれらとやり取りします。そのため、攻撃者にとって魅力的な標的になります。今回のケースでは、テナント分離のモデルが1つのチェックに依存しており、チェックとリクエストでデコード処理が1か所異なるだけで、それが破られてしまいました。

プライベートクラウドやマルチテナントのKubernetesプラットフォームでは、次の3つの習慣が役立ちます。ポリシーエンジンを攻撃対象領域の一部として扱い、そのセキュリティリリースをKubernetesのバージョンと同じくらい注意深く追跡すること。ポリシーが必要としない限りSecretの読み取り権限を付与しないなど、ServiceAccountには必要な権限だけを与えること。そして、テナントが権限委譲を通じてどの機能に到達できるかを見直すこと。プラットフォームの権限で動作する機能は、テナントの入力をプラットフォームの操作に変えてしまうからです。

参考情報