そういった状況は、現場でよく見かけます。しかし、サービスがroot権限で動いたままだと、脆弱性を突かれた際にシステム全体を乗っ取られるリスクが高くなります。
この記事では、RHEL 9系~10系やUbuntu 22.04 LTS以降を想定環境として、systemd Unitファイルにセキュリティオプションを追加して最小権限でサービスを動かす方法を解説します。DynamicUser・ProtectSystem・PrivateTmpの設定例と、systemd-analyze securityで安全スコアを確認する手順を紹介します。
この記事のポイント
・systemd-analyze security でサービスの危険スコアを数値で確認できる
・ProtectSystem=strict でサービスからの書き込みを制限できる
・DynamicUser=yes で専用ユーザーの作成と削除を自動化できる
・drop-inファイルで元のUnitファイルを変更せずに設定を追加できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
サービスをroot権限で動かし続けるリスク
Linuxサーバーで動くサービス(httpd、crondなど)は、それぞれ何らかのユーザー権限で動作しています。インストール済みのサービスは専用ユーザーで動いている場合もありますが、自作のサービスや古い設定のまま運用しているサービスがrootで動いているケースも珍しくありません。
サービスがroot権限で動いていると、そのサービスに脆弱性があった場合、攻撃者はroot権限をそのまま手に入れられます。最小権限の原則とは、サービスや利用者に「必要最小限の権限だけを与える」設計のことです。
systemdには、Unitファイルに数行追加するだけでサービスのアクセスを制限できるセキュリティオプションが用意されています。専用ユーザーを手動で作る必要もなく、ファイルシステムへの書き込みも自動で制限できます。
systemd-analyze securityで安全スコアを確認する
セキュリティ設定を変更する前に、現状のスコアを確認しておきます。systemd-analyze securityコマンドは、指定したサービスのセキュリティ設定を評価して0.0(安全)~10.0(危険)のスコアで表示します。サービス全体を一覧表示したり、特定のサービスを個別に確認したりできます(systemd-analyze の詳細はこちら)。
1. コマンドを実行する
httpdサービスを例にスコアを確認します。# httpdサービスのセキュリティスコアを確認する $ systemd-analyze security httpd
NAME DESCRIPTION WEIGHT x PrivateNetwork= Service has access to the host's network 0.5 x User=/DynamicUser= Service runs as root user 0.4 x NoNewPrivileges= Service processes may acquire new privileges 0.2 x ProtectSystem= Service has full access to the OS file 0.2 ... -> Overall exposure level for httpd.service: 9.6 UNSAFE
2. スコアの見方
スコアは0.0~10.0で表示され、数値が高いほど設定が不十分な状態です。・0.0~3.9(SAFE):セキュリティ設定が十分に適用されている
・4.0~7.9(MEDIUM):一部のオプションが未設定
・8.0以上(UNSAFE):主要なセキュリティ設定が未適用の状態
インストール直後のhttpdは9.6(UNSAFE)前後になることが多く、設定を追加するごとにスコアが下がります。まずは7.0以下を目標に設定を追加していくとよいでしょう。
drop-inファイルでUnitファイルを上書きする
systemdのセキュリティオプションは、元のUnitファイルを直接編集せず、「drop-inファイル」に追記するのが安全な方法です。drop-inファイルはサービスのアップデートで元のUnitファイルが更新されても影響を受けません。設定を元に戻したい場合はdrop-inファイルを削除するだけで済みます。
1. drop-inディレクトリとファイルを作成する
# httpdのdrop-inディレクトリを作成する $ sudo mkdir -p /etc/systemd/system/httpd.service.d/ # drop-inファイルを作成する $ sudo vi /etc/systemd/system/httpd.service.d/security.conf
2. ProtectSystemとPrivateTmpを追加する
ProtectSystemは、サービスがシステムのファイルに書き込めないよう制限するオプションです。# /etc/systemd/system/httpd.service.d/security.conf [Service] ProtectSystem=strict PrivateTmp=true ReadWritePaths=/var/log/httpd /run/httpd
・PrivateTmp=true:/tmpを他のプロセスから分離した専用の領域に切り替える
・ReadWritePaths=:書き込みが必要なパスをここで明示的に許可する
httpdはログを/var/log/httpdに書き込み、PIDファイルを/run/httpdに置くため、この2つのパスをReadWritePathsに指定します。サービスによって書き込み先は異なるので、エラーログで「Permission denied」を確認しながら調整します。
3. 設定を反映して確認する
# 設定を反映してサービスを再起動する $ sudo systemctl daemon-reload $ sudo systemctl restart httpd $ sudo systemctl status httpd
DynamicUserで専用ユーザーを自動生成する
1. DynamicUser=yes を追加する
DynamicUserは、サービスの起動時に専用のユーザーとグループを自動で生成し、停止時に削除するオプションです。手動でシステムユーザーを作成する手間が省けます。# /etc/systemd/system/myapp.service.d/security.conf [Service] DynamicUser=yes StateDirectory=myapp RuntimeDirectory=myapp LogsDirectory=myapp
・StateDirectory=:/var/libの下に専用ディレクトリを作成し、所有者を自動設定する
・RuntimeDirectory=:/runの下に専用ディレクトリを作成する
・LogsDirectory=:/var/logの下に専用ディレクトリを作成する
2. DynamicUserを使う時の注意点
DynamicUserを有効にすると、サービスを再起動するたびにUIDが変わる場合があります。ファイルの所有者をUID番号で指定しているスクリプトがあると動かなくなるので、既存のサービスに追加する場合は事前に確認します。httpdのように起動前からファイルの所有者が固定で設定されているサービスには向きません。自作のアプリやシンプルなデーモンを新規に作る際に、最初から組み込む形で使うのが一般的です。
NoNewPrivilegesとCapabilityBoundingSetで権限昇格を防ぐ
ProtectSystemとPrivateTmpで書き込み先を制限した後、権限昇格の仕組みそのものを無効にします。# /etc/systemd/system/httpd.service.d/security.conf(追記後の全体) [Service] ProtectSystem=strict PrivateTmp=true ReadWritePaths=/var/log/httpd /run/httpd NoNewPrivileges=yes CapabilityBoundingSet=CAP_NET_BIND_SERVICE
・CapabilityBoundingSet=:サービスが使えるcapabilityを制限する。空欄(=)にすると全capabilityを禁止する
httpdは80番・443番ポートにbindするためCAP_NET_BIND_SERVICEだけを許可しています。ポートをbindしないサービスなら、CapabilityBoundingSet=を空欄にして全capabilityを禁止できます。
Linuxサーバーの権限設計からセキュリティ設定まで体系的に習得したい方は、Linux Master Pro Seminarでハンズオン形式で学べます。
よくあるエラーと対処法
「Permission denied」が出た時の対処法
ProtectSystem=strictを追加した直後にサービスがPermission deniedで起動しない場合は、ReadWritePathsに書き込み先が漏れているのが原因です。# journalctlでPermission deniedが出ているパスを確認する $ sudo journalctl -u httpd -n 50 --no-pager | grep -i "permission\|denied" # 出力例(環境により異なります) httpd[1234]: (13)Permission denied: AH00526: ...can't open /var/cache/httpd/...
「Failed to set up user namespace」が出た時の対処法
PrivateTmp=trueやDynamicUser=yesはuser namespaceを内部で使います。コンテナ環境やKernel 3.x系のOSでは、user namespaceが無効になっている場合があり、このエラーが出ます。# user namespaceが有効かどうか確認する $ cat /proc/sys/user/max_user_namespaces # 0 と表示された場合は無効 # 一時的に有効にする(再起動で戻る) $ sudo sysctl -w user.max_user_namespaces=10000 # 永続化する場合 $ echo "user.max_user_namespaces=10000" | sudo tee /etc/sysctl.d/99-user-ns.conf $ sudo sysctl -p /etc/sysctl.d/99-user-ns.conf
本記事のまとめ
systemd Unitファイルのセキュリティオプションを設定する手順をまとめます。| やりたいこと | 設定オプション |
|---|---|
| ファイルシステムへの書き込みを制限する | ProtectSystem=strict |
| /tmpをプロセスから分離する | PrivateTmp=true |
| 書き込みを許可するパスを個別に指定する | ReadWritePaths=/var/log/myapp |
| 専用ユーザーをサービス起動時に自動生成する | DynamicUser=yes |
| 権限昇格を禁止する | NoNewPrivileges=yes |
| 使えるcapabilityを制限する | CapabilityBoundingSet=CAP_NET_BIND_SERVICE |
| 設定のスコアを確認する | systemd-analyze security サービス名 |
systemdのセキュリティ設定から権限設計・障害対応まで、2日間のハンズオンで徹底的に習得できます。
詳細はLinux Master Pro Seminar >>でご確認ください。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:rkhunterコマンドでLinuxのルートキットを検出する方法|インストールから定期スキャン・アラート設定まで
- この記事の属するカテゴリ:セキュリティへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます