はじめに
この記事はQiita 3-shake Advent Calendar 2025 シリーズ13日目の記事です。
最近、低レベルコンテナランタイムである youki にコントリビュートしており、特にデタッチドマウントについて調べる機会があったので、その内容を共有します。
この記事は コンテナランタイムの内部実装に興味がある方向けの内容になっています。
コンテナを利用するユーザーであれば、
- ルートレスコンテナ(rootless container)を利用する
- 脆弱性が報告された場合はランタイムのバージョンを更新する
といった対応になると思います。
コンテナの脆弱性
最近runcやyoukiなどの低レベルコンテナランタイムでは脆弱性に対する対応を行いました。
上記で報告されている脆弱性の主な原因は、コンテナ内のルートファイルシステムに対するパスベースの操作が信用できないことにより発生しています。
By using race mount conditions and procfs write redirects, an attacker could gain root access to the host system.
パスを直接扱うと、攻撃者がシンボリックリンクや race condition を利用して
意図しないパスに対して mount・write を行わせることが可能となり、
結果として コンテナ脱獄につながる危険性があります。
デタッチドマウント
上記のような問題に対処するため、コンテナ内のパスを信用せず、
マウント操作をファイルディスクリプタ(fd)ベースで行う方向に移行しています。
いわゆる「デタッチドマウント」と呼ばれる手法は、
- ファイルシステムの設定準備
- ファイルシステムを実際にアタッチする操作
ファイルシステム階層から独立した
状態でマウントを準備することで、外部から干渉されることのない安全なマウント操作を
実現します。
fd ベースのアプローチを実現するために、Linux には
fsopen / fsconfig / fsmount / move_mount/ open_tree といった一連の 新しい mount API が導入されました。(2020年ごろ導入なので新しくないかもしれませんが、mount(2)と比べて新しいので新しいという単語を使います)
https://lwn.net/Articles/1032868/
https://github.com/brauner/man-pages-md/tree/main
youkiの実装例
youkiで上記のmount APIを利用する例を見ていきます。
mount操作
たとえば、パスベースでmount(2)を利用してbind mountでread onlyにしたい場合は以下のようにremountする必要がありました。
bind mount の第 1 回目と第 2 回目の実行間で
パスが攻撃者により別パスへ誘導(symlink / race)されるという危険があり、意図した read-only の適用に失敗する可能性があります。
mount操作をnew mount APIで書くと以下のようになります
上記はbind_mountの例です。bind_mountの場合は以下のようになり、move_mountするまでは外部から独立したmountとなります。その状態でmount_setattrでread-onlyなどのマウントフラグを設定すれば、一連のmount操作が独立して実施することができます。
- open_tree でデタッチドマウントを取得する
- 元ファイルシステムの subtree を デタッチドマウントとして取得
- mount_setattrでマウントフラグを設定
- この時点ではまだどこにも attach されていない
- move_mountで実際にmountをアタッチする
procfsへの安全な書き込み
コンテナランタイムではコンテナに対する設定を変更するために/proc以下へ書き込む場合があります。
代表的な例として、以下のようなものがあります。
- Apparmorの設定を変更するために /proc/self/attr/apparmor/exec へ書き込む
以前は以下のようにパスベースで書き込んでいました。
fs::write(path, format!("exec {profile}"))
このようなパスベースで書き込みを行うと、別のパスに誘導され、意図しないパスに書き込みをする危険があります。
新しいyoukiの実装では以下のようになります。
これはlibpathrsというcrateを利用しており、中の処理の概要を書くと以下のようになります。
-
fsopen/fsconfigでprocfsのファイルシステムのコンテキストを作る -
fsmountでデタッチドマウントを作成する -
openat2で/procを基準に/proc/selfのfdを取得する -
openat2で/proc/selfを基準に/proc/self/attr/apparmor/execのfdを取得する - 取得したfdに対して書き込み処理を行う
これにより、パスベースではなく、fd ベースで procfs に対して安全に書き込むことができます。
まとめ
この記事では、最近のコンテナランタイムにおけるセキュリティ課題と、それに対処するための fd ベース mount API の活用について紹介しました。
さらなる深掘りをしたい方は以下を見ると良いと思います。
- new mount APIのman pageを見る(exampleも豊富です)
- libpathrsのソースコードを見る
- runcのメンテナであり、libpathrsの作成者の最近の発表を見る
- https://ossjapan2025.sched.com/event/29FoE/secure-path-operations-and-libpathrs-aleksa-sarai-independent
- 今回の脆弱性よりずっと前からコンテナ内のパスに関するセキュリティに取り組んでいる歴史が垣間見えて、とても学びになります。
参考
runc
- https://github.com/opencontainers/runc/security/advisories/GHSA-qw9x-cqr3-wc7r
- https://github.com/opencontainers/runc/security/advisories/GHSA-cgrx-mc8f-2prm
- https://github.com/opencontainers/runc/security/advisories/GHSA-9493-h29p-rfm2
youki