世界を動かす技術を、日本語で。

SystemDサービスの強化

概要

  • systemd は多機能で堅牢なサービス管理を提供
  • デフォルトでは セキュリティ最適化 は十分でない場合が多い
  • systemd-analyze security で現在の設定を可視化可能
  • 各サービスごとに 個別のハードニング が必要
  • 本記事はsystemdサービス/Podman Quadletの セキュリティ強化手法 の解説

systemdサービスのセキュリティハードニング入門

  • systemd はサービス管理や多様なOS機能の制御を担うフレームワーク
  • デフォルト設定は 利便性重視 であり、セキュリティは最低限
  • セキュリティ強化 は個別サービスの要件やリスクに応じて調整が必要
  • 本記事は 推奨設定例分析方法 のガイド

注意事項

  • 本ガイドは 推奨例 であり、全ての環境に適合するものではない
  • サービスごとに 必要な権限 や設定は異なるため、事前検証が必須
  • 変更後は ログ確認動作検証 を徹底

systemdのセキュリティ分析方法

  • systemd-analyze security コマンドで全サービス、または個別サービスの セキュリティ状況 を確認
    • 例:sudo systemd-analyze security sshd.service
  • 出力には 各種セキュリティ制御 の有効/無効、説明、リスクスコアが表示
  • Exposure 値(リスクスコア)を参考に、優先順位を決定
  • Name 欄はunitファイルで設定する際のキーワード

設定変更方法

  • セキュリティキーは [Service]セクション (systemd)または [Container]セクション (Podman Quadlet)に記載
  • 設定ファイルの場所
    • /etc/systemd/system/
    • /etc/containers/systemd/
  • systemctl edit サービス名.service でオーバーライド設定が可能
    • 例:EDITOR=nvim sudo systemctl edit サービス名.service
  • 手動で/etc/systemd/system/サービス名.service.d/override.confを作成も可
  • 変更後、サービスが起動しない場合は 必要な権限を再確認

主要なセキュリティオプション一覧

  • ProtectSystem :ファイルシステム全体を読み取り専用化
  • ReadWritePaths :特定パスのみ書き込み許可
  • ProtectHome :/home, /root, /run/userへのアクセス制限
  • PrivateDevices :物理デバイスアクセス遮断、疑似デバイスのみ許可
  • ProtectKernelTunables :/proc, /sysを読み取り専用
  • ProtectControlGroups :cgroupsを読み取り専用
  • ProtectKernelModules :カーネルモジュールのロード禁止
  • ProtectKernelLogs :カーネルログバッファへのアクセス制限
  • ProtectProc :他ユーザープロセスを/procから不可視化
  • ProcSubset :/proc内でプロセス管理関連以外を不可視化
  • NoNewPrivileges :新たな権限獲得禁止
  • ProtectClock :システムクロック書き込み禁止
  • SystemCallArchitectures :ネイティブシステムコールのみ許可
  • RestrictNamespaces :namespace機能の制限
  • RestrictSUIDSGID :setuid/setgidビットの設定禁止
  • LockPersonality :実行ドメインの変更禁止
  • RestrictRealtime :リアルタイムスケジューリングの制限
  • RestrictAddressFamilies :利用可能なソケットアドレスファミリー制限
  • MemoryDenyWriteExecute :書き込み・実行可能なメモリ領域の確保禁止
  • ProtectHostname :ホスト名変更システムコールの禁止
  • SystemCallFilter :許可するシステムコールのグループ制御
    • 例:SystemCallFilter=@system-service
    • 否定:SystemCallFilter=~@chown
    • エラーコード返却:SystemCallErrorNumber=EPERM

システムコール制御のトラブルシュート

  • auditd を利用し、違反発生時にsudo ausearch -i -m SECCOMP -ts recentでログ確認
  • ログ中の syscall名 をSystemCallFilterに追加して再試行

優先的に検討すべきセキュリティ項目

  • ProtectSystem=strict

  • PrivateTmp=yes

  • ProtectHome=yes または ProtectHome=tmpfs

  • ProtectClock=yes

  • ProtectKernelLogs=yes

  • ProtectKernelModules=yes

  • RestrictSUIDGUID=yes

  • UMask=0077

  • LockPersonality=yes

  • RestrictRealtime=yes

  • MemoryDenyWriteExecute=yes

  • DynamicUser=yes または User=非rootユーザ

    • これらは多くのサービスで有効だが、動作要件によっては適用不可の場合もあり

