本文へ移動

13. セキュリティに関する考慮事項

権限の分離、攻撃対象領域、Linux capabilities、安全な運用

HAProxy は、非常に限られた権限で動作するよう設計されています。標準的な使用方法は、chroot jail に隔離したうえで、jail 内で何の権限も持たない非 root ユーザーに権限を落とすことです。これにより、将来脆弱性が発見されても、侵害がシステムの他の部分に影響しないようにします。

chroot を実行するには、最初に root ユーザーとして起動する必要があります。手作業で chroot 環境を構築し、その中でプロセスを起動するのは意味がありません。そのような環境の構築は面倒で、適切に保守されることがなく、メインのファイルシステムよりはるかに多くの問題を抱えがちです。侵害された場合、侵入者はその専用のファイルシステムを利用できます。残念ながら、多くの管理者が「root として起動する」ことと「root として動作する」ことを混同し、haproxy の起動前に uid を変更してしまうため、実際のセキュリティ制限が弱くなっています。

HAProxy は、次の処理を行うために root として起動する必要があります。

  • ファイル記述子の上限を調整する。
  • 特権ポート番号にバインドする。
  • 特定のネットワークインターフェースにバインドする。
  • 他のホストのアドレスで透過的にリッスンする。
  • chroot jail 内に自身を隔離する。
  • 別の非特権 UID に切り替える。

次の処理を行うには、HAProxy を root として動作させる必要がある場合があります。

  • 送信接続を特定のインターフェースにバインドする。
  • 送信接続を特権送信元ポートにバインドする。
  • 送信接続を他のホストのアドレスに透過的にバインドする。

ほとんどのユーザーは「root として動作する」必要はありません。一方、ほとんどの用途では「root として起動する」必要があります。

安全な設定には、次の要素を含めます。

  • アクセス権限が一切ない空の場所を指す chroot 文。UNIX のコマンドラインでは、次のように準備できます。
# mkdir /var/empty && chmod 0 /var/empty || echo "Failed"

HAProxy 設定の global セクションでは、次のように参照します。

chroot /var/empty
  • global セクション内の uid/user 文と gid/group 文の両方。
user haproxy
group haproxy
  • CLI へのアクセスを許可するユーザーまたはグループに合わせて mode、uid、gid を設定した stats ソケット。これにより、他のユーザーからのアクセスを防ぎます。
stats socket /var/run/haproxy.stat uid hatop gid hatop mode 600

13.1. Linux capabilities のサポート

バージョン v2.9 以降、haproxy は Linux capabilities をサポートしています。バイナリーが USE_LINUX_CAP=1 でコンパイルされている場合、root ユーザーから非 root ユーザーに切り替える際に、‘setcap’ キーワードで指定された capability を保持できます。

バージョン v3.1 以降では、‘setcap’ キーワードで指定された capability が、管理者によってバイナリーファイルの Permitted セットに設定されているかも確認します(capget システムコール)。設定されていれば、非 root ユーザーとして動作しながら、それらをプロセスの Effective セットに移します(capset システムコール)。

これは、透過プロキシモードや特権ポートへのバインドなど、haproxy を root として起動し、動作させる必要があるすべての用途をなくすための対応です。

‘setcap’ キーワードは、次のネットワーク capability をサポートします。

  • cap_net_admin: 透過プロキシ、特定のネットワークインターフェースへのソケットのバインド、set-mark アクションの使用。
  • cap_net_raw(cap_net_admin のサブセット): 透過プロキシ。
  • cap_net_bind_service: 特定のネットワークインターフェースへのソケットのバインド。
  • cap_sys_admin: 特定のネットワーク名前空間内でのソケットの作成。

HAProxy は、これらの capability が ‘setcap’ の引数に列挙されていない限り、Permitted セットから Effective セットへ移しません。‘setcap’ キーワードとサポートされる capability の詳細は、設定ガイドの第 3.1 章「プロセス管理とセキュリティ」を参照してください。

管理者は、次のコマンドで haproxy バイナリーファイルの Permitted セットに必要な capability を追加できます。

例:

# setcap cap_net_admin,cap_net_bind_service=p /usr/local/sbin/haproxy

追加した capability は、プロセスの起動後にその Permitted セットで確認できます。同じ capability を ‘setcap’ キーワードの引数に指定していれば、プロセスの Effective セットでも確認できる場合があります。次のコマンドで確認できます。

例:

# grep Cap /proc/<haproxy PID>/status
CapInh: 0000000000000000
CapPrm: 0000000000001400
CapEff: 0000000000001400
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000

setcap と capability のセットの詳細は、Linux のマニュアルページ capabilities(7) を参照してください。

透過プロキシや、特定のネットワーク名前空間内でのソケットの作成などの用途では、設定ファイルのパーサーが cap_net_raw、cap_sys_admin、その他のサポート対象 capability が必要であることを検出します。その後、初期化段階で haproxy プロセスが、それらを自身の Effective セットに設定できるか確認します。SELinux や Seccomp などのセキュリティモジュールによるシステムコール制限のため capget や capset が失敗するなど、設定できない場合は、診断用の警告を出力します(-dD を指定して起動します)。

システム設定の異なる多数のプラットフォームをサポートしているため、特権ポートへのバインドが行われるかどうかを、パーサーが設定ファイルだけから判断することはできません。そのため、非 root での動作など権限が不足する場合は、次のようなアラートメッセージを出してプロセスが終了するだけです。設定と haproxy バイナリーの capability セットは、ユーザー自身が再確認する必要があります。

例:

$ haproxy -dD -f haproxy.cfg
...
[ALERT]    (96797): Binding [haproxy.cfg:36] for frontend fe: cannot bind socket (Permission denied) for [0.0.0.0:80]
[ALERT]    (96797): [haproxy.main()] Some protocols failed to start their listeners! Exiting.