タグアーカイブ Docker

DockerのCopy Fail脆弱性対応とseccomp破壊の教訓

DockerのCopy Fail脆弱性対応とseccomp破壊の教訓

2026年4月末に公開されたLinuxカーネルの脆弱性CVE-2026-31431、通称「Copy Fail」は、2017年以降のほぼ全てのカーネルに影響する深刻な問題だ。Docker社はこの脆弱性に対し、コンテナランタイムレベルでの緩和策を急ピッチで提供した。

その過程で、Docker Engine v29.4.2の修正が32ビットバイナリのネットワーク機能を完全に破壊するという予期せぬ副次的被害を引き起こした。本記事では、この一連の対応と教訓を技術的に深掘りする。

コンテナ運用者は、カーネルパッチの適用が最優先だが、それが叶わない場合でもDocker Engineのアップデートによりリスクを大幅に低減できる。ここで得られた知見は、今後のコンテナセキュリティ対策における多層防御の重要性を浮き彫りにしている。

Copy Fail脆弱性の仕組みとリスク

Copy Fail脆弱性の仕組みとリスク

AF_ALGサブシステムの欠陥

Copy Failは、Linuxカーネルの暗号処理をユーザー空間から利用するためのAF_ALG(Algorithm Sockets)サブシステムに存在する。具体的にはalgif_aeadモジュールの不具合により、特権のないプロセスがページキャッシュに対して不正な書き込みを行える状態になっていた。

ページキャッシュとは、ファイルの読み取りデータをメモリ上に一時保存する仕組みだ。全プロセスが参照するため、ここを汚染されると、システム全体でファイルの内容が改ざんされて見える可能性がある。最も直接的な攻撃経路は、setuidバイナリ(実行時に高い権限で動作するプログラム)の改ざんによる権限昇格である。

この脆弱性の深刻さは、その単純さにある。PoC(概念実証コード)はきわめて簡潔で、カーネルがパッチされていない限り、2017年以降のあらゆるバージョンで動作する。攻撃が成功すると、コンテナ内からホスト全体、そして同じノード上の他コンテナにまで影響が及ぶのだ。

脆弱性の悪用フロー(Before)
攻撃者 AF_ALGソケット作成 ページキャッシュ汚染 setuid改ざん root権限取得
※ デフォルトのコンテナ権限で実行可能。約7年にわたり影響。
緩和策適用後(After)
攻撃者 AF_ALGソケット作成 システムコールブロック
※ 多層防御によりAF_ALGへの経路が遮断される

このデモが示す通り、脆弱なカーネルでは攻撃者に一直線のルートを提供してしまう。Dockerの役割は、コンテナランタイムのレイヤーでこの経路を物理的に塞ぐことだった。

コンテナ環境への影響範囲

Dockerのデフォルトセキュリティプロファイルでは、コンテナからのAF_ALGソケット作成が許可されていた。つまり、攻撃者が何らかの方法でコンテナ内でコードを実行できた場合、この脆弱性を利用してホストのroot権限を奪取できる状態にあった。

さらに悪いことに、ページキャッシュはホスト全体で共有される。攻撃が成功した場合、被害はそのコンテナ内に留まらず、同じDockerイメージのレイヤーを共有する他のすべてのコンテナにも波及する。これは、マルチテナント環境やマイクロサービスを密に配置しているノードでは壊滅的な被害につながりかねない。

Docker Engineの対応と失敗の分析

Docker Engineの対応と失敗の分析

v29.4.2 seccomp修正の試み

Dockerチームは当初、seccomp(Secure Computing Mode / セキュアコンピューティングモード)プロファイルの更新で対応しようとした。seccompは、コンテナが発行できるシステムコールをフィルタリングする仕組みだ。具体的には、socket()システムコールの第一引数を検査し、AF_ALGアドレスファミリが指定された場合に拒否するルールを追加した。

