OpenBSDのchrootはガチである

#PCとプログラム覚え

概要

0 はじめに-nginxの例

OpenBSDは、ともかく<お堅い>ので、普通のノリで<ま、nginxで、ホスト作るか>的にやると、動きません。

OpenBSDのpkg_addで、nginxをインストールしたとしましょう。

一通り設定をおえて、 /var/www/に、自分のホームディレクトリにつくってあるmysiteというパスを<シンボリックリンク>で作るとします。

doas ln -s /home/自分/web/mysite /var/www/

もうこの時点で駄目です。

なぜなら、chrootが、/var/wwwでハードコーディングされており、それが、nginxの<プロセス挙動ルート>です。

その外の場所にあるものは、シンボリックリンクでは、使えません。

あるいは、外部のホスト名をIPで引くような(あるいはその逆)スクリプト(たとえばphpなりpythonなり)を、WEBで動作させようとします。

そのままでは、エラーとなります。なぜか?。nginxのchrootが、/var/wwwですので、/etc/resolv.confは<外部>なので見に行けないのです。

なので、

mkdir /var/www/etc
cp /etc/resolv.conf /var/www/etc

が必要です。(もちろんシンボリックリンクでは無理です)。

つまり、OpenBSDでは、chrootはがちに機能しています。(機能するようにパッケージも作ってあります)。

サーバ用デーモン(relaydなりOpenSMTPDなり)をいじる場合は、こいつを常に意識しておく必要があります。

じゃあ、<あたいのmysiteはどうするんだ?>ということになりますが、これは、明示的にnginxのchrootを<外す>ようにします。

rcctl set nginx flags “-u”

これで、とりあえず、これでシンボリックリンクがきくので

ln -s /foo/bar/website /var/www

などとすると、/var/www配下としてファイルアクセスは可能となります。

でも、これは、決して /var/wwwから全部自由になったわけではありません。

というのも、/var/wwwが、OpenBSD版のnginxにはハードコーディングされているので、そこから自由になるのはなかなか難しいです。

nginxには、flags -p オプションというのがあって、

rcctl set nginx flags "-u -p /home/www"

などとすると、一見nginxのrootが、/home/wwwに変わるように<見えます>。

が、マスタープロセス(動作初期化)での挙動には関係しません。そこで、いろいろなズレが生じます。

1. OpenBSDの発想

  • ネイティブプログラム(httpd, relayd など)
    → プログラム自身が privilege separation / pledge / unveil でセキュリティを担保する

  • 外来プログラム(nginx など ports)
    → 後付けで chroot をかけて「檻に入れる」ことで暴れる範囲を制限する

この「後付け」が原因で、パス解釈に不整合が生まれやすいようです。


2. マスターとワーカーの区分が重要

プロセス タイミング 主な役割 パスの扱い
マスター chroot 前 設定ファイル読み込み、証明書オープン、ワーカー起動 絶対パスが有効(/etc/nginx, /etc/ssl, /etc/letsencrypt など)
ワーカー chroot 後(-uなしの場合) リクエスト処理、ソケット接続、ファイルアクセス chroot内のパスが見える
  • /etc/resolv.conf → ワーカーが使うので、chroot内にコピーが必要
  • 証明書や設定 → マスターが読むので絶対パスでOK

3. ランタイムオプションの効果

  • -u
    chroot を完全に無効化。ワーカーも実ファイルシステムを見る。
    → シンボリックリンクが使えるようになります。

  • -p /path
    prefix を上書きする。
    一部有効(相対パスの logs/ や cache/ などには効く)。
    → しかしコンパイル時の NGX_PREFIX=/var/www ハードコードや、chrootパッチの ngx_strip_chroot() と完全には噛み合わなくなります。 → 特に unix ソケット周りでパス解釈がブレて「はまる」という次第。


4. 実践的な落としどころ

-p は使わず、以下で逃げるのが安定します:

rcctl set nginx flags "-u"
  • コンテンツは容量の多い /home に置いて、/var/wwwにシンボリックリンク
  • nginx 側は従来通り /var/www を基準に動かす
  • つまりは、document rootを除き、ハードコードされた /var/www に従いましょう。

これなら -p による複雑なパス解釈の問題を避けられまずし、ドキュメント容量を気にすることもなくなります。

5. PHP-FPM(unixソケット)で問題が顕在化する理由

静的ファイルだけなら、-pもつけても比較的素直に動くんですが、 PHP-FPM を入れると、迷路に陥ります。

  • nginx 側のパス解釈(後付けパッチ + prefix)
  • PHP-FPM 側のパス関連パラメータ(listen, chroot, chdir, SCRIPT_FILENAME など)

この2つが衝突するようです。

特に:

  • ソケットパスは「ファイルを読む」のではなく「connect() するアドレス」
  • -u を使っている場合は絶対パスで書いても動く
  • しかし -p を併用すると解釈が崩れて破綻しやすい、ということになります。

6. まとめ

  • 後付けの chroot パッチが、元の nginx のパス設計と完全に整合していない感じ
  • その不整合は マスター/ワーカーのタイミング差unixソケット で表面化します
  • -p は一部有効だが、完全には解決しないです(というか、はまる原因になりやすい)
  • 実践では -u + シンボリックリンク で逃げつつ、仕組みとしては「後付け改変によるパス解釈の不整合」だと理解しておいて、幸せを追求するほうがいいのでは、というのが暫定結論。
Popup