コンテナ分離の原理(namespace・cgroup・seccomp)
コンテナはなぜVMより漏れやすいのか。namespace・cgroup・capability・seccompが何を隔離し何を隔離しないかを原理から押さえれば、エスケープ経路の評価と現実的な多層防御の設計ができるようになる。
- 1.コンテナは単一カーネルを共有し、namespace が資源の「見え方」を、cgroup が資源の「使用量」を分離する。カーネル自体は共有なので、VM のようなハードウェア境界による隔離ではない。
- 2.root の万能権限は capability で細分化され、seccomp-bpf でシステムコール自体を絞り、ユーザー名前空間でコンテナ内 root をホスト上の非特権 UID に写像することで攻撃面を縮める。これらは併用して初めて実用的な防御になる。
- 3.エスケープの典型は、過剰な capability・特権コンテナ・ホストパスのマウント・カーネル脆弱性の悪用に集約される。共有カーネルである以上、カーネルのバグは即境界突破につながる。
コンテナ隔離は「分離」であって「境界」ではない
コンテナと仮想マシン(VM)の決定的な違いは、コンテナはホストと同じ一つの Linux カーネルを共有する点にあります。VM はハイパーバイザを介して各ゲストに独立したカーネルを与え、ハードウェア(VT-x など)が物理的なメモリ境界を強制します。一方コンテナは、ホストカーネルが提供する三つの仕組み、すなわち namespace(資源の見え方の分離)・cgroup(資源の使用量の制限)・capability/seccomp(権限とシステムコールの制限) を組み合わせて、プロセス群に「あたかも専有マシンに見える環境」を作り出す、いわば「カーネル機能による錯覚」です。
したがってコンテナ隔離の強度は、共有カーネルの堅牢性に上限を縛られます。ハイパーバイザの攻撃面(数十のハイパーコール)に対し、Linux のシステムコールは 300 以上あり、ここに一つでもカーネルバグがあれば隔離は破られます。これが「コンテナは VM ほど強い境界ではない」と言われる原理的な理由です。
namespace:資源の「見え方」を切り替える
namespace は、グローバルなカーネル資源をプロセスごとに別々のビューへ写像する仕組みです。同じシステムコールでも、所属する namespace が違えば見える対象が変わります。種類ごとに何を分離するかが要点です。
| namespace | 分離する資源 | 破られたときの影響例 |
|---|---|---|
| pid | プロセスID空間(コンテナ内 PID 1 など) | ホストの他プロセスが見え、シグナル送信が可能に |
| mnt | マウントポイント(ファイルシステムの木) | ホストのファイルツリーへのアクセス |
| net | ネットワーク(インタフェース・ルーティング・ポート) | ホストネットワークへの直接到達 |
| uts | ホスト名・ドメイン名 | 識別情報の偽装(影響は限定的) |
| ipc | System V IPC・POSIX メッセージキュー | 他コンテナの共有メモリへのアクセス |
| user | UID/GID の写像 | コンテナ内 root とホスト権限の分離が崩壊 |
| cgroup | cgroup 階層のルートビュー | ホストの cgroup 構成の露出 |
実装上、namespace は clone(2) / unshare(2) / setns(2) の三つのシステムコールで操作します。clone に CLONE_NEWPID などのフラグを渡して新しい namespace ごと子プロセスを生成し、unshare は呼び出し元自身を既存の namespace から切り離し、setns は /proc/[pid]/ns/ のファイルディスクリプタを使って既存の namespace へ参加します。重要なのは、namespace は資源を「隠す」だけで、カーネル内部のオブジェクトは一つだという点です。pid namespace を分けてもプロセススケジューラは単一で、全コンテナのプロセスが同じ実行キューを奪い合います。
pid namespace 内の PID 1 は、ホストの init と同じく「孤児プロセスの引き取り」と「特定シグナルのデフォルト無効化」という特別な意味を持ちます。コンテナのエントリポイントが孤児プロセスを回収(reap)しないと、ゾンビプロセスが溜まり続けます。これが軽量 init(tini など)を PID 1 に据える実務上の理由です。隔離とは別に、PID namespace のセマンティクスを理解していないと運用で足をすくわれます。
cgroup:資源の「使用量」を強制する
namespace が「何が見えるか」を制御するのに対し、cgroup(control group)は「どれだけ使えるか」を制御します。CPU 時間・メモリ・PID 数・ブロックI/O・デバイスアクセスなどをプロセスグループ単位で上限設定し、超過時にスロットリングや OOM kill を発動させます。cgroup v2 では全コントローラが単一の統一階層にまとまり、cgroup.subtree_control で子へ委譲するコントローラを選びます。
cgroup は隔離というより可用性の保護が主目的です。メモリ上限がなければ一つのコンテナの暴走(メモリ枯渇)がホスト全体を巻き込み、PID 上限がなければ fork 爆弾で他コンテナを巻き添えにします。つまり cgroup は機密性ではなく、資源枯渇という形の DoS を局所化するための仕組みです。逆に言えば、cgroup だけではファイルやネットワークへの不正アクセスは何も防げません。最小権限の原則と多層防御 の文脈では、cgroup は「一つの侵害が全体へ波及する半径を縮める」層として位置づけられます。
capability と seccomp:root の万能性を分解する
Linux の root(UID 0)は本来あらゆる特権操作が可能ですが、capability はこの万能権限を約 40 個の独立した権利に細分化します。コンテナはこのうち危険なものを落とし、最小限だけ残します。
CAP_SYS_ADMIN— 「もう一つの root」と呼ばれるほど広範。マウント、namespace 操作など多数を含み、これを残すとエスケープの足がかりになりやすい。CAP_NET_RAW— raw ソケット。ARP spoofing などに悪用されうる。CAP_SYS_PTRACE— 他プロセスへのptrace。同一 namespace 内の他プロセスのメモリ読み取りに繋がる。CAP_DAC_OVERRIDE— ファイル権限チェックの無視。
capability が「root のどの権利を持つか」を絞るのに対し、seccomp-bpf は「そもそもどのシステムコールを呼べるか」を呼び出し時点で制限します。seccomp はプロセスに BPF プログラムをアタッチし、各システムコールの番号と引数を検査して ALLOW / ERRNO(エラーを返す)/ KILL(プロセス殺害)などを返します。一度有効化すると解除できず、fork した子にも継承されます。
| 観点 | capability | seccomp-bpf |
|---|---|---|
| 制限する対象 | 特権操作の「権利」 | システムコールの「呼び出し」そのもの |
| 判定の粒度 | 操作カテゴリ単位(約40種) | syscall 番号+引数値 |
| 評価の場所 | 各 syscall 内部の権限チェック | syscall 入口(カーネルに入る直前) |
| 主な目的 | root 権限の過剰付与を防ぐ | カーネル攻撃面そのものを縮小する |
両者は補完関係です。capability は「許された操作でも危険なもの」を残しうる一方、seccomp は危険な syscall(keyctl、ptrace、mount、古い clone フラグなど)をカーネルに到達する前に弾けます。Docker のデフォルト seccomp プロファイルは約 44 個の syscall を遮断し、これだけで複数のカーネル脆弱性の悪用経路を実際に塞いできました。seccomp の本質はカーネルの攻撃面(reachable syscall)を減らすことにあり、共有カーネルという弱点を直接的に補強します。
拒否リスト(危険な syscall を個別に禁止)は、新しい syscall がカーネルに追加されるたびに穴が開きます。堅牢なのは許可リスト(必要な syscall だけを通し残りは全拒否)で、アプリが実際に使う syscall を strace や監査で洗い出して固定します。攻撃面の縮小という観点では、未知の syscall がデフォルトで閉じている許可リストが原理的に優れます。
ユーザー名前空間:コンテナ内 root を無力化する
最も強力な防御の一つが ユーザー名前空間(user namespace) です。これは UID/GID をコンテナ内とホストで別々に写像し、コンテナ内の root(UID 0)をホスト上の非特権 UID(例 100000)に対応づけます。コンテナ内では root に見えても、ホストカーネルから見れば一般ユーザーに過ぎません。
コンテナ内 UID ホスト実 UID
0 (root) --> 100000 (非特権)
1 --> 100001
...
(/proc/<pid>/uid_map に写像範囲を記述)
これにより、仮にコンテナ内 root がホストのファイルへ到達しても、ホスト UID 100000 の権限しか行使できず、/etc/shadow の書き換えのような操作は権限不足で失敗します。capability もこの user namespace の中でのみ有効になるため、コンテナ内で CAP_SYS_ADMIN を持っていてもホストの資源には及びません。これが「rootless コンテナ」の安全性の核心です。
ただし注意点があります。ユーザー名前空間の作成自体が CAP_SYS_ADMIN を非特権ユーザーに(namespace 内で)与えるため、過去には user namespace 経由でしか到達できないカーネルコードのバグが新たな攻撃面になりました。防御機能の追加が新たな攻撃面を生むという、セキュリティ設計の普遍的なトレードオフがここにも現れます。
コンテナエスケープの典型経路
エスケープとは、隔離を破ってホストの権限や資源を奪取することです。経路は大きく「設定の緩さ」と「カーネルの脆弱性」に分かれます。
| 経路 | 原理 | 対策 |
|---|---|---|
| 特権コンテナ(--privileged) | 全 capability 付与+seccomp/AppArmor 無効+全デバイスアクセス。隔離がほぼ無効 | 原則使わない。必要権限のみ個別付与 |
| docker.sock のマウント | コンテナ内からホストの Docker API を操作し、特権コンテナを新規起動できる | ソケットをマウントしない/プロキシで制限 |
| ホストパスのマウント | / や /proc、ホストの機微ファイルをマウントし直接読み書き | マウント範囲を最小化、read-only 化 |
| 過剰な capability | CAP_SYS_ADMIN 等で mount や namespace 操作が可能になり境界を越える | cap-drop=ALL の後に必要分だけ追加 |
| カーネル脆弱性 | 共有カーネルの LPE バグを突き、ホストカーネル権限を直接奪取 | カーネル更新+seccomp で syscall 攻撃面縮小 |
歴史的な実例として、runc の CVE-2019-5736 は、コンテナ内から実行中の runc バイナリ(ホスト側)を /proc/self/exe 経由で上書きし、次回 exec 時にホスト権限でコードを実行させる手口でした。これは namespace も cgroup も「正しく」機能していたにもかかわらず、ランタイム実装の欠陥で境界が破れた例で、隔離はカーネル機能だけでなくランタイムの実装品質にも依存することを示します。
コンテナの隔離は最終的にホストカーネルの完全性に帰着します。Spectre/Meltdown のような投機的実行のバグ(投機的実行に基づくマイクロアーキ攻撃 参照)や、キャッシュを介した サイドチャネル攻撃 は、namespace の論理的な壁を貫通してメモリ内容を漏らしうります。機密性が最重要な信頼境界(マルチテナントで相互不信のワークロード)では、コンテナ単体ではなく、軽量 VM(gVisor のユーザー空間カーネルや Kata Containers のマイクロVM)でカーネル自体を分離する判断が必要です。
「namespace は見え方を分離、cgroup は使用量を制限、両者は別物」が頻出。capability は root 権限の細分化、seccomp-bpf は syscall 呼び出しそのものの制限で、評価される場所(権限チェック内 vs syscall 入口)が違う点を区別できること。ユーザー名前空間はコンテナ内 root をホスト非特権 UID に写像する。エスケープは「特権コンテナ・docker.sock・ホストマウント・過剰 capability・カーネル脆弱性」に集約。VM との本質差は「カーネル共有か否か」。
まとめ
コンテナ隔離は、namespace(資源の見え方)・cgroup(資源の使用量)・capability/seccomp(権限と syscall) を重ねてプロセス群に専有マシンの錯覚を与える仕組みであり、VM のようなハードウェア境界ではありません。すべては単一の共有カーネル上で動くため、隔離の強度はカーネルの堅牢性に縛られます。
capability は root の万能性を細分化し、seccomp-bpf は到達可能な syscall を絞ってカーネル攻撃面を直接縮小し、ユーザー名前空間はコンテナ内 root をホスト非特権 UID に写像して権限を無力化します。これらは単独ではなく多層で重ねて初めて実用的な防御になります(最小権限の原則と多層防御)。エスケープの典型は設定の緩さ(特権コンテナ・ホストマウント・過剰 capability)とカーネル脆弱性に集約され、相互不信のマルチテナント環境では、コンテナを超えてカーネルごと分離する軽量 VM の採用が原理的な解になります。
セキュリティ Article
コンテナ分離の原理(namespace・cgroup・seccomp)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
コンテナ
比較で見る軸
難易度: advanced / カテゴリ: セキュリティ / タグ数: 6
導入後に効く点
root の万能権限は capability で細分化され、seccomp-bpf でシステムコール自体を絞り、ユーザー名前空間でコンテナ内 root をホスト上の非特権 UID に写像することで攻撃面を縮める。これらは併用して初めて実用的な防御になる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- セキュリティ
- タグ数
- 6
判断チェックリスト
- 自社の用途が「コンテナ / namespace」に近いか確認する。
- 強みである「コンテナは単一カーネルを共有し、namespace が資源の「見え方」を、cgroup が資源の「使用量」を分離する。カーネル自体は共有なので、VM のようなハードウェア境界による隔離ではない。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。