systemdのサンドボックス機能を活用しよう

はじめに

サンドボックスといえばDockerやcontainerd、podmanを思い浮かべる方が多いと思いますが、実はsystemdでもサンドボックスを作ることができます。コンテナイメージを作っておらず、直接ホスト上で動かしているアプリケーションをよりセキュアに動作させるために使えそうです。

Docker使えば良くない?

Dockerを使わず、systemdでサンドボックスを構成することには次のようなメリットがあります:

  • Dockerが不要。Docker自体がソフトウェアコンポーネントであり、導入することでシステムの複雑性が増すほか、Dockerがattack surface (攻撃対象) となりえます。
    • Dockerのアップデート、コンテナレジストリの選定・設定、コンテナイメージのビルドが不要となります。

systemdのサンドボックス機能を使ってできること

systemdのサンドボックス機能を使ってできることの例を示します。

  • アプリケーションが読み書き可能なファイルやディレクトリを制限すること
  • 専用のテンポラリファイル格納ディレクトリをマウントすること
  • ネットワーク名前空間を隔離すること
  • 権限昇格を制限すること

設定集

systemd ユニットファイルのサンドボックス関連の設定について記載します。

ProtectSystem

引数としてブール値または full または strict をとります。

ProtectSystem=true: /usr /boot /efi を読み取り専用でマウントします。

ProtectSystem=full: /usr /boot /efi /etc を読み取り専用でマウントします。

ProtectSystem=strict: /dev /proc /sysを除く、ファイルシステム全体を読み取り専用でマウントします。

PrivateTmp

引数としてブール値または disconnected をとります。

有効にすると、/tmp /var/tmpが他のプロセスと共有されなくなります (他のプロセスから見えなくなります)。disconnected にすると、「完全に新しいtmpfsインスタンス」が使われるようです。こちらはまだ著者もよく理解できていません。

これを使うことで、「他のプロセスのテンポラリファイルを、脆弱性により漏洩してしまう」ことや、「他のプロセスにテンポラリファイルを盗み見られてしまう」ことを防ぐことができます。

PrivateNetwork

引数としてブール値をとります。

PrivateNetwork=true: ネットワーク名前空間がホストから分離されます。分離された名前空間には、 lo デバイスのみが存在する状態になります。

ネットワークアクセスを想定していないアプリケーションを動作させることに適した設定です。「外部から不正なデータを受信して処理してしまう」ことや「外部に意図しないデータを送信してしまう」ことを防ぐことができます。

NoNewPrivileges

引数としてブール値をとります。

NoNewPrivileges=true: プロセスとその子孫が新たな特権を獲得するのを防ぎます。setuidビットやsetgidビットによる特権獲得、およびファイルシステムケイパビリティによる特権獲得を含みます。

この設定は、プロセスがすでに持っている特権を剥奪することはないので、最小限の特権を持った状態でプロセスを起動しなければうまく効果を発揮しません。非rootユーザーで起動したプロセスがroot権限を獲得することを防ぐことができます。

serviceファイル例

サンドボックスを使用した example.service ファイルの例を示します。

[Unit]
Description=Example daemon with hardening options
After=network.target

[Service]
Type=simple
User=nobody
Group=nogroup
ExecStart=/usr/bin/example-daemon --option
Restart=on-failure

# Hardening / sandboxing options
ProtectSystem=full        # /usr は読み取り専用、/etc は読み取り専用。一部書き込みは /var に限定
PrivateTmp=true           # /tmp と /var/tmp を名前空間で分離
PrivateNetwork=true       # ネットワーク名前空間で分離 (ループバックのみ)
NoNewPrivileges=true      # セットユーザー/グループや execve で権限昇格を禁止

# 追加の堅牢化 (任意)
ProtectHome=true          # /home、/root、/run/user をアクセス不可にする
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
PrivateDevices=true
RestrictAddressFamilies=AF_INET AF_INET6 unix

[Install]
WantedBy=multi-user.target

終わりに

systemdは導入されているもののdockerは導入されていない場合や、dockerを使うための準備 (イメージのビルドやContainer Registryの用意など) が困難な場合などに本記事が役に立つことを願っています。

新規CTA