しかし、x86_64 Linuxにはsocketcall()という古い多重化システムコールが存在する。これはsocket()bind()などの複数のソケット操作を一つのシステムコール番号の背後にまとめたものだ。問題は、socketcall()では実際の引数(アドレスファミリを含む)がユーザー空間の配列にパックされ、そのポインタが渡されることだ。seccompのフィルタエンジンであるBPFは、このポインタ先を参照して検査できない。

つまり、seccompだけではsocketcall()経由のAF_ALGを選択的にブロックできない。やむを得ずDockerチームは、socketcall()全体を拒否するという決断を下し、v29.4.2をリリースした。

v29.4.2でのブロック範囲(Bad)
socket(2) AF_ALG拒否 socketcall(2) 全体拒否
※ 32bitバイナリのネットワーク機能が破壊。SteamCMDやWineが動作不能に。
v29.4.3でのブロック範囲(Good)
AppArmor AF_ALG選択拒否 SELinux AF_ALG選択拒否 socketcall 許可(32bit互換性維持)
※ LSMを活用し、通常のネットワーク機能は維持したままAF_ALGだけを遮断。

この比較が示すのは、セキュリティ対策における粒度の重要性だ。全体をブロックすれば安全だが、システムの機能を破壊する。真に効果的な対策は、悪意のある操作だけをピンポイントで無効化することにある。

32bitバイナリ破壊の実態

socketcall()の一律拒否は、思わぬ大規模な副次的被害を引き起こした。32bit版のglibcは、すべてのソケット操作をsocketcall()経由で行う古いバージョンが残っている。Go言語のランタイムも、GOARCH=386でビルドされたバイナリでは無条件にsocketcall()を利用する。さらに、SteamCMDやWineといったレガシー・ゲーミング系のワークロードも、この仕組みに依存している。

これは単なるi386(32bit)の問題ではない。amd64環境であっても、プロセスはint $0x80命令を使うことでia32互換モードに切り替わり、直接socketcall()を呼び出せる。つまり、64bitのコンテナやバイナリを使っていても、この経路を利用される可能性があるのだ。

