systemd Unitファイルのセキュリティオプションで最小権限のサービスを設計する方法|DynamicUser・ProtectSystem・PrivateTmpの実践パターン

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Linuxtips > セキュリティ > systemd Unitファイルのセキュリティオプションで最小権限のサービスを設計する方法|DynamicUser・ProtectSystem・PrivateTmpの実践パターン
「サービスがrootで動いているのは分かっているが、専用ユーザーを作る手間が面倒で放置している」
そういった状況は、現場でよく見かけます。しかし、サービスが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ファイルを変更せずに設定を追加できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

サービスを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

・ProtectSystem=strict:/usr・/boot・/etcへの書き込みを禁止する
・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

statusコマンドでActive: active (running)を確認してから、実際にWebページへアクセスできることを確認します。その後、再度systemd-analyze securityでスコアを確認してみてください。ProtectSystem=strictとPrivateTmp=trueを追加するだけでスコアが2.0前後下がります。

DynamicUserで専用ユーザーを自動生成する

1. DynamicUser=yes を追加する

DynamicUserは、サービスの起動時に専用のユーザーとグループを自動で生成し、停止時に削除するオプションです。手動でシステムユーザーを作成する手間が省けます。

# /etc/systemd/system/myapp.service.d/security.conf [Service] DynamicUser=yes StateDirectory=myapp RuntimeDirectory=myapp LogsDirectory=myapp

・DynamicUser=yes:サービス専用の一時的なユーザーを自動生成する
・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

・NoNewPrivileges=yes:setuidビットを使って新しい権限を取得できなくする
・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/...

エラーに表示されたパスをReadWritePathsに追加して、daemon-reloadとrestartを実行します。ReadWritePathsはスペース区切りで複数指定できます。

「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

RHEL 9系・Ubuntu 22.04以降では初期値が10000以上に設定されているため、このエラーは出ません。古い環境では、PrivateTmp=trueを無効にして代わりにProtectSystem=strictだけ使う構成にします。

本記事のまとめ

systemd Unitファイルのセキュリティオプションを設定する手順をまとめます。
やりたいこと 設定オプション
ファイルシステムへの書き込みを制限する ProtectSystem=strict
/tmpをプロセスから分離する PrivateTmp=true
書き込みを許可するパスを個別に指定する ReadWritePaths=/var/log/myapp
専用ユーザーをサービス起動時に自動生成する DynamicUser=yes
権限昇格を禁止する NoNewPrivileges=yes
使えるcapabilityを制限する CapabilityBoundingSet=CAP_NET_BIND_SERVICE
設定のスコアを確認する systemd-analyze security サービス名
drop-inファイル(/etc/systemd/system/サービス名.service.d/security.conf)に追加するだけで、元のUnitファイルを変更せずにセキュリティ設定を積み上げられます。まずはProtectSystem=strictとPrivateTmp=trueを追加して、systemd-analyze securityでスコアが下がることを確認してみてください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、
systemdのセキュリティ設定から権限設計・障害対応まで、2日間のハンズオンで徹底的に習得できます。
詳細はLinux Master Pro Seminar >>でご確認ください。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。