---
title: "GPUStack 脆弱性 CVE-2026-102895：確認手順と2.3.0への更新"
url: "https://www.issoh.co.jp/tech/details/19559/"
published: 2026-11-10
updated: 2026-11-10
categories: ["セキュリティ"]
publisher: "株式会社一創"
---

# GPUStack 脆弱性 CVE-2026-102895：確認手順と2.3.0への更新

GPU上でLLMの推論サーバーを管理するOSS、GPUStackの認証回避CVE-2026-102895が、2026年11月9日にCISAの悪用確認済み脆弱性カタログ（KEV）へ追加されました。2.2.0より前の版では、認証情報を持たない外部からの要求が組み込みの管理者として扱われることがあります。この記事では、管理者になってしまう2つの経路と原因コード、手元の版と露出を調べるコマンド、Dockerで2.3.0へ上げる手順、すぐ更新できない環境の暫定策を順に整理します。本記事の記述は11月10日時点で公開されている情報にもとづくものです。

## まとめ：GPUStackは2.2.0で止めず2.3.0へ、更新前に露出と侵害の確認

CVE-2026-102895そのものは2.2.0で直っています。ただ、2.2系には別の認証回避と認可の欠陥が残り、2.3.0で多数が修正されました。これから更新するなら、11月10日時点の最新安定版である2.3.0を選んでください。

更新より先にやることもあります。2.1系の管理画面やAPIをインターネットへ出していた環境は、すでに侵入されている前提で、ユーザー・APIキー・推論バックエンドを洗い出します。米国連邦機関向けのKEVの是正期限は11月12日で、民間でも同じ速さで片づける案件です。手が回らない場合は、[脆弱性診断・セキュリティ診断](https://www.issoh.co.jp/service/system/vulnerability/)で公開範囲と設定の確認を受け付けています。

## CVE-2026-102895で未認証の要求が管理者になる2つの経路と原因コード

[GitHubのアドバイザリGHSA-gj9m-f8ph-pmhr](https://github.com/gpustack/gpustack/security/advisories/GHSA-gj9m-f8ph-pmhr)は、原因を「送信元のネットワーク情報を認証の根拠にしたこと」と説明しています。仕組みはコードを見ると短く理解できます。

### ループバック判定で管理者を割り当てていたauth.pyの3行と修正差分

[修正コミット53f42a6](https://github.com/gpustack/gpustack/commit/53f42a6a553542974396d16a4ff6b12bcbedb48d)は、`gpustack/api/auth.py`から次の3行を消しただけの変更です。トークンでユーザーが決まらず、送信元が127.0.0.1と判定され、`force_auth_localhost`が偽なら、最初に見つかった管理者ユーザーを割り当てていました。

```
# 2.1系の get_current_user（修正で削除された部分）
if user is None and client_ip_getter(request=request) == "127.0.0.1":
    if not server_config.force_auth_localhost:
        user = await User.first_by_field(session, "is_admin", True)
```

`force_auth_localhost`の既定値は`False`でした。つまり初期設定のままなら、送信元さえ127.0.0.1に見えれば認証なしで管理者になります。

### 同一ホストのリバースプロキシでTCPピアが127.0.0.1になる構成

1つ目の経路は、細工を必要としません。nginxなどを同じホストに置き、`127.0.0.1`のGPUStackへ転送している構成では、GPUStackから見た接続元が常にプロキシ自身、つまり127.0.0.1になります。外部の誰がアクセスしても管理者扱いです。

TLS終端のためにプロキシを前段へ置くのはよくある構成です。プロキシの役割と置き方は[リバースプロキシの仕組みと導入判断](https://www.issoh.co.jp/tech/details/13554/)で解説しています。今回は、その定番構成がそのまま穴になった点が厄介です。

### 埋め込みゲートウェイがX-GPUStack-Real-IPの値を信じる条件

2つ目の経路はヘッダーの偽装です。2.1.1の`client_ip_getter`は、ゲートウェイを埋め込み（embedded）で動かしているとき、要求に付いた`X-GPUStack-Real-IP`ヘッダーの値をそのまま送信元として返していました。

```
# 2.1.1 の client_ip_getter
def client_ip_getter(request: Request) -> str:
    if request.app.state.server_config.gateway_mode == GatewayModeEnum.embedded:
        return request.headers.get("X-GPUStack-Real-IP", "")
    else:
        return request.client.host
```

攻撃者がこのヘッダーに127.0.0.1を入れれば、どこから接続しても条件を満たします。2.3.0の同じ関数は、ゲートウェイのトークンを確かめた要求に限ってプロキシ由来のヘッダーを読む形に変わっています。

## 影響版・修正版とCISA KEV追加で11月12日が是正期限となった経緯

修正自体は早く出ていました。修正コミットは2026年5月19日、[2.2.0のリリース](https://github.com/gpustack/gpustack/releases/tag/v2.2.0)は6月18日、GHSAの公開は7月31日です。一方で[NVDへの登録](https://nvd.nist.gov/vuln/detail/cve-2026-102895)は11月4日でした。NVDのデータに頼る脆弱性スキャナでは、11月まで検出されなかった可能性があります。

### 2.2.0未満が対象で2.1.1環境のリバースシェル悪用が確認済み

影響を受けるのはPyPIの`gpustack`パッケージ2.2.0未満で、Dockerイメージも同じ版で判断します。CVSS 3.1の基本値は9.8で、ネットワーク越しに認証も利用者の操作も要りません。

NVDの記載では、2.1.1の環境で実際に悪用されています。管理者として`POST /v2/inference-backends/from-yaml`を呼び、リバースシェルを仕込んだ独自の推論バックエンドを登録する手口です。ワーカーやクラウド事業者の秘密情報の読み出し、永続的な管理者APIキーの作成も影響として挙がっています。[CISAのKEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)は11月9日に本件を追加し、是正期限を11月12日としました。期限の決まり方は[BOD 26-04の是正期限の仕組み](https://www.issoh.co.jp/tech/details/18233/)で整理しています。

### 2.2系に残るSAML認証回避など2.3.0までに直った別のアドバイザリ

2.2.0へ上げれば本件は塞がります。それでも2.2.0を勧めないのは、同じ時期に次の欠陥が公表されているためです。

| 識別子               | 内容                        | 影響版            | 修正版   |
| ----------------- | ------------------------- | -------------- | ----- |
| CVE-2026-102895   | ループバック判定による認証回避（9.8）      | 2.2.0未満        | 2.2.0 |
| CVE-2026-102894   | SAMLコールバックの署名未検証（9.1）     | 0.7.1以上2.2.2未満 | 2.2.2 |
| CVE-2026-103002   | Kubernetes API中継認可漏れ（8.8） | 2.2.0以上2.2.2未満 | 2.2.2 |
| GHSA 8件（10月26日公開） | 認証情報ファイル権限・ワーカー管理API認可など  | 2.2.3以前を含む     | 2.3.0 |

[SAMLの欠陥](https://github.com/gpustack/gpustack/security/advisories/GHSA-62m9-f875-rqw6)は、SAMLを設定していない環境でも、既存の管理者を名乗るセッションを発行できたというものです。[Kubernetes APIプロキシの欠陥](https://github.com/gpustack/gpustack/security/advisories/GHSA-3hch-x692-6qj3)は、一般ユーザーがワーカーの権限でクラスタを操作できたというものです。残る8件は[リポジトリのセキュリティ一覧](https://github.com/gpustack/gpustack/security)と[2.3.0のリリースノート](https://github.com/gpustack/gpustack/releases/tag/v2.3.0)に並んでいます。

## 手元のGPUStackで版番号と未認証で管理者になるかを調べるコマンド

調べる項目は2つです。動いている版と、外から認証なしで管理者の情報が返るかどうかです。どちらも読み取りのみで、サービスを止めません。

### gpustack versionとDockerイメージのタグで動いている版を確認

```
# Dockerで動かしている場合（コンテナ名は公式手順の既定値 gpustack）
docker ps --filter name=gpustack
docker exec gpustack gpustack version

# pipで入れた場合
pip show gpustack
```

1行目のIMAGE列に`latest`と出ていても、取得した時期によって中身の版は違います。2行目の`gpustack version`の結果を正としてください。ワーカーを別ホストで動かしている場合は、そちらでも同じ確認をします。

### 認証情報なしのcurlで自分のユーザー情報APIが401を返すかの確認

2.1.1では、`/v2/users/me`は認証済みユーザー向けのAPIです。認証情報を付けずに、利用者が実際に使うURLから呼び出します。自社で管理するサーバー以外には実行しないでください。

```
# 経路1：プロキシ経由の通常のアクセス
curl -s -o /dev/null -w "%{http_code}\n" https://gpustack.example.com/v2/users/me

# 経路2：送信元ヘッダーを付けたアクセス
curl -s -o /dev/null -w "%{http_code}\n" -H "X-GPUStack-Real-IP: 127.0.0.1" https://gpustack.example.com/v2/users/me
```

どちらも401なら、少なくともこの2経路での認証回避は成立していません。200が返った場合は回避が成立しており、すでに誰かが同じことをした可能性があります。その環境は後述の侵害確認へ進んでください。

## Docker環境をバックアップ後に2.3.0へ更新しサーバーとワーカーを戻す手順

[公式のDocker更新手順](https://github.com/gpustack/gpustack/blob/v2.3.0/docs/upgrade/docker.md)は、v2.0以降を対象に「イメージを取得し、同じ引数とボリュームでコンテナを作り直す」流れを示しています。順番はサーバーが先、ワーカーが後です。

### 埋め込みPostgreSQLのデータを退避してからサーバーを入れ替える順番

公式手順は、更新前にサーバーを止めてデータベースを退避するよう強く勧めています。既定構成では`/var/lib/gpustack/postgresql/data`が埋め込みPostgreSQLのデータです。次の例は、公式の起動例どおり`gpustack-data`ボリュームを使っている前提です。

```
docker stop gpustack

# ボリューム全体をtarで退避（Docker一般の方法）
docker run --rm -v gpustack-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/gpustack-data-20261110.tgz -C /data .

docker pull gpustack/gpustack:v2.3.0
docker rm gpustack
docker run -d --name gpustack --restart unless-stopped \
  -p 80:80 --volume gpustack-data:/var/lib/gpustack \
  gpustack/gpustack:v2.3.0

docker exec gpustack gpustack version
```

起動引数は、元のコンテナと同じものを必ず付け直します。11月9日には2.3.1rc1も出ていますが、公式手順はrc版への更新を避けるよう明記しています。安定版の2.3.0に固定してください。

### ワーカーの更新とforce\_auth\_localhostが無効になった後の設定整理

サーバーが新しい版で動き出したら、各クラスタのワーカーを更新します。2.2.0でサーバーとワーカーの版の不一致を検知する仕組みが入ったため、ワーカーを古いまま残さないでください。

[2.3.0のCLIリファレンス](https://github.com/gpustack/gpustack/blob/v2.3.0/docs/cli-reference/start.md)では、`--force-auth-localhost`は「互換のために残した何もしない設定」です。ローカルホストからの要求も常に認証されます。設定ファイルに残っていても起動は止まりませんが、将来の版で削除予定なので消しておきます。リバースプロキシの後ろで動かす場合は、同じ文書にある`--trusted-hosts`で信頼するホスト名を指定してください。未指定のまま`--server-external-url`も無いと、全ホストを信頼する`*`になります。

## すぐ更新できない2.1系で入れる暫定策とリバースプロキシの設定例

アドバイザリが示す暫定策は3つです。ローカルホストにも認証を要求する設定、プロキシでのヘッダー除去、APIを信頼できないネットワークから切り離すことです。どれも更新までのつなぎで、更新の代わりにはなりません。

### force\_auth\_localhostをtrueにして起動し直す設定と環境変数

2.1系のCLIリファレンスには、多くの起動引数を`GPUSTACK_`接頭辞の大文字の環境変数でも指定できると書かれています。Dockerなら次のように付けて作り直します。

```
docker run -d --name gpustack --restart unless-stopped \
  -e GPUSTACK_FORCE_AUTH_LOCALHOST=true \
  -p 80:80 --volume gpustack-data:/var/lib/gpustack \
  gpustack/gpustack:v2.1.1
```

設定ファイルを使っているなら`force_auth_localhost: true`を書き足します。反映できたかは、前章のcurlが2本とも401になるかで判定します。同一ホストのプロキシ構成では、この設定が経路1を塞ぐ唯一の手段です。ヘッダー除去だけでは足りません。

### nginxで偽装ヘッダーを転送前に消すproxy\_set\_headerの書き方

経路2に対しては、外部から届いた`X-GPUStack-Real-IP`をエッジで捨てます。[nginxの公式文書](https://nginx.org/en/docs/http/ngx%5Fhttp%5Fproxy%5Fmodule.html#proxy%5Fset%5Fheader)によると、`proxy_set_header`の値を空文字列にしたヘッダーは転送先へ渡されません。

```
# GPUStackを -p 127.0.0.1:8080:80 で公開し、nginxを前段に置く例
location / {
    proxy_set_header X-GPUStack-Real-IP "";
    proxy_set_header Host $host;
    proxy_pass http://127.0.0.1:8080;
}
```

この設定だけでは、プロキシ経由の要求がGPUStackから127.0.0.1に見える状態は変わりません。前節の`force_auth_localhost`と必ず組み合わせてください。管理画面を社内網やVPNの内側へ移せるなら、それが最も確実な切り離しです。

## 判断：侵害を疑うべきGPUStack環境と更新だけで済ませてよい条件

更新だけで終えてよいのは、2.1系の管理画面とAPIが社内網やVPNの内側にしかなく、前章のcurlを外部から打てなかった環境です。それ以外は、更新と並行して侵害の確認を行います。

### 管理画面を外部公開していた2.1系は更新前に侵害の痕跡を調べる判断

同一ホストのプロキシで外部公開していた2.1系は、curlの結果に関係なく、外部の誰もが管理者だった期間があったと見なします。悪用の手口が公開済みで、KEVにも載ったからです。痕跡を消される前に確認したいので、更新の前にコンテナのログと管理画面の状態を控えておきます。

見送ってよい対応もはっきりさせます。WAFで`X-GPUStack-Real-IP`を含む要求だけを止める対策は、経路1を塞げないので採用しません。同じGPU基盤の露出が狙われた例としては、Rayクラスタを狙ったShadowRay 2.0があり、関連記事で扱っています。

### 推論バックエンド・APIキー・クラウド認証情報の棚卸しと失効の順番

確認は、攻撃者が残しそうなものから順に行います。

1. 推論バックエンドの一覧に、身に覚えのない独自バックエンドやYAML登録が無いかを見る
2. ユーザー一覧に、作った覚えのない管理者が無いかを見る
3. APIキーを全件失効させ、必要なものだけ発行し直す
4. GPUStackに登録したクラウド事業者の認証情報とワーカーのトークンを、発行元で無効化して入れ替える
5. ワーカーホストで不審なプロセスと外向きの通信を調べる

3と4は、痕跡が見つからなくても実施します。アクセスの記録が残っていない以上、読み出されていないとは言えないからです。キーの失効と入れ替えの設計は[APIキー管理の発行・失効・ローテーション](https://www.issoh.co.jp/tech/details/16300/)が参考になります。推論の入出力に個人情報が含まれていた場合は、[個人情報保護委員会への報告義務](https://www.issoh.co.jp/column/details/17840/)の該当性も確認してください。

## よくある質問

GPUStackの脆弱性CVE-2026-102895について、運用の現場で迷いやすい点に答えます。

### 2.2.0に上げていれば対応は完了ですか？

CVE-2026-102895に限れば2.2.0で修正されています。ただ、SAMLコールバックの署名未検証（CVE-2026-102894）とKubernetes APIプロキシの認可漏れ（CVE-2026-103002）は2.2.2まで残り、ワーカー管理APIの認可や認証情報ファイルの権限など8件は2.3.0で直りました。11月10日時点では2.3.0への更新を完了の基準にしてください。

### リバースプロキシを使っていなくても影響はありますか？

あります。ゲートウェイを埋め込みで動かしている2.1系では、`X-GPUStack-Real-IP`ヘッダーに127.0.0.1を入れるだけで条件を満たします。プロキシの有無ではなく、管理画面とAPIに外部から届くかどうかで判断してください。curlの2本目が401を返すかで確かめられます。

### KEVの是正期限11月12日は民間企業にも適用されますか？

KEVの是正期限は、BOD 26-04にもとづき米国の連邦政府機関に課されるものです。日本の民間企業に法的な拘束力はありません。それでも、KEVへの追加は実際の攻撃が確認されたことを意味します。インターネットから到達できるGPUStackは、同じ期限を目安に更新か切り離しを終えてください。

### ワーカーだけ古い版のままでも大丈夫ですか？

CVE-2026-102895の判定処理はサーバー側のコードです。ただし2.3.0で直った欠陥には、ワーカー登録や認証情報ファイルの権限に関わるものも含まれます。公式手順もサーバーの後にワーカーを更新する順番を示しているので、両方を2.3.0にそろえてください。

### 悪用されたかどうかをログで判定できますか？

アドバイザリとNVDに、侵害を判定する具体的な方法は書かれていません。確認の手がかりは、身に覚えのない推論バックエンドや管理者ユーザー、APIキーの有無です。プロキシのアクセスログに`X-GPUStack-Real-IP`が記録されていれば参考になりますが、既定のログ形式では残らないことが多いため、無いことを安全の根拠にはできません。

## 関連記事

- [vLLMとは？PagedAttentionの仕組み・使い方とOllama・TensorRT-LLMとの違い・0.32.0の脆弱性対応](https://www.issoh.co.jp/tech/details/15786/)：GPUStackが裏で起動する推論エンジンの仕組みと脆弱性対応です。
- [ShadowRay 2.0とは？Rayクラスタを狙う自己増殖ボットネットと露出面の閉じ方](https://www.issoh.co.jp/tech/details/16905/)：GPU基盤の管理面が狙われた別の事例と、露出の閉じ方を扱います。
- [API不正アクセス対策の実装手順：JPCERT注意喚起に沿うレート制限・認可・トークン失効とアプリ解析・SSRF対策](https://www.issoh.co.jp/tech/details/18346/)：送信元ではなくトークンで認可する設計の基本をまとめています。

---

出典: [GPUStack 脆弱性 CVE-2026-102895：確認手順と2.3.0への更新](<https://www.issoh.co.jp/tech/details/19559/>)（株式会社一創）