結果として、v29.4.2へのアップグレード後に多数の32bitアプリケーションがネットワークに接続できなくなるというインシデントが発生した(GitHub Issue: moby/moby#52506)。セキュリティパッチが新たな機能不全を引き起こすという、運用者にとって最も避けたいシナリオが現実となった。

根本原因:seccompの限界

この問題の本質は、seccompがシステムコール境界でのみ動作する点にある。socketcall()は一つのシステムコール番号の背後に多種の操作を隠蔽する。seccompのフィルタは、その中身である配列のポインタ先を解析できない。これが、seccomp単独では対応できない構造的な限界だ。

Dockerのブログ記事の著者は、「seccompはsocket(AF_ALG)をすべてのシステムでブロックするが、socketcall()に対しては盲目だ」と端的に表現している。この「見えない経路」の存在が、多層防御の必要性を強く示す教訓となった。

v29.4.3 LSMベースの恒久対策

v29.4.3 LSMベースの恒久対策

AppArmorとSELinuxによる多層防御

v29.4.3では、より根本的な解決策としてLinuxセキュリティモジュール(LSM)を活用する方針に切り替えた。AppArmorとSELinuxは、カーネル内部のsecurity_socket_create()コールバックに直接フックする。このコールバックは、socket()経由であれsocketcall()経由であれ、カーネルが実際にソケットオブジェクトを生成する瞬間に必ず呼ばれる。システムコールの入り口ではなく、より深いレベルで制御を行うのだ。

具体的な実装として、AppArmorプロファイルには deny network alg, というルールが追加された。これはAF_ALGアドレスファミリだけを対象に拒否する。SELinux環境向けには、すべてのcontainer_domainタイプに対してalg_socketの作成を拒否するCIL(Common Intermediate Language)ポリシーモジュールが提供され、semoduleコマンドでロード可能だ。

対策の全体像と適用優先度

v29.4.3の防御スタックは、以下の3層で構成されている。seccompによる直接のsocket(AF_ALG)ブロックは防御の一層目として維持しつつ、AppArmorまたはSELinuxによってsocketcall()経由の抜け道を塞ぐ。これにより、どちらか一方の防御層が無効化されても、もう一方がカバーする体制を実現した。

ただし、AppArmorやSELinuxはホストの設定に依存するため、LSMが有効化されていない環境ではsocketcall()経路が無防備なままとなる。この点については、依然としてカーネルパッチが唯一の完全な解決策であることに変わりはない。

STEP 1 Linuxディストリビューションのカーネルパッチを適用する
STEP 2 Docker Engineをv29.4.3以上にアップグレードする(再起動不要)
STEP 3 アップグレード不可ならカーネルモジュールをブラックリスト化する
STEP 4 それも不可ならカスタムseccompプロファイルを適用する

このステップを踏むことで、カーネルパッチの提供を待つ間のリスクを段階的に低減できる。最優先はカーネル修正だが、それが叶わない状況でもDocker Engineの更新だけで強固な緩和策となる。

コンテナセキュリティのための実践的教訓

コンテナセキュリティのための実践的教訓

ランタイム更新のスピードが生む防御力

Copy Failのケースで特筆すべきは、脆弱性の詳細が公表された時点で、主要ディストリビューションの多くはカーネルパッチを提供できていなかった点だ。Ubuntuは記事執筆時点で未対応であり、DebianやRHEL 9が対応を発表した段階だった。この数日間のギャップにおいて、コンテナランタイムの更新は唯一の実用的な緩和策だった。

コンテナ運用においてDocker Engineを最新に保つことは、単なる機能向上のためではない。カーネル脆弱性の公開からパッチ適用までの「空白期間」を埋める、最も迅速な防御手段の一つなのだ。

多層防御の絶対的必要性

このインシデントは「単一の防御層に頼ることの危険性」を端的に示した。seccompは強力だが、システムコールの粒度でしか制御できない。AppArmorやSELinuxはカーネル内部のオブジェクト生成にフックするため、より精密な制御が可能だが、ホストOSの設定に依存する。両者を組み合わせることで初めて、互いの死角を補完できる。

また、v29.4.2のsocketcall()拒否が引き起こした互換性問題は、セキュリティと互換性のトレードオフの難しさを教えている。広範なブロックは新たな問題を生む。可能な限りピンポイントな制御を追求し、やむを得ず広範な制限をかける場合は、その影響範囲を事前に十分評価する必要がある。

単層防御のリスク(Bad)
seccompのみ socket(2)ブロック socketcall()経由で突破
※ seccompはポインタ先を検査できず、抜け道を許す。
多層防御の有効性(Good)
seccomp socket(2)ブロック + AppArmor 両経路でAF_ALG拒否
※ カーネル内部フックにより、システムコールの種類を問わず遮断。

この比較から得られる教訓は明確だ。セキュリティ対策は、異なるレイヤーで相互に補完し合う設計が必須である。一つの仕組みで完璧を目指すのではなく、それぞれの得意領域を理解し、弱点を他の層でカバーする。これこそがコンテナセキュリティの基本原則である。

この記事のポイント

  • CVE-2026-31431はAF_ALGソケットを悪用し、2017年以降のLinuxカーネルに影響する
  • Docker v29.4.2のseccomp修正は32bitバイナリのネットワークを破壊する副次的被害を起こした
  • v29.4.3ではAppArmorとSELinuxを組み合わせた多層防御で選択的なAF_ALG遮断を実現
  • カーネルパッチが最も確実な修正だが、エンジン更新が迅速な緩和策として有効
  • 単一防御層の限界を認識し、複数の技術で死角を補完する設計が今後の鉄則となる
DockerがカスタムMCPカタログとプロファイルを正式提供、企業のAIツール管理が新段階へ

DockerがカスタムMCPカタログとプロファイルを正式提供、企業のAIツール管理が新段階へ

Dockerは2026年5月15日、MCP(Model Context Protocol)サーバーを管理する「カスタムカタログ」と「プロファイル」の一般提供を開始した。組織はこれらを使ってMCPサーバー群を一元的に管理し、開発者は作業内容に応じたツール構成を簡単に切り替えられるようになる。

この発表の背景には、企業へのMCP導入が進むにつれて「誰がどのMCPサーバーを信頼して使うべきか」という調整コストが急増していた現実がある。Dockerの新機能は、プラットフォームチームが推奨するツール群を「カタログ」として配布し、現場の開発者が「プロファイル」で自由に組み替えるという二層構造でこの課題を解決する。

MCP活用の壁とDockerの解決策

MCP活用の壁とDockerの解決策

MCPはAIエージェントが外部ツールやデータソースと対話するための標準プロトコルだ。ChatGPTのプラグイン機能に相当するが、ベンダーに依存せずオープンな仕様で設計されている。Dockerは2025年後半からMCPサーバーの統合管理機能「MCPカタログ」を提供してきたが、公開サーバーだけでは社内ツールや独自要件に対応しきれないという声が増えていた。

特に大きかったのは「全社で使える信頼済みリストがほしい」というニーズと「開発者個人のワークフローに合わせた構成を使いたい」というニーズのせめぎ合いだ。前者を強めると開発者の自由度が下がり、後者を優先するとセキュリティ基準が守れなくなる。Dockerが今回一般提供を始めたカスタムカタログとプロファイルは、この二つを両立させるインフラにあたる。

カスタムカタログとプロファイルの役割分担

カスタムカタログは「組織が推奨するMCPサーバーの集合」を定義し、OCIアーティファクトとして配布できる仕組みだ。プロファイルは個人がカタログから選んだサーバー群を「コーディング用」「企画用」といった用途別にまとめ、クライアント(Claude Codeなどのエージェント)に切り替えて接続できる。

組織のプラットフォームチーム
Docker MCPカタログ、コミュニティソース、社内開発サーバー
カスタムカタログを作成してOCIレジストリに配布
信頼審査済み、組織として推奨するMCPサーバーを一元管理
現場の開発者
カスタムカタログから必要なサーバーを選択
コーディング用プロファイル
← クライアントを切り替え →
企画用プロファイル
用途に応じたツールだけをエージェントに接続できる

この二層構造によって「組織が定める信頼の枠組み」と「個人が工夫する効率化」が衝突しなくなる点が最大の価値だ。プラットフォームチームはガードレールを引き、開発者はその中で自由にツールを組み替える。

カスタムMCPカタログの作成と配布手順

カスタムMCPカタログの作成と配布手順

Dockerの公式ブログで解説されている手順に沿って、実際にカスタムカタログを作成する流れを見ていこう。ここではDocker Hubをレジストリとして使う例だが、プライベートレジストリにも対応する。

ステップ1 自前のMCPサーバーをイメージ化する

まず、組織内で使いたい独自のMCPサーバーをDockerイメージとしてビルドし、レジストリにプッシュしておく。Dockerの解説では、さいころを振るroll-diceというサンプルサーバーが使われている。stdioで通信する標準的なMCPサーバーであり、Dockerfileからイメージを作成する手順は通常のコンテナ開発と変わらない。

イメージが用意できたら、そのサーバーのメタデータをYAMLファイルに記述する。ファイル名や格納場所は任意だ。

name: roll-dice
title: Roll Dice
type: server
image: roberthouse224/mcp-dice@latest
description: An mcp server that can roll dice

このYAMLにはサーバーの識別名、表示タイトル、Dockerイメージの参照先、説明文が含まれる。実際の運用では、ここにアクセス権限や設定パラメータのメタ情報を追加することも考えられる。

ステップ2 Docker MCPカタログと自前サーバーを束ねる

次に、docker mcp catalog createコマンドを使ってカスタムカタログを作成する。引数にはDocker公式カタログから取り込みたいサーバーと、先ほど用意したYAMLファイルのパスを指定する。

docker mcp catalog create roberthouse224/our-catalog \
  --title "Our Catalog" \
  --server catalog://mcp/docker-mcp-catalog/playwright \
  --server catalog://mcp/docker-mcp-catalog/github-official \
  --server catalog://mcp/docker-mcp-catalog/context7 \
  --server catalog://mcp/docker-mcp-catalog/atlassian \
  --server catalog://mcp/docker-mcp-catalog/notion \
  --server catalog://mcp/docker-mcp-catalog/markitdown \
  --server file://./mcp-dice.yaml

catalog://スキームでDockerの公式カタログから既存のサーバーを取り込み、file://スキームで自作サーバーのメタデータを追加している。このカタログはローカルマシン上にOCIアーティファクトとして作成され、docker mcp catalog showで内容を確認できる。

ステップ3 カタログをレジストリで共有する

作成したカタログは、docker mcp catalog pushでDocker Hubやプライベートレジストリにプッシュすれば即座に共有可能になる。OCIアーティファクトとしての配布は、組織内のリポジトリアクセス権限をそのまま使えるため、追加のインフラ管理が不要だ。

docker mcp catalog push roberthouse224/our-catalog

これで、組織内の他メンバーはDocker Desktopの「カタログインポート」機能か、docker mcp catalog pullコマンドでこのカタログを取得できる。公式カタログにはない社内ツールが含まれている点、すべてのサーバーが組織としての信頼審査を通過している点が、単なる公開カタログとの決定的な違いだ。

カスタムカタログがエンタープライズにもたらす意味

カスタムカタログがエンタープライズにもたらす意味

ここで一歩引いて、この機能が企業のAI活用に何をもたらすかを考えてみたい。単なる「MCPサーバーリストの共有」に見えるが、実際にはもっと大きな変化の起点になる。

第一に、MCPサーバーの発見と評価にかかるコストが大幅に下がる。開発者がインターネット上からMCPサーバーを探して安全性を個別に判断する必要がなくなり、組織が「使ってよいもの」をあらかじめ提示できる。これはソフトウェアサプライチェーン管理の考え方をAIツールに応用したものとも言える。

第二に、プライベートレジストリと組み合わせれば、社内限定のAIツールを企業秘密として保護しながら配布できる。たとえば自社データベースに特化したMCPサーバーを、アクセス権のあるメンバーだけに提供する使い方が想定される。Dockerのブログでも「プライベートカタログ」という方向性が示唆されている。

第三に、OCIアーティファクトという既存の業界標準に乗っていることが地味ながら重要だ。組織はすでにコンテナレジストリの運用ノウハウとアクセス管理の仕組みを持っている。それをそのままMCPに転用できるため、新たに専用の配信インフラを構築する必要がない。

従来のMCPサーバー探し
開発者個人がウェブ検索 → 品質不明、安全性未確認のサーバーを個別に試す
発見コスト大・チーム間でバラつき発生
カスタムカタログ導入後
プラットフォームチームが審査済みリストを配布 → 開発者はカタログから選ぶだけ
信頼性確保・チーム全員が同じ基準で作業開始できる

このようにカスタムカタログは「野良ツールの乱立を防ぎつつ、社内イノベーションを促進する」バランサーとして機能する。とはいえ、カタログで提供されるのはあくまで「選択肢の集合」だ。実際の作業でどのツールをどう組み合わせるかは、次のプロファイル機能が受け持つ。

MCPプロファイルで個人ワークフローを最適化する

MCPプロファイルで個人ワークフローを最適化する

プロファイルは、カタログから選んだMCPサーバー群を「コーディング」「企画」「調査」などの用途別に束ね、任意のAIクライアントに接続できる仕組みだ。特定のプロファイルには必要なツールだけが含まれるため、エージェントのコンテキストウィンドウを無駄に消費しない利点がある。

作業モードの切り替えを数クリックで実現

Docker Desktop 4.63から利用できるプロファイル機能の基本動作はシンプルだ。カスタムカタログを開き、使いたいサーバーを選択して「新しいプロファイル」を作成する。プロファイルには接続先クライアント(Claude Codeなど)を指定できるが、後から付け替えることも可能だ。

たとえば、Playwright、GitHub、Context7を含む「コーディング」プロファイルと、Atlassian、Markitdown、Notionを含む「企画」プロファイルを別々に作っておけば、作業内容に応じてクライアントの接続先を切り替えるだけでツール環境が丸ごと入れ替わる。これまではツールセットを切り替えるたびに再設定が必要だったが、プロファイルによりワンアクションで済む。

設定の保存と再利用で反復作業を削減

プロファイルのもう一つの利点は、MCPサーバーの設定を永続化できることだ。Markitdownサーバーにアクセス可能なディレクトリパスを指定する場合や、GitHubサーバーのうち使うツールをget_meだけに絞る場合など、一度設定した内容はプロファイルに保存される。これにより、毎回手動で同じ設定を繰り返す手間が省ける。

コンテキストウィンドウの最適化という観点では、大量のツールをエクスポートするMCPサーバーに対して「このタスクではツールAとBだけ有効化する」と制限できる点が実用的だ。エージェントの推論性能を落とさず、必要な機能だけに集中させられる。この仕組みは、社内開発するMCPサーバーにリッチな設定オプションを持たせることで、さらに強力な再利用性を発揮するだろう。

コーディングプロファイル
Playwright
GitHub(get_meのみ有効化)
Context7
コンテキストウィンドウ消費: 小
↕ クライアント接続を切り替え
企画プロファイル
Atlassian
Markitdown(アクセスパス制限あり)
Notion
コンテキストウィンドウ消費: 中
※各プロファイルは独立しており、作業内容に合わせて必要なツールだけをエージェントに提供する

上図のように、同じカタログから異なるプロファイルを複数作成し、用途に応じて切り替える運用が基本スタイルになる。この考え方は、VS Codeのワークスペース設定や、ターミナルのプロファイル管理に近い。AIエージェント時代の「作業環境テンプレート」と捉えるとわかりやすいだろう。

プロファイルの共有と今後の展望

プロファイルの共有と今後の展望

プロファイルもカスタムカタログと同様、OCIアーティファクトとしてレジストリで共有できる。docker mcp profile pushでプッシュすれば、チームメンバーはdocker mcp profile pullで即座に同じツール構成を手に入れられる。うまくいった設定を「テンプレート」として展開できるこの仕組みは、プロジェクト立ち上げ時の環境構築コストを大幅に下げる。

docker mcp profile push coding your-namespace/coding

Dockerは今後、以下の方向性でカスタムカタログとプロファイルを拡張していくとしている。

  • ガバナンスとポリシー制御により、承認されたカスタムカタログ以外からのMCP利用を制限
  • カタログとプロファイルのディスカバビリティを向上し、実績のある構成を見つけやすくする
  • プロファイルスコープでのシークレット・設定値管理を強化し、セキュアな代替手段として整備
  • エージェントスキルとの連携により、プロファイルを依存関係として参照するワークフロー

特に最後の「エージェントスキルがプロファイルを依存関係として参照する」という構想は興味深い。たとえば「データ分析スキル」が起動するときに、必要なMCPサーバー構成をプロファイルから自動で引き込むといった使い方が想定されている。これが実現すれば、AIエージェントが自律的に必要なツールを調達して動く世界がさらに近づく。

この記事のポイント

  • DockerがカスタムMCPカタログとプロファイルの一般提供を開始し、企業のAIツール管理に新たな基盤が加わった
  • カスタムカタログは組織が信頼するMCPサーバー群をOCIアーティファクトで配布し、発見コストとセキュリティリスクを同時に下げる
  • プロファイルは個人が用途別にツール構成を保存・切り替えできる仕組みで、コンテキスト最適化にも有効
  • 両方ともOCIアーティファクトで共有可能なため、既存のコンテナレジストリ運用の延長でチーム展開できる
  • 今後のポリシー制御やエージェントスキル連携により、エンタープライズMCPのガバナンス基盤として発展が見込まれる