Docker EngineにおけるCVE -2026-31431 (「コピー失敗」)の緩和

投稿日: 5月 27日, 2026年

CVE-2026-31431 は最近公開されたLinuxカーネルの脆弱性です。このCVEはDockerのインフラを侵害しません。

とはいえ、Docker Engineのv29以前のデフォルトプロファイルは4。3コンテナはAF_ALGソケットを作成でき、これはエクスプロイトが使用するsyscallの表面です。Docker Engine v29を使っていれば、露出はありません。4。3 以降、あるいはパッチを当てたホストカーネル。もしそれらが欠けていれば、そのホストで露出しているので、この投稿の続きを読むべきです。

執筆時点で、カーネルパッチはDebian(CVE-2026-31431)およびRHEL 9 (RHSB-2026-002)で利用可能ですが、 Ubuntu上はまだ利用できません。カーネル修正をまだ提供していないディストリビューションのユーザーにとっては、Docker Engineのアップグレードが今日適用できる緩和策です。

コピー・フェイルについて読むべき理由

この CVE は、多くの Linux ディストリビューションがカーネルパッチを入手する前に公になったため、大きな注目を集めました。その結果、ほとんどのディストリビューションは依然として脆弱であり、開示時点で即効した解決策を持っていませんでした。特に注目すべきは、このバグが約 2017年頃まで遡るLinuxカーネルに影響を与え、影響が異常に広範囲に及んでいるからです。

Docker Engineチームでは、脆弱なホストのユーザーを守るために自分たち側で何ができるかを調査し始めました。しかし、その緩和策は最初に思ったよりも複雑で、最初の試みで 32ビットのバイナリが破られました。この投稿は、私たちが発売したもの、壊れたもの、学んだこと、そして現在の状況について述べています。

コピー失敗とは何か

4月 29日、研究者たちはLinuxカーネルのAF_ALG暗号サブシステムにおける権限昇格の脆弱性として「コピーフェイル」と呼ばれるCVE-2026-31431を明らかにしました。

欠陥は algif_aead モジュールにあります。AF_ALGソケットにアクセスできる非特権ユーザーがページキャッシュへの制御された書き込みを行うことを可能にします。ページキャッシュはシステム全体のファイル読み取りをバックバックするため、攻撃者はホスト上のすべてのプロセスが認識する任意の読み取り可能なファイルの内容を一時的に変更できます。setuidバイナリを破損させることがローカルルートへの最も直接的な経路ですが、プリミティブ自体はより一般的です。

この脆弱性は簡単で、 2017以降リリースされたすべてのパッチ未修正のLinuxカーネルで有効です。

正しい修正はカーネルアップデートです。以下に説明する緩和策は、パッチ未処理のカーネル上で動作するコンテナの露出を減らすものですが、根本的な脆弱性を修正するものではありません。カーネルベンダーがパッチをリリースしているなら、適用してください。

これはコンテナにとって何を意味するのでしょうか?

デフォルトのセキュリティプロファイルで動作するコンテナ内で、コード実行を行う攻撃者はコピー失敗を使ってページキャッシュ内のページを破損させることができます。一つの可能な結果は、setuidバイナリを破損してコンテナ内でroot化するまでエスカレーションすることです。

しかし、ページキャッシュはホスト全体で共有されるため、影響は攻撃者のコンテナに限定されません。修正されたページはホストだけでなく、同じファイルをマッピングする他のすべてのコンテナ(共有画像レイヤーも含む)に見えます。同じノード上の他のワークロードも影響を受けることがあります。

この攻撃は、デフォルトのコンテナが提供する以上の特別な能力や権限を必要としません。唯一の要件は AF_ALG ソケットの作成能力であり、これは以前Dockerのデフォルトのセキュリティプロファイルで許可されていました。

初回挑戦:seccomp(294.2)

Docker Engineのデフォルトの seccompプロファイル を更新し、 AF_ALG ソケットをブロックしました。seccompフィルターは最初の引数を検査して socket(2) し、すでにブロックされていたアドレスファミリー AF_ALGAF_VSOCK を拒否します。

socket(2)をブロックするだけでは十分ではありません。Linux64 x86_でソケットを作成するもう一つの方法があります。それはsocketcall(2)という古い多重化システムコールで、socketbindconnectなどのソケット操作を単一のSyscall番号にラップしています。

Linux上でソケットを作成するもう一つの方法があります。 socketcall(2)という古い多重化システムコールで、 socketbindconnectなどのソケット操作を単一のシスコール番号でラップします。

seccompの問題は、 socketcall が実際の引数(アドレスファミリーを含む)をユーザースペース配列にパックし、BPFがそのポインタをリリレオンして検査できないことです。seccompではsocketcallを通じたAF_ALGを選択的にブロックする方法はありません。

Linux 4。3 すでにi386 とS390に直接ソケットのシステムコールを追加しているので、ほとんどの現代のバイナリはすでに新しい socket シスコールを使っており、 socketcall は古いバイナリにのみ影響すると想定していました。そこで完全にブロックし、Docker Engine v29を出荷しました。4。2(リリースノート)

