SeccompとLinux Capabilities(root権限の無力化工学)

2026-11-06 / シリーズ「コンテナとLinuxカーネルの物理」第5話 / Docker, Linux, セキュリティ, Seccomp, Capabilities, カーネル

Dockerコンテナの中で whoami や id を叩くと、誇らしげに以下の文字が出力されます。

root
uid=0(root) gid=0(root) groups=0(root)

ユーザーIDは「0」。正真正銘のスーパーユーザー(root)です。 UNIXの伝統的な常識に従えば、rootとは「神」であり、OS内のあらゆるファイルを削除でき、あらゆるハードウェアを操作し、システムを意のままに再起動できる絶対君主のはずです。

しかし、そのコンテナ内のroot権限を使って、ホストマシンの時刻を変更しようとしたり、ネットワークカードを再起動しようとすると、無情なエラーが突きつけられます。

# 時刻を変更しようとする
date -s "2026-11-06 12:00:00"
date: cannot set date: Operation not permitted

# ハードウェア時計にアクセスしようとする
hwclock
hwclock: Cannot access the Hardware Clock via any known method.

「root(管理者)なのに、Operation not permitted(権限がありません)とは一体どういうことなのか?」 「UID 0 のはずなのに、なぜホストマシンのカーネルを破壊したり、物理マシンを再起動できないのか?」

この矛盾を解く鍵は、Linuxカーネルが長年にわたるセキュリティ侵害の歴史の中で編み出した2つの防御境界――**特権を分子レベルに粉砕した「Linux Capabilities」**と、危険なシステムコールを門前払いする「Seccomp(Secure Computing Mode)」の物理にあります。


1. 絶対君主の死:root特権を40個の「分子」に解体する

古典的なUNIXにおいて、権限モデルは恐ろしいほど原始的でした。 「一般ユーザー(UID > 0)」か、「絶対権力者root(UID = 0)」かの**オール・オア・ナッシング(0か100か)**の二者択一しかなかったのです。

この仕組みには致命的な欠陥がありました。 例えば、Webサーバー(Nginx等)が特権ポートである「80番ポート」をリッスンするためだけに、プロセス全体をroot権限で動かさざるを得ませんでした。もしそのWebサーバーにバッファオーバーフローの脆弱性が1つでもあれば、攻撃者は一瞬でサーバー全体の完全な支配権(神の権力)を手に入れてしまいます。

この「絶対君主制」を解体するために、Linuxカーネル2.2で導入されたのが**「Linux Capabilities(ケーパビリティ)」**です。

👑 古典的UNIX:単一の絶対特権 root(UID 0) 1箇所破られたら、システム全体が即座に陥落! 約40個の微小な権能に粉砕 CAP_NET_BIND_SERVICE 1024番未満のポートを開く権利 CAP_SYS_TIME システムの時刻を変更する権利 CAP_SYS_ADMIN マウント・名前空間を操る準神特権 CAP_NET_ADMIN IPルーティング・NICを設定する権利 CAP_SYS_RAWIO 物理ストレージを直接叩く権利 CAP_CHOWN / CAP_SETUID 所有者・実行権限を変更する権利

コンテナ内のrootは「去勢されたroot」である

カーネル内部において、プロセスの実行権限は uid == 0 という数字ではなく、プロセス構造体(task_struct->cred)に保持された**「ビットマスク(フラグの集まり)」**によって判定されます。

Dockerはコンテナを起動するとき、たとえコンテナ内のプロセスが uid=0 (root) を名乗っていようとも、**ホストを破壊しうる危険なCapabilitiesをすべて剥奪(ドロップ)**した状態でプロセスを産み落とします。

デフォルトで許可されているのは、以下のごく一部の安全な権能だけです。

一方で、以下のような「牙」は最初から完全に抜かれています。

コンテナ内のrootが「Operation not permitted」で弾かれるのは、カーネルがUIDではなく**「お前はrootを名乗っているが、CAP_SYS_TIME のビットフラグを持っていない」**という冷徹なビット判定を下しているからなのです。


2. 禁忌の扉を開ける:--privileged が「悪魔のオプション」と呼ばれる理由

開発現場でコンテナが権限エラーで動かないとき、初心者がやってしまいがちな最悪の悪手があります。 それが、docker run --privileged というオプションの付与です。

このオプションを付けた瞬間、何が起きるでしょうか?

通常のコンテナ ⚠️ --privileged コンテナ(監獄崩壊) コンテナ内 root コンテナ内 root Capabilitiesの大半を剥奪 全Capabilities(約40個)を全付与! 🔒 安全な箱庭の中だけに限定 ホストOSの全権限を掌握 そのまま脱獄へ 💥 コンテナ脱獄(Container Escape) ホストの /dev/sda を直接マウント・破壊可能!

--privileged の物理的破壊力