適用例:Traefik Quadletユニット

  • TraefikのPodman Quadlet用unitファイル例
    • [Unit]

      • Description=Traefik Reverse Proxy with Socket Activation
      • Requires=http.socket https.socket
    • [Container]

      • ContainerName=traefik
      • HostName=traefik
      • Image=docker.io/traefik:v3
      • Network=traefik.network
      • Volume=traefik-config.volume:/etc/traefik/:Z
      • Volume=/var/log/traefik:/logs/:Z
      • AutoUpdate=registry
      • Notify=true
      • HealthCmd=CMD-SHELL traefik healthcheck --ping
      • HealthInterval=10s
    • サービス用途や運用形態に応じて 柔軟な調整 が必要

まとめと運用のポイント

  • systemdの セキュリティ強化 は「リスク管理」と「運用要件」のバランスが重要
  • 外部公開サービス(nginx, httpd, Traefik, ssh等)から優先的に適用
  • カスタムスクリプト自作サービス には積極的なハードニング推奨
  • 設定変更後は ログ解析動作検証 を徹底
  • manページ (man systemd.exec(5)等)が最新情報の信頼できる情報源

systemdサービスのセキュリティ強化 は地道な作業だが、インフラ全体の安全性向上に直結。 適切な設定・検証・運用 を心がけ、リスク低減を目指すことが重要。

Hackerたちの意見

システムコールのデバッグに関するいいヒントだね!

昨日の(なんか変にフラグが立ってるか死んでる?)記事よりも、試すべきオプションについてのリアルなヒントがたくさん載ってる、ずっといい記事だよ。昨日の記事は楽しめたけど、内容が薄かったから、コメント欄で実際の例を挙げてもっと役立つ情報を提供しようとしたんだ。でも、これはsystemdが簡単に、すぐに、そして手軽に隔離とセキュリティを大幅に向上させる素晴らしい方法のリストだね。素晴らしいまとめだ!昨日の記事も念のために: https://us.jlcarveth.dev/post/hardening-systemd.md https://news.ycombinator.com/item?id=44928504

サイトの証明書の問題を直した方がいいかも。悪い証明書だと、ブラウザによっては先に進めないこともあるから。

それは、古いinitスクリプトでは絶対にできないことなんだよね。あれはそれぞれ独自のもので、全然統一感がないから。

確かに、独自のinitスクリプトでもこれらのことは実現できるけど、やっぱりsystemdのおかげで、私たちが愛するカーネルとその機能を統一的かつ標準的に使えるようになったんだよね… :) 私はLinuxの旅を始めるのが遅かったから、systemdなしの生活なんて想像できないよ。systemdがないシステムに出会ったことがあるけど、使うのが本当に面倒だった。最近、「unshare」を見つけて、他のプロセスに影響を与えずにハードリンクのために/nixをRWで再マウントするのに使えるんだ。systemdは本当に素晴らしいけど、使うときのUXはちょっとイマイチだね。でも、私の場合、代わりは正直Windowsだよ。

straceプロファイリングによる自動systemdサービスのハードニング https://github.com/desbma/shh

見つけたいいことは、もしやるなら(例ではやってないみたいだけど)

ProtectSystem=

を使うと、 TemporaryFileSystem=/:ro BindReadOnly=/usr/bin/binary /lib /lib64 /usr/lib usr/lib64 を設定できて、実際にはバイナリと利用可能なパスを含めるだけで済むんだ。ProtectSystem=はこの動作とは現在互換性がないけど。 EDIT: 詳細はこちらで確認してね: https://github.com/systemd/systemd/issues/33688

systemdが提供するもう一つの素晴らしいセキュリティ機能は、資格情報管理だよ。これにより、アプリケーションに対して、環境変数やファイルシステムのファイルよりも安全に資格情報を公開できるんだ。Vaultが使えないとき、例えばサイドプロジェクトに取り組んでいるときは、これをいつも使ってる。そういう資格情報を取得するための小さなGoパッケージも書いたよ、アプリケーションがその機能を持つサービス内で動いているときにね。[1]: https://systemd.io/CREDENTIALS/ [2]: https://sr.ht/~jamesponddotco/credential-go/

