Linuxサーバーを運用していると、「サービスが正常に動いているか」に意識が向きがちです。しかし「サービスが攻撃された後、被害をどこまで封じ込められるか」を考えている現場は、まだ少ないのが実情です。
systemdには、サービスごとの動作環境を隔離する強力なセキュリティ機能が組み込まれています。PrivateTmp、NoNewPrivileges、ProtectSystem——これらの設定指令(Directive)を正しく適用するだけで、サービスが侵害されても被害をそのプロセス内に閉じ込めることができます。
この記事では、systemdのセキュリティ指令を使ったサンドボックス化の実践手順を、コピペで使える設定例とともに解説します。追加のツール導入は不要です。
systemdサービスのサンドボックス化とは?
サンドボックス化とは、プログラムの動作範囲を制限し、システムの他の部分への影響を遮断する技術です。ブラウザのタブがクラッシュしても他のタブに影響が出ないのと同じ考え方で、systemdでは各サービスを「隔離された箱」の中で動かします。
systemd(Linuxの標準的なinitシステム)は、バージョン209以降からサービス向けのセキュリティ指令を段階的に拡張してきました。現在では30種類以上のセキュリティ関連指令が用意されており、RHEL 7/Ubuntu 16.04以降の多くのディストリビューションで利用できます。
特に重要なのは、これらの設定が「追加ツール不要」「サービス側のコード変更不要」で適用できる点です。既存サービスの .service ファイルに数行追加するだけで有効になります。
| 指令名 | 効果 | 難易度 |
|---|---|---|
| PrivateTmp | /tmp と /var/tmp をサービス専用の名前空間に隔離 | 低(副作用ほぼなし) |
| NoNewPrivileges | setuid/setgid による権限昇格を禁止 | 低(多くのサービスで問題なし) |
| ProtectSystem | /usr、/boot 等のシステム領域を読み取り専用化 | 低~中 |
| ProtectHome | /home、/root、/run/user を見えなくする | 低(多くのサービスで問題なし) |
| CapabilityBoundingSet | 使用可能なケーパビリティを最小限に制限 | 中(サービスに応じた調整が必要) |
| RestrictAddressFamilies | ソケット通信プロトコルを制限 | 中(通信要件を把握する必要あり) |
サービスが突破されると何が起きるか
サンドボックス化の重要性を理解するために、攻撃者の動きを把握しておきましょう。攻撃手法を知ることは、正しい防御を設計するために欠かせません。
たとえば、古いバージョンのWebアプリに脆弱性があり、攻撃者がWebサーバープロセス(www-data や nginx ユーザー)でコマンドを実行できたとします。サンドボックス化されていない環境では、次のような横展開が可能になります。
・/tmp へのマルウェア配置: 攻撃ツールを /tmp に書き込み、実行するルートとして悪用される
・他サービスの設定ファイル読み取り: /etc/mysql/my.cnf などの資格情報が漏洩する可能性がある
・ホームディレクトリの探索: /home/admin/.ssh/id_rsa などのSSH秘密鍵が盗まれるリスクがある
・setuid バイナリによる権限昇格: root 権限を奪ってシステム全体を乗っ取られる恐れがある
サンドボックス化により、これらの動きを「サービス自身が侵害されても」封じ込めることができます。完全に侵入を防ぐわけではありませんが、被害の拡大を食い止める「被害局所化(コンテインメント)」の重要な手段です。
サンドボックス化の具体的な設定手順
設定はサービスの Unit ファイルに [Service] セクションを追記して行います。システム付属のファイルを直接編集するのではなく、systemctl edit コマンドでオーバーライドファイルを作成するのがベストプラクティスです。
# オーバーライドファイルを作成(例: nginx の場合) sudo systemctl edit nginx # 上記コマンドで /etc/systemd/system/nginx.service.d/override.conf が開く # 編集後は自動で保存される # 設定を反映する sudo systemctl daemon-reload sudo systemctl restart nginx
1. PrivateTmp でテンポラリ領域を分離する
最も副作用が少なく、すぐに適用できる設定です。このディレクティブを有効にすると、そのサービスだけが使える専用の /tmp と /var/tmp が作成されます。システム共通の /tmp への読み書きはできなくなるため、攻撃者が /tmp を踏み台にする手口を封じられます。
[Service] # サービス専用の /tmp を作成する(攻撃ツールの配置・共有を防ぐ) PrivateTmp=true
サービスが /tmp を一時ファイル置き場として使うケースでも問題は起きません。サービスは自分専用の /tmp を引き続き使えます。ただし、別プロセスから /tmp を通じてファイルをやり取りする仕組みを持つ構成では動作確認が必要です。
設定後にサービスの名前空間内の /tmp を確認するには、次のコマンドを使います。
# サービスの PID を確認する systemctl show nginx --property=MainPID # その PID の名前空間内の /tmp を確認する(PID は実際の値に置き換える) sudo nsenter -m --target=
ls /tmp
2. NoNewPrivileges で権限昇格を封じる
このディレクティブを有効にすると、サービスプロセスが setuid/setgid バイナリを実行しても権限昇格が起きなくなります。侵害したプロセスから root 権限を奪おうとする最も一般的な手口を封じる、即効性の高い設定です。
[Service] # setuid/setgid による権限昇格を禁止する NoNewPrivileges=true
Webサーバー、APIサーバー、ログ収集デーモンなど、通常 root 権限を必要としないサービスであれば、ほぼ問題なく適用できます。注意が必要なのは、サービス起動後に子プロセスとして特権コマンドを呼び出す仕組みを持つ一部のサービスです。
3. ProtectSystem / ProtectHome でファイルシステムを保護する
ProtectSystem はシステム領域を読み取り専用にし、ProtectHome はユーザーのホームディレクトリをサービスから見えなくします。SSH秘密鍵やパスワードファイルが保存された /home や /root へのアクセスを遮断できます。
[Service] # "strict" で /usr /boot /etc を含むシステム全体を読み取り専用化(推奨) ProtectSystem=strict # /home /root /run/user をサービスから見えなくする ProtectHome=true # サービスが書き込む必要があるディレクトリは ReadWritePaths で例外指定する # 例: /var/log/nginx と /var/cache/nginx には書き込みを許可 ReadWritePaths=/var/log/nginx /var/cache/nginx
ProtectSystem の設定値は3段階あります。
・true: /usr /boot を読み取り専用化(最小限)
・full: /usr /boot /etc を読み取り専用化(多くの環境で推奨)
・strict: システム全体(/ 以下)を読み取り専用化し、ReadWritePaths で例外を指定(最強)
nginx の場合、/var/log/nginx と /var/cache/nginx への書き込みが必要なため、ReadWritePaths で明示的に許可します。サービスが書き込むパスを事前に洗い出してから適用するのが重要です。
4. CapabilityBoundingSet でケーパビリティを最小化する
Linuxのケーパビリティ(Capability)とは、root 権限を細かく分割した権限単位のことです。たとえば CAP_NET_BIND_SERVICE は1024番以下のポートをバインドする権限、CAP_SYS_PTRACE は他プロセスをデバッグする権限です。
CapabilityBoundingSet を使うと、そのサービスが持てるケーパビリティを必要最小限に絞れます。
[Service] # まず全ケーパビリティを剥奪し CapabilityBoundingSet= # 必要なものだけを追加する # 例: nginx は 80/443 バインドに NET_BIND_SERVICE が必要 CapabilityBoundingSet=CAP_NET_BIND_SERVICE # 環境変数経由でも昇格させない(NoNewPrivileges と組み合わせる) AmbientCapabilities=CAP_NET_BIND_SERVICE
サービスが必要とするケーパビリティは、journalctl のエラーログや strace コマンドで調査できます。設定後にサービスが正常に動作するかを必ず確認してください。
よく使うケーパビリティの例を参考として示します。
・CAP_NET_BIND_SERVICE: 1024番未満のポートをバインドする(Webサーバー等)
・CAP_NET_RAW: RAWソケットを使う(ping 等を発行するデーモン)
・CAP_SYS_TIME: システム時刻を変更する(NTPデーモン等)
・CAP_DAC_OVERRIDE: ファイルパーミッションを無視する(基本的に付与すべきでない)
5. RestrictAddressFamilies でネットワーク通信を制限する
このディレクティブは、サービスが使えるソケットの種類を制限します。IPv4/IPv6通信しか必要ないサービスにUnixドメインソケットやBluetoothソケットの使用を禁止することで、攻撃者が利用できる通信経路を減らします。
[Service] # AF_INET(IPv4)と AF_INET6(IPv6)のみ許可する例 RestrictAddressFamilies=AF_INET AF_INET6 # Nginx + PHP-FPM のように Unix ドメインソケットも使う場合 RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
6. systemd-analyze security でスコアを確認する
設定内容のセキュリティ強度を数値で確認できるのが systemd-analyze security コマンドです。0.0(最強)~10.0(未設定)のスコアで評価されます。
# 全サービスのセキュリティスコアを一覧表示する systemd-analyze security # 特定サービスの詳細スコアを確認する systemd-analyze security nginx
出力では各ディレクティブの適用状態と評価(「OK」「WARN」「UNSAFE」等)が一目でわかります。外部公開しているサービスで UNSAFE が複数残っている場合は優先的に対処しましょう。
一般的な目安として、Webサービスであれば 4.0 以下(低リスク)、重要なシステムでは 2.0 以下を目指すとよいでしょう。
Linuxのより詳細なセキュリティ設定については、姉妹サイトLinuxMaster.JPでも実践的な解説を掲載しています。
中小企業でも今日からできること
「systemdの設定変更は難しそう」と感じるかもしれません。しかし、最初の一歩は非常にシンプルです。
・まず現状を把握する: systemd-analyze security で自社サービスのスコアを一覧確認する
・優先対象を決める: スコアが 7.0 以上でかつ外部公開しているサービスから着手する
・まず3行から始める: PrivateTmp=true、NoNewPrivileges=true、ProtectHome=true を追加するだけでスコアが大幅に改善する
・1サービスずつ適用する: 一度に全サービスへ適用せず、1つずつ動作確認しながら進める
・変更はオーバーライドファイルで管理する: systemctl edit を使えばパッケージ更新で設定が上書きされない
本番環境へ適用する前に、必ず検証環境か非ピーク時間帯でテストしてください。設定が原因でサービスが起動しない場合も、journalctl -xe コマンドでエラー内容を確認すれば原因を特定できます。
よくある誤解と注意点
【注意1】サンドボックス化は「完全な防御」ではない
サンドボックス化はあくまで「被害の局所化」です。サービス自体に脆弱性があれば侵害は発生します。定期的なパッケージアップデートや WAF の導入と組み合わせることで、初めて効果が最大化します。
【注意2】全設定を一度に適用しない
特に ProtectSystem=strict と CapabilityBoundingSet の組み合わせは、サービスによっては予期しない動作停止を招くことがあります。1つずつ適用して動作確認するのが鉄則です。
【注意3】コンテナ環境では効果が限定的な場合がある
DockerやPodmanなどのコンテナ内のプロセスには、ホスト側の systemd サンドボックス指令は直接作用しません。コンテナ環境では、seccompプロファイルやAppArmorプロファイルなどコンテナ固有のセキュリティ設定を別途確認してください。
【注意4】設定変更後はサービスのフル動作テストを忘れずに
ログ出力・データ書き込み・外部API通信など、サービスの全機能を検証してから本番適用してください。開発環境や検証環境で先行テストするのが安全な運用の基本です。
本記事のまとめ
systemdのサンドボックス化は、追加コストゼロで実装できる効果的な深層防御策です。
| 設定指令 | 防ぐ攻撃・リスク | 導入難易度 |
|---|---|---|
| PrivateTmp=true | /tmp を悪用した攻撃ツール配置・共有 | 低(すぐ適用可) |
| NoNewPrivileges=true | setuid バイナリによる root 権限昇格 | 低(すぐ適用可) |
| ProtectSystem=strict | システムファイルへの不正書き込み・改ざん | 中(ReadWritePaths の調整が必要) |
| ProtectHome=true | SSH鍵・パスワードファイルの盗取 | 低(すぐ適用可) |
| CapabilityBoundingSet | 不要な root 相当権限の悪用 | 中(要件の事前調査が必要) |
| RestrictAddressFamilies | 不要な通信プロトコル経由の横展開 | 中(通信要件の把握が必要) |
まずは systemd-analyze security でスコアを確認し、外部公開サービスから順に PrivateTmp・NoNewPrivileges・ProtectHome の3つを追加することをお勧めします。それだけで多くの攻撃シナリオにおける被害を大幅に軽減できます。
Linuxサーバーのセキュリティ設定、正しく把握できていますか?
systemdのサンドボックス化は始まりに過ぎません。SELinux・firewalld・auditdなどと組み合わせた多層防御の設計、中小企業でも実践できる運用ルールについて、メルマガで実践的なセキュリティ対策ノウハウをお届けしています。
正しいセキュリティ知識を体系的に身につけたい方へ、メルマガで実践的なセキュリティ対策ノウハウをお届けしています。