--privileged を指定すると、Dockerエンジンは以下の安全装置をすべて同時に解除します。

  1. 約40個すべてのLinux Capabilitiesを完全付与(CAP_SYS_ADMIN を含む神の権力の復活)。
  2. ホストのすべてのデバイスノード(/dev/*)へのアクセスを開放。
  3. AppArmorやSELinux、Seccompの保護フィルタをすべて全停止。

この状態になったコンテナは、もはや隔離された箱庭ではありません。 コンテナの中から mknod でホストの物理ディスク(/dev/sda)のデバイスファイルを捏造し、それをコンテナ内にマウントすれば、**ホストOSのルートファイルシステム(/)の全データを1秒で書き換え、ホストのホスト名を奪い、カーネルを破壊(コンテナ脱獄:Container Escape)**できます。

「動かないから --privileged をつける」とは、金庫の暗証番号が合わないからといって、金庫の扉をダイナマイトで粉砕して街中に放り出すに等しい狂気の行為なのです。


3. 門番の物理:Seccomp(Secure Computing Mode)の防衛線

Capabilitiesによって、rootの「身分(権力)」は縛り上げられました。 しかし、これだけではまだ防ぎきれない最深部の急所があります。

それが、**「カーネルの脆弱性(Kernel Exploit)」**です。

Linuxカーネルには、プロセスがOSの機能を利用するための窓口である**「システムコール(System Call)」が、およそ450種類以上**も存在します(read, write, open, fork, clone など)。 Capabilitiesのチェック機構自体も、カーネルのコードの中に書かれたC言語のプログラムです。もし、マイナーで滅多に使われないシステムコールの実装に未知のバッファオーバーフローやメモリ破損のバグ(ゼロデイ脆弱性)が存在したらどうなるでしょうか?

プロセスはそのシステムコールに異常な引数を叩き込むことで、Capabilitiesの判定ロジックごとカーネルをハイジャックしてしまいます。

この「最後の砦」を守るために作られたのが、**Seccomp(セックコンプ:Secure Computing Mode)**です。

📦 コンテナ内プロセスがシステムコールを発行 🛂 Seccompフィルタの検問(BPFが番号と引数を判定) ✅ 許可リストの日常命令 read, write等 → カーネル本体へ通す ⛔ 禁止リストの危険命令 reboot等 → 即拒絶(EPERM/SIGSYSで終了)

BPF(Berkeley Packet Filter)を応用した「システムコールの関所」

Seccompの正体は、ネットワークのパケットフィルタリングで使われていた「BPF(cBPF)」の技術を、**「システムコールのフィルタリング」**に応用したものです。

プロセスがCPUの特権モード(リング0)へ移行してシステムコールを呼ぼうとした瞬間、カーネルはシステムコール番号とその引数を「パケット」と見なし、事前に登録された**小さなルール(BPFバイトコード)**を実行します。

Dockerのデフォルトプロファイル(Default Seccomp Profile)は、全450個以上あるシステムコールのうち、**危険な約40個以上のシステムコールを完全に遮断(ブロック)**しています。

ブロックされる代表的システムコール本来の機能遮断される理由
rebootシステムの再起動コンテナが勝手にホストマシン全体を再起動するのを防ぐ。
kexec_load新しいカーネルのロード・実行コンテナ内からホストのOSカーネルそのものをすり替える攻撃を阻止。
sys_chrootルートディレクトリの変更chroot監獄の脱獄テクニックによるファイルシステム脱出を防ぐ。
ptrace他プロセスのデバッグ・メモリ改ざん別コンテナやホストのプロセスのメモリ空間をハッキングするのを防ぐ。
bpfカーネル内BPFプログラムのロードカーネルの深部に不正な追跡・変更コードを注入されるのを防ぐ。

コンテナ内でどんな凶悪なマルウェアがroot権限で暴れ回ろうとも、reboot や kexec_load のシステムコールを発行した瞬間に、Seccompが首根っこを掴んで Operation not permitted(EPERM) で叩き落とします。 カーネルの脆弱性がある深い部屋へ至る「扉」そのものを、コンクリートで埋め立ててしまう物理防御です。

触って試す:Seccomp門番の体験

🛂 Seccomp門番の体験(Dockerデフォルトプロファイル)

コンテナ内プロセスのつもりで、システムコールのボタンを押してみてください。カーネル本体に届く前に、Seccompの門番が許可リストと照合します。日常命令は素通りですが、危険な命令はカーネルの脆弱性を突く隙すら与えられず、門前払いされます。

門番「システムコールを発行してみてください。全450種のうち、危険な約40種は通しません」


4. Linux標準コマンドで体感する:CapabilitiesとSeccompの実験

この多重防衛の物理は、手元のLinux環境で簡単に実験・確認できます。

実験①:rootから特権を剥奪してみる(capsh)

Linuxには、現在のプロセスのCapabilitiesを表示・操作する capsh コマンドが用意されています。

# 現在の権限を確認してみる(rootで実行)
sudo capsh --print

出力を見ると、Current: =ep、あるいは cap_chown,cap_dac_override... と、40個近い特権がズラリと並んでいるのが見えます。 では、ここから**「時刻変更の特権(cap_sys_time)」だけを剥奪したシェル**を起動してみましょう。

# cap_sys_time を抜いた状態で bash を起動
sudo capsh --drop=cap_sys_time -- -c "date -s '2026-11-06 12:00:00'"

結果はどうなるでしょうか?

date: cannot set date: Operation not permitted

sudo(root)で実行しているにもかかわらず、時刻の変更が確実に拒絶されました。 「UID 0 であっても、Capabilityのフラグがなければ無力である」というカーネルの物理が証明された瞬間です。

実験②:DockerのデフォルトSeccompを暴く

Dockerコンテナを立ち上げ、Seccompによってブロックされているシステムコール(例えばホスト再起動)を直接叩いてみます。

# 通常のコンテナを起動
docker run --rm -it alpine sh

# コンテナ内で、rebootコマンド(rebootシステムコール)を実行
/ # reboot
reboot: Operation not permitted

rootでありながら、rebootは弾かれます。 では、もし Seccomp を無効化(--security-opt seccomp=unconfined)して起動したらどうなるでしょうか? Capabilitiesで CAP_SYS_BOOT が止められているため依然として弾かれますが、「Seccomp(門前払い)」➔「Capabilities(身分検査)」という二重の防壁によって、コンテナの暴走は水際で食い止められているのです。


5. 多層防御(Defense in Depth)の全体像:コンテナを閉じ込める4つの檻

ここまで連載で解剖してきたすべての技術を統合すると、Linuxコンテナという技術が、いかに用意周到な**「多層防御(Defense in Depth)」の幾何学**の上に成り立っているかが分かります。

😈 攻撃者:コンテナ内で root を奪取! 第1の檻 Namespaces:視野の遮断(他人のプロセスやホストが見えない) 第2の檻 cgroups:胃袋の拘束(CPU・メモリの上限を縛る) 第3の檻 Capabilities:権力の去勢(rootでも危険な特権を剥奪) 第4の檻 Seccomp:窓口の閉鎖(危険なシステムコールを門前払い) ★ たった1つのホストOSカーネルが、絶対に傷つかない!
  1. 第1の檻:Namespaces(第1話) ホストのプロセス、ファイル、ネットワークを見えなくさせ、物理的な接触面を消滅させる。
  2. 第2の檻:cgroups(第2話) CPUとメモリの上限を縛り、資源枯渇攻撃による共倒れを物理的に防ぐ。
  3. 第3の檻:Linux Capabilities(第5話) たとえUID 0(root)を奪われても、ハードウェアやカーネル設定を変更する牙をすべて抜いておく。
  4. 第4の檻:Seccomp(第5話) カーネルの脆弱性を突く怪しいシステムコールそのものを、BPFフィルタで門前払いする。

コンテナの安全性とは、単一の頑丈な壁(ハイパーバイザ)によって保たれているのではありません。 **「薄いが性質の異なる4枚のフィルターを重ね合わせることで、あらゆる攻撃ベクトルを確率的にゼロへ追い込む」**という、極めて現代的なソフトウェア工学の勝利なのです。


結論:セキュリティとは「権力を細分化する美学」である

かつて、コンピュータの世界では「信用できる管理者」にすべての鍵を預けるのが当たり前でした。 しかし、ネットワークが世界中と繋がり、無数のオープンソースコードやサードパーティ製コンテナを組み合わせて動かす現代、その前提は完全に崩壊しました。

「誰も信用しない(ゼロトラスト)」。 コンテナのセキュリティ物理が教えてくれるのは、この冷徹な原則です。

rootという名前の看板に惑わされず、

この精緻な無力化工学があるからこそ、私たちは世界中からダウンロードしてきた正体不明のコンテナイメージを、同じホストマシンの上で安心して同時に動かすことができるのです。

さて、Namespaces、cgroups、OverlayFS、vethネットワーク、そしてCapabilitiesとSeccomp。 コンテナを構成する「すべての物理部品」が、私たちの手元に揃いました。

次回、新企画Hの最終回となる第6話。 **「Dockerを完全にアンインストールし、Linuxの標準コマンドとシェルスクリプト数十行だけで、本物のDockerコンテナを一から自作する(最小コンテナランタイム自作)」**という究極の実証実験に挑みましょう。


📝 3行で頭に入る『試験・実務・投資の要点まとめ』

  1. 【Capabilitiesの物理】 root(UID 0)の絶対権力を約40個の特権ビットに解体。Dockerは CAP_SYS_TIME や CAP_SYS_ADMIN 等の危険な権能をデフォルトで剥奪してrootを去勢している。
  2. 【Seccompの門番】 カーネルの脆弱性を突く攻撃を防ぐため、BPFフィルタを用いて約450個のシステムコールのうち危険な約40種(reboot等)をカーネル到達前に門前払いで遮断する。
  3. 【—privilegedの危険性】 --privileged は全Capabilities付与・全デバイス開放・Seccomp全停止を行うため、コンテナ脱獄(ホスト破壊)を招く実務上極めて危険な劇薬である。

確認クイズ

📝 確認クイズ(3問)