をセットした。 Linuxのキャパビリティ を導入し、最小限の原則に基づいてサーバーサービスを運用することで、絶対に必要な部分的な権限のみを付与するようにしています。これにより、 アタック・サーフェス 機能に支障をきたすことなく、はっきりと感じられる。.
中心点
- 最低限の特権 一貫性:各サービスには、必要とされる能力のみが割り当てられる。.
- 微粒状 Rootの代わりに:約40~50のCapabilitiesがフルアクセス権に代わる。.
- 分離 プロセス間:権限の分離により、エクスプロイトによる被害を軽減する。.
- ファイル 機能:権限をバイナリファイルに直接紐付ける。.
- 監査可能: getcap は、特権に関する明確な情報を提供します。.
なぜルート権限は危険なのか――そして「Capabilities」がそれをどう変えるのか
以前は、ほぼすべてのサーバーサービスが ルート権, これにより、システムが侵害された場合、即座にシステムが乗っ取られる可能性がありました。現在、私は次のようなキャパビリティを用いて、権限を適切に割り当てています。 CAP_NET_BIND_SERVICE 1024未満のポートについては権限を付与し、それ以外のすべての強力な権限を削除する。これにより、Webサーバーはポートにバインドすることはできるが、カーネルモジュールをロードしたり、ファイルの所有者を変更したりすることはできなくなり、これにより セキュリティ を大幅に引き上げます。役割を明確に区分することで、攻撃の有効性は低下します。なぜなら、悪用されたプロセスが実行できる操作には制限があるためです。この概念にさらなる構造を持たせたい場合は、権限を非常に細かく きめ細かく分割する こうして、重要な操作を体系的に制限します。これにより、モノリシックなルートサービスが、最小限かつ明確に定義された権限を持つ一連のサービスへと生まれ変わります。.
カーネルにおけるキャパビリティセットの仕組み
どのプロセスにもいくつもの 機能セット, 、カーネルが重要な操作を行う際にチェックするものです。Effective-Setは、プロセスが現在直ちに実行できることを決定する一方、Permitted-Setには、許可される可能性のある権限のプールが含まれています。Inheritable-Setを通じて、私は execve() 子プロセスに引き継がれるものであり、これは特にラッパーや起動スクリプトにおいて重要な点です。バウンディング・セットは厳格な上限を定義するため、アプリケーションでエラーが発生した場合でも、特定の権限が再び取得されることはありません。アンビエント・セットを使用することで、SUIDなしで通常のプログラムに権限を付与し、 攻撃ルート 小さい。これらのセットを組み合わせることで、UID 0の従来の「全か無か」という方式をはるかに超えた、非常にきめ細かな制御が可能になります。.
| セット | 目的 | 代表的な使用例 | 設定ミスのリスク |
|---|---|---|---|
| 有効 | 現在有効なスキル | 各特権操作の検証 | プロセスがすぐに過剰になる可能性がある |
| 許可済み | 使用可能なスキルのプール | エフェクティブ・セットの出典 | 不要な余剰資金は手元に残る |
| 継承可能 | 遺伝する能力 | execve() における制御された引き渡し | 子供たちは不必要に権利を相続してしまう |
| バウンディング | あらゆる権利の上限 | 永続的な除外を定義する | 強力な権利を取り戻すことが可能 |
| アンビエント | SUIDなしで転送 | 通常のプログラムに機能が付与される | より広範で、目立たない権利付与 |
実務上、さらに2つの点が重要となります。第一に、決定するのは Securebits ユーザーIDの変更後(例: setuid()) その機能を維持する。これにより、 PR_SET_KEEPCAPS これを意図的に制御することが可能です。典型的な手順は、一時的にroot権限で起動し、必要なソケットやリソースを作成した後、UIDを特権のないユーザーに変更し、必要なキャパビリティのみを引き継ぐというものです。第二に、次のことが当てはまります:その バウンディングセット これは現在のプロセスフローに完全に組み込まれています。ここで起動パス初期段階で不要な権限を削除しておけば、後で設定ミスがあっても「許可されていない」権限を取得することはできなくなります。.
ファイル権限をファイル機能で制御する
一時的なサービスではなく、永続的な 特別権限 それを認めるなら、むしろバイナリファイルに直接バインドしたほうがいい。以下の方法を通じて setcap cap_net_bind_service=+eip /usr/bin/node プロセスをrootとして実行する必要なく、ポートバインディングを許可します。次のようにして getcap /usr/bin/node あるいは再帰的に getcap -r / 2>/dev/null 割り当て状況を確認し、管理を維持します。削除は以下から行えます setcap -r /バイナリへのパス/, 、そのため、作業終了後は一時的な権限を取り消します。コピーを行うとキャパビリティが失われることが多いため、デプロイ時に明示的にそれらを保存し、 回帰 回避する。そうすることで、ビルドの再現性が確保され、権限についても常に追跡可能な形で文書化される。.
ファイル機能は拡張属性として存在します(security.capability) ファイルシステム上で。これには、対応するファイルシステムと適切なマウントオプションが必要です。次のようなツール: タール そして 同期 XAttrsを明示的に含める必要があります(例:. tar --xattrs, rsync -XA)、そうしないと権限が黙って消えてしまいます。パッケージマネージャーはインストール後の手順でキャパビリティを設定できますが、アップグレード時の予期せぬ事態を避けるため、私はビルド/リリースプロセスでこれを固定することを推奨します。 また、重要な点として、インタプリタスクリプト(例:シェバン付き)は、ELFバイナリとは異なり、ファイルキャパビリティを継承しません。強力なキャパビリティを 通訳者 設定するのはそもそもリスクが高いので、私はむしろ切り離して、専用の小さな補助バイナリを使って作業する方が好きだ。.
サーバーサービスにおける最小限の原則の実践
私はWebサーバーを特権のないユーザーとして起動し、以下の権限のみを付与しています CAP_NET_BIND_SERVICE, これにより、プロセスが 80/443 にバインドできるようになり、それ以上の 特典 を備えています。ファイルやディレクトリについては、引き続きPOSIX権限およびオプションでMACプロファイルを用いて制御しており、これにより設定とコンテンツが別々に保護されます。監視やロギング用のエージェントには、必要なネットワーク権限とログへの読み取り権限のみを付与し、システム変更に関する権限は一切与えません。 コンテナ環境では、さらにキャパビリティセットを絞り込み、システムコールフィルターと組み合わせることで、動作を厳格に制限しています。この組み合わせにより、エクスプロイトが成功した場合の影響を軽減し、 透明性 実際の権限。サービスは機能し続けるが、行動の余地は限られている。.
Capabilitiesを割り当てる代わりに、時にはそれらを完全に排除することもあります。ソケットのアクティベーションでは、Initプロセスを通じて特権付きリスナー(例:443/tcp)を提供し、サービスには開かれたファイル記述子のみを引き渡します。 そうすれば、アプリケーションプロセスは CAP_NET_BIND_SERVICE さらに。同様に、一度きりのroot操作(例:PIDディレクトリの作成など)を事前に済ませておき、その後は一貫して権限を返還することも可能です。権限が少なければ少ないほど そもそも が関与しているほど、システムは連鎖エラーに対してより堅牢になります。.
特権の分離を適切に実施する
大規模なサービスをいくつかの サブプロセス, 、それぞれが必要な権限のみを保持しています。フロントエンドプロセスはTLSを終了させ、ポートにバインドしますが、重要な変更を行うためのファイルシステム権限は持ちません。 バックエンドプロセスは内部でデータを処理し、設定ファイルに対する最小限の読み取り権限のみを持ち、独自のネットワーク機能を持たずにデータベースと通信します。ログローテーションやメンテナンスなどの管理ジョブは、権限が時間制限付きの専用ツールを通じて実行されます。攻撃者がシステムの一部を攻撃しても、残りのシステムは影響を受けません。なぜなら、 認可 厳密に定義されている。このように、セキュリティは、無制限のシステム権限ではなく、アプリケーションの構造に合わせて拡張される。.
この分割には、明確な起動オーケストレーションが適しています。従来の構成では、これをスーパーバイザーが担当しますが、現在のシステムでは、Capabilities、cgroups、ネームスペースを直接統合している systemd を好んで使用しています。 これにより、ネットワークフロントエンド、ワーカー、管理ツールをそれぞれ独自のサンドボックスで起動し、リソースを制限し、障害発生時には自動的に再起動させることができます。しかも、root権限を一律に付与する必要は一切ありません。.
セキュリティ制御の組み合わせ:POSIX、MAC、およびCapabilities
Capabilitiesは、従来の方法と組み合わせたときに最も効果を発揮します。 ファイルのアクセス権 とMACシステムを組み合わせます。SELinuxやAppArmorは、割り当てられたキャパビリティにもかかわらずアクションをさらに制限し、多重の保護を実現します。例えば、あるプロセスはポートにバインドすることはできますが、ポリシーによって機密ファイルの読み取りは阻止されます。 これらのアプローチの違いについてさらに詳しく知りたい方は、以下の記事で明確な比較が掲載されています。 SELinux 対 AppArmor そして、適切なポリシー戦略を選択することができます。その結果、複数のレベルで攻撃を阻止し、 アタック・サーフェス さらに縮小されました。これにより、権限の付与は検証可能、再現可能、かつ一貫性を保つことができます。.
さらに、私が追加で NoNewPrivileges 有効化:これにより、プロセスや子プロセスは新たな特権(SUIDや新たに設定されたファイルキャパビリティなど)を取得できなくなります。厳格なキャパビリティ・バウンディング・リストと組み合わせることで、設定に誤りがあった場合でも、その後の特権の拡大を防ぐセキュリティ上の障壁が形成されます。.
機能を確実に割り当て、監査を行う
割り当てられた スキルセット できるだけ小さくし、「セカンドルート」を連想させるものはすべて避けること。例えば、 CAP_SYS_ADMIN. Python、Perl、シェルといったインタプリタには、その機能範囲が悪用されやすいため、強力な権限は付与されません。定期的な監査を通じて getcap -r / 2>/dev/null 異常なファイルを検出して整理します。Capabilities を持つバイナリファイルは書き込み保護されており、root が所有し、一般ユーザーが変更できるパスには存在しません。さらに、リリース前には独自のバイナリを毎回チェックし、変更内容を記録して、 レビュー そして、複製が確実に機能する。そうすることで、権利の付与を管理しやすくし、変更の経緯も追跡可能になる。.
実行時に、以下の方法でプロセスをチェックしています。 /proc//ステータス (フィールド CapEff, CapPrm, CapInh). これにより、アクティブなセットの16進数が取得され、アプリケーションが想定以上の機能を持っているかどうかが即座にわかります。次のようなツールなど capsh --print 或いは getpcaps デバッグを容易にする。Linux監査サブシステムを使用することで、さらに、Capabilitiesや security.capability-ファイルの属性を利用して、改ざんを追跡します。Capabilitiesを設定オブジェクトとして扱い、変更内容を厳格にレビューすることで、監査を再現可能にし、コンプライアンスの証明を簡素化できます。.
よくある落とし穴とそれを回避する方法
よくある落とし穴:コピーする際に 属性 失われてしまい、その結果、サービスが突然起動しなくなったり、逆に制限が不十分になったりすることがあります。そのため、私はビルド段階でキャパビリティを明示的に確保するか、インストール後のステップで自動的に割り当てるようにしています。 もう一つの間違いは、必要以上に権限を開放してしまう汎用的なCapabilitiesを多用することです。むしろ、次のような具体的なCapabilitiesを割り当てる方が望ましいです。 CAP_NET_RAW 或いは CAP_CHOWN 実際に機能を発揮する場所でのみ使用するようにしています。アンビエント・セットも、意図しない効果が生じないように、控えめに使用しています。 継承 広まっています。意図的に数を減らし、定期的に確認を行えば、操作ミスによるセキュリティ上の脆弱性を防ぐことができます。.
また重要な点として、SUIDバイナリを体系的に廃止すること。以前はSUIDが必要だった場面(ICMPの送信など)でも、多くの場合、 CAP_NET_RAW 処理を行う――あるいは、さらに良いのは、その機能を、要件が極めて厳格に定義された、可能な限り最小限のヘルパープロセスに外注することです。また、一時的なパスやユーザーが書き込み可能なパスにCapabilitiesを配置することは避けています。 厳格な所有権およびデプロイ体制(Root:root、0755/0555、変更不可能なパス)を確立することで、バイナリが置き換えられたことによる権限の「喪失」を防ぐことができます。.
コンテナにおける機能とDevSecOps
コンテナ環境では、私は 能力 徹底的に整理し、ワークロードに必ずしも必要ではないものはすべて削除します。さらに、 Seccompプロファイル これにより、リスクの高いシステムコールをブロックし、さらなる障壁を設けることができます。ビルドパイプラインでは、Capabilitiesを宣言的に定義し、ステージング環境でテストを行い、バージョン管理下に記録します。 これにより、最小権限の原則を証明し、権限の変更を漏れなく文書化できるため、コンプライアンスの向上につながります。こうして、コンテナのタスクを妨げることなく厳格に管理され、 アタック・サーフェス サイズが小さくなります。必要な要素のみを含む画像と組み合わせることで、セキュリティがさらに向上します。.
コンテナの文脈で重要な点:Capabilitiesはネームスペース内に存在する 相対的. ユーザーネームスペース内では、プロセスが「root」権限を持つことは可能ですが、その権限は関連するネームスペースにのみ及ぶため、影響範囲は大幅に限定されます。その一方で、「„--特権“「」は事実上常にタブーです。これはハードバウンディングの制限を無効にし、必要以上に範囲を広げてしまうからです。そのため、私はデフォルトで「すべてドロップ、対象を指定して追加」の設定でコンテナを起動し、さらに NoNewPrivileges, 、cgroupのリミット、および読み取り専用マウント。リスニングのみを行うサービスについては、追加のキャパビリティを一切使用せずに済むよう、ソケットアクティベーションまたはサイドカーを利用しています。.
systemd の例:Capabilities を宣言的に制限する
サービス・ユニットでは、プロセスが許容される上限を、明確かつ再現可能で、バージョン管理可能な形で定義します。ポート443にのみバインドでき、それ以外は厳しく制限されているWebサービスの簡潔な例を以下に示します:
[Unit]
Description=root権限不要の最小限のWebサービス
[Service]
User=web
Group=web
ExecStart=/usr/bin/my-web
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/my-web
RestrictAddressFamilies=AF_INET AF_INET6
SystemCallFilter=@basic-io @network-io
LockPersonality=yes
MemoryDenyWriteExecute=yes
[Install]
WantedBy=multi-user.target
の組み合わせである。 AmbientCapabilities そして厳しい CapabilityBoundingSet このサービスが、必要な機能のみを受け取り、それ以上のものは一切受け取らないようにします。. NoNewPrivileges 事後の特権の格上げを防止し、, ProtectSystem そして ReadWritePaths 書き込みアクセス制御を行い、厳格なシステムコールフィルタによって不要なカーネルへの呼び出しを阻止します。.
よく使われる機能――そして安全な代替案
- CAP_NET_BIND_SERVICE: ポート番号1024未満にバインドする。代替案:ソケットの有効化、リバースプロキシを前段に配置する。.
- CAP_NET_RAW: ローソケット(Ping、DHCP)。代替案:広範なインタプリタ権限の代わりに、小さなヘルパープロセスを使用する。.
- CAP_CHOWN/CAP_FOWNER: 所有者/ACLの設定変更。代替案:あらかじめ用意されたディレクトリ、専用のメンテナンスツール。.
- CAP_SYS_PTRACE: デバッグ/トレース – ステージング環境でのみ実施し、本番環境では決して広範囲に展開しない。.
- CAP_SYS_ADMIN:「第二のルート」――避ける;本当に必要なものを具体的に明確にする。.
私は常に、必要な機能を正確に有効にする最小限の権限を選択するようにしています。ある機能(例:RAWソケット)が複数の攻撃経路を開く可能性がある場合は、その機能を独立した短命なプロセスにカプセル化し、処理が完了したら権限を撤回します。.
堅牢な能力のための実践チェックリスト
- このサービスはroot権限なしで起動しますか?もしできない場合、その理由は何ですか?また、ソケットの有効化や小さな補助バイナリを使って解決することは可能ですか?
- そうなのか? すべて 割り当てられた機能について、その必要性が実証されているか(機能検証、テストケースなど)?
- バウンディングセットは、可能な限り狭く、かつ早い段階で設定されているか?
- ビルド、デプロイ、バックアップの際、XAttrsは一貫して保持されていますか(rsync/tarのフラグ、パッケージスクリプトなど)?
- インタプリタやSUIDバイナリについては、Capabilitiesを徹底して使用しないようにしていますか?
- 所有者権限およびファイル権限(Root:root、0755/0555)ならびにパスは、変更から保護されていますか?
- 追加の制御(NoNewPrivileges、Seccomp、MACプロファイル)は有効か?
- プロセス・キャパビリティは実行時に監査されるか(
/proc//ステータス, getpcaps)や変更点は記録されていますか? - コンテナはデフォルトで「drop all, add minimal」に設定され、「privileged」は設定されていないのでしょうか?
簡単にまとめると
リナックス Capabilitiesは、従来のroot権限を小さく管理しやすい単位に分解し、それによって最小権限の原則を技術的に適切に実装します。各サービスには、本当に必要な機能のみを割り当て、これをPOSIX権限やMACポリシーと組み合わせています。 ファイル・キャパビリティにより、権限がバイナリに直接紐付けられ、監査によって誰が何を許可されているかが明確に示されます。特権分離、コンテナ権限の制限、システムコールフィルタリングを組み合わせることで、脆弱性が悪用された場合の被害を最小限に抑えます。 定期的な点検、厳格な所有権および書き込み権限、そして文書化されたリリースプロセスにより、権限の付与を最小限に抑えています。これにより、サーバーサービスは機能し続けますが、 操縦の余地 攻撃者に対する防御は、一貫して最小限に抑えられている。.