nodejsとnpmの道を選んだみたいだね。2行のコードがそれぞれパッケージになるなんて。 dir, err := os.Getenv("CREDENTIALS_DIRECTORY") cred, err := os.ReadFile(filepath.Join(dir, "name")) これってleft-padよりも複雑さが少ないよね。Goの文化って依存関係は悪いし、抽象化(つまり関数呼び出し)は混乱を招くから、こういうのはインラインで書いて毎回新しくする方がいいって聞いたけど。

... 具体的には、どうやってフォークされた子プロセスへの継承を防ぐの?

共有してくれてありがとう。どうやら「systemd-analyze」を「--user」フラグ付きで使って、systemdのユーザー単位も調べられるみたいだね(「systemd-analyze --user security」)。コンテナをPodmanに移行してからsystemdをもっと使い始めたけど、これは私のsystemdユニットやコンテナサービスのセキュリティを向上させるのに役立つユーティリティになるね。

細かい指摘とタイトル修正: systemdの正しいスペルはsystemdで、SystemDじゃないよ。ブランドページによると、そう書かれてるんだ。system DやSystem D、さらにはSystemDでもないし、system dでもない。なんでかっていうと、これはシステムデーモンだからで、Unix/Linuxではそれらは小文字で書かれて、小文字のdが付くんだ。そしてsystemdがシステムを管理するから、systemdって呼ばれてるんだよ。

面白い!システムDって書かれてるのはよく見るけど、なんでそんなに人気なんだろうね…

一般的なサービスのためのハードニングを提案するリポジトリがあったらいいなと思う。ハードニングの選択肢がたくさんあるからね。多くのユーザーが共通の提案を使っていると、意外と権限をもっと開放しないといけないことがあるのに気づくかも。

一般的なサービスのためのハードニングを提案するリポジトリがあったらいいな nixpkgsのためにパッケージを作っていると、上流サポートがないディストロで、メインストリームのディストロがサービスをパッケージ化する方法を見るのはとても役立つよ。これらのハードニング手順は、時には少し緩いこともあるけど、よくテストされていることが多いからね。例えば、postgresqlをハードニングする方法を知りたいなら、Debian、Ubuntu、またはRHELのパッケージを出発点にするといいよ。

いい記事だね。プロパティのリストと「マニュアルページをチェックして、頑張って」ってアドバイスがすごくありがたい。systemdは本当に素晴らしいソフトウェアで、ホストにデプロイするのが楽しみだよ!

なんでディストロはもっとこういうスイッチを切り替えないんだろう?これらの設定をもっと攻撃的にすることにはデメリットがあるのかな?多くの人にとっては、いじるのが大変なんだよね。

これらの設定をもっと攻撃的にすることのデメリットってあるの? うーん、デメリットは、知らず知らずのうちにいくつかのセットアップを壊しちゃうかもしれないことかな。例えば、NetworkManagerを厳しく設定した後、IPv4とIPv6の接続性は確認した? dns=systemd-resolveddns=default の動作モード(つまり、/etc/resolv.confを誰が管理するか)が両方とも機能しているか確認した? ModemManagerとの統合もチェックした?それがまだセルラー接続を管理できるかどうかも確認した? openvpnやcisco anyconnectのプラグインがまだ動いているかは? NetworkManager-dispatcherのフックはどうなってる?

なんでディストロはもっとこれらのスイッチを切り替えないの? 「実際にメンテナンスしている人が、どれだけのスイッチを壊さずに切り替えられるかを理解しているか」という問題もあるけど、「これらのフラグはディストロが持つべきなのか、それともアップストリームパッケージが持つべきなのか」という問題もあるよね。ディストロがこれを管理すると、次のアップストリームリリースでより多くの後退が起きることになる。アップストリームがこれを設定すると、下流のいくつかを壊さずに攻撃的にはできないからね。

この質問は「なんでディストロはデフォルトで制限的なMACポリシーを有効にしないの?」に似てるね。メンテナはsshdをロックダウンして、もし悪用された場合の被害を制限するために時間をかけることもできるけど、それにはコストがかかるんだ。1. 初期の開発コスト 2. バグレポートの処理によるメンテナンスコスト(ユーザーにとってのエッジケースが多い) 3. アップストリームの変更に合わせるためのメンテナンスコスト。この議論をさらに広げることもできるけど、ディストロはどんなセキュリティ機能も気にしなくていいって言えるかもしれない。でも、ディストロのメンテナの仕事の一部はここでバランスを取ることだから、SELinuxやAppArmorみたいに、ほとんどのメインストリームのデスクトップディストロのメンテナは、労力に見合う価値がないと思ってるんじゃないかな。