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の用意など) が困難な場合などに本記事が役に立つことを願っています。
