rootlessコンテナとuser名前空間による非特権隔離
root権限なしでもコンテナを動かせるのはなぜか。user名前空間のID写像とsubuid/subgidの仕組みから、newuidmapやfuse-overlayfsが補う制約までを原理から解き明かします。
- 1.rootlessコンテナは最初にuser名前空間を作って内側rootを獲得し、その権限を足場に他の名前空間を非特権で生成する。実権限はホスト上の単一非特権UIDに留まる。
- 2.1人のユーザーが複数UIDを使うには /etc/subuid・/etc/subgid で割り当てた範囲が必要で、その範囲を多段マッピングへ展開するのがsetuidヘルパの newuidmap/newgidmap。
- 3.非特権ゆえに mount(2) でのoverlay重ねやネットワーク配線が制限され、fuse-overlayfs や slirp4netns / pasta が代替手段として使われる。
rootlessコンテナが解く矛盾
通常のコンテナランタイムは root(より正確には CAP_SYS_ADMIN などの capability)を要求します。名前空間の生成、overlay マウント、veth の配線といった操作が特権を必要とするからです。rootless コンテナは、これらを 一般ユーザーの権限だけで 実現します。狙いは攻撃面の縮小です。デーモンが root で常駐しないため、ランタイムやコンテナを踏み台にしてもホストの root は奪えません。
成立の鍵は user 名前空間(userns)一点に尽きます。unshare(CLONE_NEWUSER) だけは、非特権ユーザーでも呼べる(多くのディストリで kernel.unprivileged_userns_clone=1 相当が有効)よう設計されています。新しい userns の作成者は、その名前空間の中で全 capability を満たした状態になります。つまり「自分が作った userns の内側でだけ root」になれる。この内側 root を足場に、PID・mount・network といった他の名前空間を 追加の特権なしに 作っていく——これが rootless の基本構造です。詳しい ID 写像の原理は 名前空間の内部実装 で扱っています。
subuid/subgid:1ユーザーに複数IDを貸し与える
userns の中で root になれても、使える UID が自分の1個だけ では困ります。コンテナ内で複数のサービスユーザー(www-data、postgres など別 UID)を動かしたいのに、写せる外側 UID が1つなら全員が同じホスト UID に潰れ、隔離が崩れます。
これを解くのが /etc/subuid と /etc/subgid です。各行は ユーザー名:開始ID:個数 で、そのユーザーが 自分のもの以外に追加で使ってよい UID/GID の範囲 を管理者が事前に割り当てます。
# /etc/subuid ユーザー名:開始サブUID:個数
alice:100000:65536
bob:165536:65536
この例では alice はホスト UID 100000〜165535 の 65536 個を、自分の権限として使ってよいことになります。重要なのは、この範囲が ユーザー間で重ならない よう割り当てられる点です。重なれば、alice のコンテナが書いたファイルを bob のコンテナが所有者として扱えてしまい、隔離が破れます。割り当て自体は useradd 時や usermod --add-subuids で行われ、ファイル所有権は実際にはこの「サブ ID」で記録されます。
本来 uid_map に複数行(複数の外側 ID)を書くには親 userns で CAP_SETUID が要ります。非特権ユーザーは自分の実 UID 1個しか自力で写せません。subuid の範囲は、この制約を 管理者の事前承認 という形で緩める仕組みです。あらかじめ /etc/subuid に書かれた範囲だけは、後述の setuid ヘルパ経由でマッピングに使えます。承認済みの ID プールを配るので、ユーザーが任意の他人の UID を騙る危険がありません。
newuidmap/newgidmap:マッピングを書き込むsetuidヘルパ
/etc/subuid に範囲があっても、それを実際の uid_map へ展開する権限 がユーザーにはありません。uid_map への複数行書き込みには特権が要るからです。ここで登場するのが newuidmap・newgidmap という小さな setuid root プログラム(shadow-utils 付属)です。
ランタイム(Podman、Buildah、rootlesskit 等)は、子プロセスで userns を作った後、newuidmap PID 0 1000 1 1 100000 65536 ... のように呼びます。各引数の組は 内側ID 外側ID 個数 で、uid_map の各行に対応します。
| マッピング行 | 意味 | 由来 |
|---|---|---|
| 0 1000 1 | 内側UID 0(root)→ ホストの実UID 1000 | 自分の実UID。1個だけは特権なしでも写せる |
| 1 100000 65536 | 内側UID 1〜65536 → ホストUID 100000〜 | /etc/subuid の割り当て範囲 |
newuidmap は setuid root で動くものの、要求された各範囲が 本当にその呼び出しユーザーへ /etc/subuid で割り当てられているか を必ず検証してから書き込みます。承認外の ID を要求すれば拒否されます。これにより「特権が必要な操作を、検証付きの小さなヘルパへ閉じ込める」——最小権限の原則がそのまま設計に現れています。GID 側も同様で、gid_map 書き込み前に setgroups を deny にする規則(補助グループ悪用対策)も newgidmap が面倒を見ます。capability の境界そのものは Linuxケーパビリティによる特権分割 を参照してください。
fuse-overlayfs:非特権でレイヤを重ねる
イメージのレイヤ合成には OverlayFS が使われますが、ここで rootless の壁にぶつかります。カーネルの overlayfs を mount(2) で重ねる操作は、長らく特権(実質ホスト root)を要した からです。userns の内側で root でも、それは「その userns に属する資源」にしか効かず、ファイルシステムのマウントというホスト規模の操作はそのままでは許されません。
回避策が2系統あります。
- fuse-overlayfs: OverlayFS と同等のレイヤ重ね合わせを ユーザー空間(FUSE)で実装 したもの。FUSE のマウント自体は非特権で行えるため、カーネル overlayfs の特権要件を迂回できます。代償として、I/O がユーザー空間を往復する分のオーバーヘッドが乗ります。
- カーネル overlayfs の userns 対応: 比較的新しいカーネルでは、user 名前空間内からの overlayfs マウントが条件付きで許可されるようになりました。条件が揃えば FUSE を介さずネイティブ性能で動きます。
OverlayFS そのものの上位・下位レイヤや copy-up の原理は コンテナのオーバーレイファイルシステム で詳しく扱っています。rootless では、どちらの方式でも 下位レイヤのファイル所有者が subuid 経由でずれて見える 点に注意が必要です。イメージ内で UID 0 所有だったファイルが、ホスト上では subuid(例 100000)所有として記録されるためです。
veth ペアをホストのブリッジへ繋ぐ通常の配線は、ホスト側ネットワーク名前空間の操作を伴うため非特権では行えません。rootless では代わりに slirp4netns や pasta が使われます。これらはユーザー空間で TCP/IP を喋り、コンテナの netns 内のタップデバイスとホストの通常ソケットを橋渡しします。性能とのトレードオフはありますが、root なしで外部疎通を得る現実的な手段です。
rootlessの設計上の制約
rootless は万能ではありません。原理的に解けない、あるいは代償を伴う制約があります。
| 制約 | 理由 | 緩和策 |
|---|---|---|
| 1024未満のポートをbindできない | 特権ポートのbindにCAP_NET_BIND_SERVICEが要る(ホスト側) | sysctlでの開放、ポート転送、pasta |
| overlayマウントが特権要件に当たる | ファイルシステムのマウントはホスト規模の操作 | fuse-overlayfs / 新カーネルのuserns対応 |
| pingやveth配線が制限される | RAWソケットやホストnetns操作に特権が要る | slirp4netns / pasta |
| ファイル所有がsubuidへずれる | 内側UIDは外側subuidへ写像されるため | podman unshare 等の名前空間内での操作 |
もう一つの本質的制約が カーネルそのものの攻撃面 です。userns は非特権ユーザーが大量のカーネルコード経路(特に過去に脆弱性が集中した領域)へ到達できるようにします。このため一部のディストリやセキュリティ方針では、非特権 userns を無効化したり、seccomp や LSM で締めたりします。rootless はホスト root の奪取を難しくしますが、カーネル LPE(ローカル権限昇格)の入口を増やしうる 両刃の側面を持つ、と理解しておく必要があります。
rootless の安全性は「コンテナを破ってもホスト上では一介の非特権ユーザーに過ぎない」点にあります。しかし userns 内の root は そのユーザーの資産(ホームディレクトリ等)に対しては実 UID 1000 として 振る舞えます。つまりコンテナ脱出が起きれば、ホスト root は守れても そのユーザーのデータは守れません。さらにカーネル脆弱性を突く LPE は別問題として残ります。rootless は防御の一層であって、seccomp・LSM・最新カーネルと組み合わせて初めて意味を持ちます。
押さえどころは3点。第一に「rootless の起点は userns で、内側 root を足場に他の名前空間を非特権生成する」。第二に「複数 UID を使うには /etc/subuid・/etc/subgid の範囲が必要で、それを uid_map へ展開するのが検証付き setuid ヘルパの newuidmap/newgidmap」。第三に「非特権ゆえ overlay マウントとネットワーク配線が制限され、fuse-overlayfs と slirp4netns/pasta が代替を担う」。「特権ポート不可」「ファイル所有が subuid へずれる」「カーネル攻撃面は増える」も差がつくポイントです。
まとめ
rootless コンテナは、唯一非特権で作れる user 名前空間 を起点に、内側 root という足場を得て他の名前空間を積み上げる設計です。1ユーザーが複数 ID を扱うために /etc/subuid・/etc/subgid で範囲を事前承認し、その範囲を uid_map/gid_map へ展開する特権操作を、検証付き setuid ヘルパ newuidmap/newgidmap に閉じ込めます。非特権ゆえカーネル overlayfs のマウントや veth 配線が壁になり、fuse-overlayfs と slirp4netns/pasta がユーザー空間で代替します。攻撃面を縮める強力な手段ですが、ユーザー資産は守れずカーネル LPE の入口は増えるため、コンテナランタイムの内部 や seccomp・LSM と組み合わせた多層防御の一部として位置づけるのが正解です。
OS Article
rootlessコンテナとuser名前空間による非特権隔離を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
コンテナ
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 5
導入後に効く点
1人のユーザーが複数UIDを使うには /etc/subuid・/etc/subgid で割り当てた範囲が必要で、その範囲を多段マッピングへ展開するのがsetuidヘルパの newuidmap/newgidmap。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 5
判断チェックリスト
- 自社の用途が「コンテナ / Linux」に近いか確認する。
- 強みである「rootlessコンテナは最初にuser名前空間を作って内側rootを獲得し、その権限を足場に他の名前空間を非特権で生成する。実権限はホスト上の単一非特権UIDに留まる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。