Kubeflow Pipelinesのテナント間コード実行(CVE-2026-77596)
Kubeflow Pipelines 2.18.0より前は共有パイプラインのアップロードで認可を省略し、低権限のテナントが別の名前空間でコードを実行できます。2.18.0で修正されました。
要点
- 低権限のテナントが、KubernetesのSubjectAccessReviewを受けずに共有パイプラインまたは既存の共有パイプラインの新しいバージョンをアップロードできます。アドバイザリは2026年10月28日に公開されました。
- Kubeflow Pipelines 2.18.0より前が、
REQUIRE_NAMESPACE_FOR_PIPELINESがfalseのマルチユーザーモードで影響を受けます。これは既定値です。最新バージョンに追従する固定されていない定期実行が、注入されたパイプラインを別テナントの名前空間で実行します。 - CVEレコードの評価はCVSS v3.1 9.6 Criticalです。公開実証コードも実際の悪用も報告されておらず、CVEは2026年11月6日時点でCISAのKEVカタログにありません。
- Kubeflow Pipelines 2.18.0以降へ更新してください。すぐに更新できない場合は、パイプライン操作に名前空間を必須とし、APIサーバーの前に認証プロキシを置いてください。詳しくは「修正方法」を参照してください。
影響を受けますか?
次の条件がすべて当てはまる場合にのみ影響を受けます。
- クラスタでKubeflow Pipelines 2.18.0より前を実行している。アドバイザリは2.17.3までのリリースを影響対象としています。
- APIサーバーがマルチユーザーモードで動作している。シングルユーザーのインストールは認可経路を完全に省略するため、この欠陥の影響を受けません。
REQUIRE_NAMESPACE_FOR_PIPELINESがfalseである。既定値はfalseで、同梱のマニフェストにも設定がないため、既定のマルチユーザーインストールは露出しています。- 攻撃者がAPIサーバーへ到達できる。認証プロキシなしで直接到達できる場合、未認証の呼び出し元がバイパスを利用できます。認証プロキシの背後では、呼び出し元に有効な低権限テナントのIDが必要です。
- コード実行の段階では、対象の名前空間に、最新パイプライン版に追従する固定されていない定期実行がある。バージョンを固定した実行、または仕様を埋め込んだ実行は置き換えられません。
APIサーバーはkubeflow名前空間のml-pipelineデプロイです。HTTPアップロードのエンドポイントはPOST /apis/v2beta1/pipelines/uploadとPOST /apis/v2beta1/pipelines/upload_versionで、古いv1beta1のパスも同じサーバーが処理します。
| ブランチ | 影響を受けるバージョン | 修正バージョン |
|---|---|---|
| Kubeflow Pipelines | 2.18.0より前のすべてのリリース(アドバイザリ: 2.17.3まで) | 2.18.0(2026年10月28日公開) |
イメージタグと名前空間の設定を確認します。
kubectl -n kubeflow get deploy ml-pipeline -o jsonpath='{range .spec.template.spec.containers[*]}{.name}{" "}{.image}{"\n"}{end}'
kubectl -n kubeflow get deploy ml-pipeline -o yaml | grep -i REQUIRE_NAMESPACE_FOR_PIPELINES
イメージタグが2.18.0より低く、REQUIRE_NAMESPACE_FOR_PIPELINESの項目がない、またはfalseの場合、露出した既定値に一致します。変数がない場合、サーバーはfalseを使います。
修正方法
- Kubeflow Pipelines 2.18.0以降へ更新します。2.18.0リリースは認可の修正を最初に含むリリースです。インストール方法に合ったマニフェストを適用してください。
- すぐに更新できない場合は、APIサーバーで
REQUIRE_NAMESPACE_FOR_PIPELINES=trueを設定します。検証はアップロードが保護されていない分岐に届く前に空の名前空間を拒否します。これは緩和策であり修正ではありません。呼び出し元は所有していない名前空間を指定でき、その後に続く確認がパッチで直る箇所です。 - APIサーバーの前に認証プロキシを置き、直接公開しないでください。アドバイザリは、直接到達できるサーバーでは未認証の呼び出し元がバイパスを利用できると述べています。すべてのリクエストを認証するプロキシはその経路をなくします。
- 共有パイプラインとそのバージョンについて、見覚えのないコンテナイメージ、コマンド、引数を監査し、最新バージョンに追従する定期実行を確認します。
ベンダーのアドバイザリはコードレベルの回避策を挙げていません。更新が唯一の完全な修正です。
根本原因は何ですか?
HTTPアップロード経路は、対象の名前空間が空のとき認可ヘルパーから成功を返すため、共有パイプラインのアップロードはSubjectAccessReviewに到達しません。2.17.3のbackend/src/apiserver/server/pipeline_upload_server.goのヘルパーは次のとおりです。
func (s *PipelineUploadServer) canUploadVersionedPipeline(r *http.Request, pipelineID string, resourceAttributes *authorizationv1.ResourceAttributes) error {
if !common.IsMultiUserMode() {
// Skip authorization if not multi-user mode.
return nil
}
if len(pipelineID) > 0 {
namespace, err := s.resourceManager.FetchNamespaceFromPipelineId(pipelineID)
if err != nil {
return util.Wrap(err, "Failed to authorize with the Pipeline ID")
}
if len(resourceAttributes.Namespace) == 0 {
resourceAttributes.Namespace = namespace
}
}
if resourceAttributes.Namespace == "" {
return nil
}
resourceAttributes.Group = common.RbacPipelinesGroup
// ... IsAuthorized() runs only after this point
}
空の名前空間は、Kubeflow Pipelinesがクラスタ全体の共有パイプラインを指す方法です。呼び出し元がnamespaceクエリパラメーターを省略したときの既定値でもあります。これを拒否できる検証は、IsNamespaceRequiredForPipelines()がtrueのときだけ実行されます。
func ValidateNamespaceRequired(namespace string) error {
if common.IsMultiUserMode() &&
common.IsNamespaceRequiredForPipelines() &&
namespace == "" {
return util.NewInvalidInputError("Namespace is required for pipeline operations")
}
return nil
}
IsNamespaceRequiredForPipelines()はREQUIRE_NAMESPACE_FOR_PIPELINESを既定値falseで読み、manifests/以下のどのマニフェストもこれを設定していません。同じ書き込みのgRPC経路であるpipeline_server.goのcanAccessPipeline()は逆の動作をします。create、update、deleteでは空の名前空間をシステム名前空間に再マップし、共有パイプラインの書き込みも認可される必要があるというコメントを付けています。したがってHTTPヘルパーは意図的な例外ではなく省略です。
どのように悪用されますか?
- 攻撃者がAPIサーバーへ到達します。認証プロキシがなければアカウントは不要です。プロキシの背後では、低権限のテナントアカウントで足ります。
- 攻撃者が
namespaceパラメーターなしで/apis/v2beta1/pipelines/uploadへ、または既存の共有パイプラインのIDを付けて/apis/v2beta1/pipelines/upload_versionへマルチパートPOSTを送ります。 canUploadVersionedPipeline()は空の名前空間を見てnilを返します。SubjectAccessReviewは送信されません。- アップロードは新しい共有パイプラインを作成するか、既存の共有パイプラインに新しいバージョンを追加します。バージョンは攻撃者のコンテナイメージ、コマンド、引数を運びます。
- 別テナントの名前空間で、最新バージョンに追従する固定されていない定期実行が注入された仕様を取得します。
- 実行は、被害者の名前空間でその実行のサービスアカウントとして攻撃者のコンテナを起動します。そのアカウントのKubernetes権限が攻撃者のコードに適用されます。
リクエストの形は標準のKFPマルチパートアップロードです。公開実証コードはなく、アドバイザリはエンドポイントと欠けている確認を説明しています。
攻撃の流れは?
空の名前空間が認可呼び出しを省略し、共有パイプラインのバージョンが攻撃者のコンテナを別テナントの定期実行へ運びます。
攻撃者に何ができますか?
攻撃者は、別テナントの名前空間で、そのテナントの定期実行のサービスアカウントとして任意のコンテナを実行できます。条件はすべてここに列挙します。
- Kubeflow Pipelines 2.18.0より前である。
- マルチユーザーモードで、
REQUIRE_NAMESPACE_FOR_PIPELINESがfalseまたは未設定である。 - 攻撃者がAPIサーバーへ到達できる。直接、または低権限アカウントを許可する認証プロキシ経由である。
- バージョン注入の経路では既存の共有パイプライン、作成の経路では新しい共有パイプラインを作成する権限がある。
- 被害者の名前空間に、最新バージョンに追従する固定されていない定期実行がある。
影響には、実行のサービスアカウントが読める名前空間のシークレットの読み取り、リソースの変更、ネットワーク位置の利用が含まれます。正確な影響はサービスアカウントのrole binding次第です。バージョンを固定した実行、または仕様を埋め込んだ実行はこの経路では置き換えられません。アドバイザリは、認証プロキシがサーバーを保護している場合の未認証経路を示していません。
修正が機能する理由
修正は空の名前空間をKFPのシステム名前空間にマップし、既存のSubjectAccessReviewに判断を委ねるため、共有パイプラインの書き込みは他の書き込みと同じように認可されます。pipeline_upload_server.goのパッチは次のとおりです。
if len(pipelineID) > 0 {
namespace, err := s.resourceManager.FetchNamespaceFromPipelineId(pipelineID)
if err != nil {
return util.Wrap(err, "Failed to authorize with the Pipeline ID")
}
if len(resourceAttributes.Namespace) == 0 {
resourceAttributes.Namespace = namespace
}
}
- if resourceAttributes.Namespace == "" {
- return nil
+ if s.resourceManager.IsEmptyNamespace(resourceAttributes.Namespace) {
+ // Shared-pipeline writes require authorization in the KFP system namespace.
+ resourceAttributes.Namespace = common.GetPodNamespace()
}
resourceAttributes.Group = common.RbacPipelinesGroup
コードは共有書き込みに対して、APIサーバーが動く名前空間でIsAuthorized()を呼ぶようになりました。その名前空間でパイプラインを作成できない呼び出し元は拒否されます。修正は、共有と名前空間付きのアップロード、バージョンアップロード、許可と拒否のケース、シングルユーザーモードを網羅するrecordingSubjectAccessReviewClientテスト行列も追加するため、空の名前空間の分岐はCIで実行されます。
修正はアドバイザリが挙げる2つのHTTPエンドポイントに対して完全です。RBACの設計は変わりません。共有パイプラインの書き込み権限をすでに持つテナントはそれを保持し、認可の判断はクラスタに属します。
侵害されたかどうかを確認するには?
ベンダーは侵害指標を公開していません。APIサーバーのログと共有パイプラインの一覧を確認します。
# Pipeline upload requests and authorization failures in the API server log.
kubectl -n kubeflow logs deploy/ml-pipeline --since=720h | grep -Ei 'upload|SubjectAccessReview|Authorization Failure'
# Shared pipelines visible through the API.
kubectl -n kubeflow port-forward deploy/ml-pipeline 8888:8888 &
curl -s http://localhost:8888/apis/v2beta1/pipelines | head -c 2000
マルチユーザーインストールでは、名前空間のないパイプラインは共有パイプラインです。すべての共有パイプラインとそのバージョンを確認し、各バージョンのコンテナイメージ、コマンド、引数をチームが期待する内容と比較してください。次に、最新バージョンに追従する定期実行を一覧し、最近の実行履歴にデプロイしていないイメージがないか確認します。Kubernetes監査ログは、監査ポリシーにSubjectAccessReviewが含まれる場合、その呼び出しを記録します。監査レベルがRequest以上なら、レビューの本文に呼び出し元のIDが含まれます。アップロード要求自体は、リクエストのログ記録が有効な場合、ml-pipelineまたはプロキシのアクセスログで探してください。SubjectAccessReviewがないこと自体は記録されません。
アップロードが成功しても、KFPの画面には新しいパイプラインまたはバージョン以外の表示はありません。予期しない共有パイプライン、または自分が公開していない共有パイプラインの新しいバージョンは、調査すべき手がかりとして扱ってください。
年表
- 2026-09-09: 修正がプルリクエスト#14352としてマージされました。
- 2026-10-28: Kubeflow Pipelines 2.18.0がリリースされ、アドバイザリGHSA-rjr3-43j3-4q8gが公開されました。
- 2026-11-05: CVE-2026-77596がCVSS v3.1 9.6で公開されました。
- 2026-11-06: 実際の悪用は報告されておらず、CVEはCISAのKEVカタログにありません。
よくある質問
シングルユーザーのKubeflow Pipelinesは影響を受けますか?
いいえ。マルチユーザーモードが無効のときヘルパーはすぐに戻るため、認可経路と空の名前空間の分岐は使われません。この欠陥はマルチユーザーインストールに該当します。
攻撃者にKubernetesアカウントが必要ですか?
認証プロキシなしでAPIサーバーへ直接到達できる場合は不要です。アドバイザリは、その場合に未認証の呼び出し元がバイパスを利用できると述べています。認証プロキシの背後では、有効な低権限テナントアカウントが必要です。
攻撃者のコードは常に別テナントの名前空間で実行されますか?
いいえ。対象の名前空間に、最新バージョンに追従する固定されていない定期実行がある場合に実行されます。バージョンを固定した実行、または仕様を埋め込んだ実行はこの経路では置き換えられません。
REQUIRE_NAMESPACE_FOR_PIPELINES=trueで十分ですか?
このCVEが示すバイパスである空の名前空間の経路を塞ぎます。これは緩和策であり修正ではなく、パッチが追加する認可確認の代わりにはなりません。可能なときに更新してください。
実際の悪用はありましたか?
2026年11月6日時点で実際の悪用は報告されておらず、CVEはCISAのKEVカタログにありません。アドバイザリは2026年10月28日に公開されたため、報告がない期間はまだ短いです。
実行しているバージョンはどう確認しますか?
ml-pipelineデプロイのイメージタグを確認します。マニフェストはリリースごとにイメージタグを固定しており、2.18.0が最初の修正リリースです。プラットフォームベンダーがKubeflow Pipelinesを同梱している場合は、同梱物に含まれる上流バージョンを確認してください。
参考資料
- Kubeflow PipelinesセキュリティアドバイザリGHSA-rjr3-43j3-4q8g、2026年10月28日公開、影響バージョン、脆弱なコード、修正、影響を含む: https://github.com/kubeflow/pipelines/security/advisories/GHSA-rjr3-43j3-4q8g (2026年11月6日確認)
- CVE-2026-77596レコード、2026年11月5日公開、CVSS v3.1 9.6、影響範囲: https://www.cve.org/CVERecord?id=CVE-2026-77596 (2026年11月6日確認)
- Kubeflow Pipelinesプルリクエスト#14352、修正: https://github.com/kubeflow/pipelines/pull/14352 (2026年11月6日確認)
- Kubeflow Pipelinesコミット62516d8c525f146b754cb515cf441dd0290ad841、「fix(backend): authorize shared pipeline uploads (#14352)」、2026年9月9日: https://github.com/kubeflow/pipelines/commit/62516d8c525f146b754cb515cf441dd0290ad841 (2026年11月6日確認)
- Kubeflow Pipelines 2.18.0リリース、2026年10月28日: https://github.com/kubeflow/pipelines/releases/tag/2.18.0 (2026年11月6日確認)
- Kubeflow Pipelines
backend/src/apiserver/server/pipeline_upload_server.go2.17.3、脆弱なヘルパー: https://github.com/kubeflow/pipelines/blob/2.17.3/backend/src/apiserver/server/pipeline_upload_server.go (2026年11月6日確認) - Kubeflow Pipelines
backend/src/apiserver/server/pipeline_server.go2.17.3、共有書き込みを認可するgRPCのcanAccessPipeline(): https://github.com/kubeflow/pipelines/blob/2.17.3/backend/src/apiserver/server/pipeline_server.go (2026年11月6日確認) - Kubeflow Pipelines
backend/src/apiserver/validation/namespace.go2.17.3、ValidateNamespaceRequired(): https://github.com/kubeflow/pipelines/blob/2.17.3/backend/src/apiserver/validation/namespace.go (2026年11月6日確認) - Kubeflow Pipelines
manifests/kustomize/base/pipeline/ml-pipeline-apiserver-deployment.yaml2.18.0、ml-pipelineデプロイ: https://github.com/kubeflow/pipelines/blob/2.18.0/manifests/kustomize/base/pipeline/ml-pipeline-apiserver-deployment.yaml (2026年11月6日確認) - CISAの悪用済み脆弱性カタログJSONフィード、2026年11月6日にCVE-2026-77596を確認(未掲載): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (2026年11月6日確認)
- GitHub、
kubeflow/pipelines、4,262スターと2,185フォーク: https://github.com/kubeflow/pipelines (2026年11月6日確認)