何が壊れたのか

socketcallの否定はあまりにも広範であることが判明しました。

古いバージョンのglibcはsocketcall386すべてのソケット操作をルーティングし、GoランタイムではGOARCH=386に無条件使用(glibcとは独立)。多くのレガシーやゲームワークロード(SteamCMD、Wine)はそれに依存しています。

socketcallブロックはコンテナ内で動作する多くの32ビットバイナリ(moby/moby#52506)のネットワークを壊しました。

これは単なる私386 の問題ではありません。AMD64では、任意のプロセスがint $0x80とのIA32互換モードに切り替え、直接socketcallを呼び出しることができ、socket(2)ARGフィルターを完全に回避できます。その経路に到達するのに 32ビットコンテナや 32ビットのバイナリは必要ありません。

影響を受けたコンテナは、カスタムのseccompプロファイルを使ってsocketcallを再有効化しつつ、直接socket(2)パスでブロックAF_ALG保持することでこれを回避できます。

しかし、それはコンテナの硬化に穴を開けるだけです。なぜなら、その中の攻撃者がsocketcallを通してAF_ALGに届く可能性があるからです。

2回目の試み:LSMベースの執行(v29.4.3)

根本的な問題は、seccompがシスコール境界で動作し、 socketcall ポインタ引数付きで単一のシスコール番号の背後で多くの操作を多重化していることです。Seccompだけでソケットコールを通じた AF_ALG を選択的にブロックすることはできません。

AppArmorとSELinuxは異なるレベルで機能しています。Linuxセキュリティモジュールはカーネルの security_socket_create() コールバックに直接フックし、どのsyscallエントリーポイントが使われていても、カーネルが実際にソケットオブジェクトを作成するとコールバックが発動します。LSMは AF_ALG を特定の条件で拒否しつつ、他のソケットコールの使用はそのままにすることができます。

v29において。4。3(リリースノート)、私たちは:

  1. socketcallseccomp denyを元に戻し、32ビット互換性を回復しました。
  2. デフォルトのAppArmorプロファイル( moby/profiles#)に22 deny network alg, を追加しました。
    AppArmorが有効になっているシステム(例:Ubuntu、Debian)、これによりsocket(2)socketcall(2)の両方でAF_ALGをブロックします。
  3. SELinuxを動作させるシステム(Fedora、RHEL、CentOS)向けにSELinux CILポリシーモジュールを統合しました。
    このモジュールはすべてのcontainer_domainタイプに対してalg_socket作成を拒否し、semoduleで読み込むことができます。
    SELinuxの強制には、デーモンが --selinux-enabledと共に動作していることが必要です。
  4. seccomp socket(AF_ALG) argフィルターは 、直接 socket(2) シスコールパスの防御深層として保持しました。

あなたがすべきことは

  1. カーネルにパッチを当ててください。
    これが本当の解決策です。
    あなたのディストリビューションで、CVE-2026-31431に対応するカーネルアップデートがあるか確認してください。
  2. Docker Engineをv29にアップグレードしてください。4。3 かそれ以降。更新されたseccomp + AppArmor + SELinuxのデフォルトが出ます。systemctlの再起動docker(または同等のもの)で十分です。ホストの再起動は不要です。
  3. カーネルやエンジンをすぐにアップグレードできない場合:
  • カーネルモジュールをブラックリストに入り、 blacklist af_algblacklist algif_aead を追加 /etc/modprobe.d/
    これはモジュールがカーネルにコンパイルされていない場合に限り、ロード可能なモジュール(CONFIG_CRYPTO_USER_API=m)として構築されている場合のみ機能します。
  • カスタムのseccompプロファイルを適用して、--security-opt seccomp=/path/to/profile.jsondaemon.jsonseccomp-profileオプションの使用をAF_ALG拒否します。

締めの思いつき

セキュリティは層状に重なり、時には一層だけでは十分ではありません。Seccompはすべてのシステムで socket(AF_ALG) をブロックしますが、 socketcallには気づきません。AppArmorとSELinuxは両方の経路をブロックしますが、ホストの設定に依存します。二人は共に、どちら一人でもカバーできないことをカバーしている。

LSMを持たないシステムでは、Docker側からの socketcall 経路はブロックされていません。最終的に修正すべきはカーネルのバグです。

カーネルの脆弱性は今後も発生し続けるでしょう。そうなると、コンテナランタイムが緩和策を展開するのに最も速い場所となることが多いです。なぜならエンジンの更新はホスト上のすべてのコンテナを保護する一つの変更だからです。コピー失敗のタイムラインはそれを特に明確に示していました。エンバーゴはディストリビューションが修正を整える前に解除され、数日間はカーネルの再構築を待たずにエンジンだけが問題を軽減できる場所でした。

Docker Engineを最新の状態に保つことは、単なる新機能の話ではありません。これは、カーネルCVEが公開されるまでの期間を縮小する最も効果的な方法の一つです。

関連記事