2003年12月26日

Apache suEXECの導入と既存環境からの移行

Apache に付属する suEXEC モジュールを使うことで、
CGIをWebサーバとは異なる実行権限で動作させることができる。
例えば、あるユーザのCGIをユーザの権限で動かすことで、
ユーザディレクトリへのアクセスをアクセス許可の設定変更なしに行えたり、
CGIに問題があった場合に他のユーザやシステムに影響が及ぶのを防ぐことができる。
suEXECモジュールを導入してみた。

「suEXECのコンパイルとインストール」

今回は、apache-2.0.48 (httpd-2.0.48)を対象に、FreeBSD 5.2-CURRENT上で作業を行った。
また、FreeBSDのportsを利用して、コンパイルとインストールを行った。
suEXECモジュールは、apacheをコンパイルする際に、オプションを指定することで有効となり利用できる。

まず、該当するディレクトリに移動する。
# cd /usr/ports/www/apache2

Makefile.modules を見ると、次のようなオプションが提供されているのが分かる。
.if defined(WITH_SUEXEC)
SUEXEC_DOCROOT?= ${PREFIX_RELDEST}/www/data
SUEXEC_USERDIR?= public_html
SUEXEC_SAFEPATH?= ${PREFIX_RELDEST}/bin:${LOCALBASE}/bin:/usr/bin:/bin
_APACHE_MODULES+= ${SUEXEC_MODULES}
CONFIGURE_ARGS+= --with-suexec-caller=www \
--with-suexec-uidmin=1000 --with-suexec-gidmin=1000 \
--with-suexec-userdir="${SUEXEC_USERDIR}" \
--with-suexec-docroot="${SUEXEC_DOCROOT}" \
--with-suexec-safepath="${SUEXEC_SAFEPATH}" \
--with-suexec-logfile="/var/log/httpd-suexec.log" \
--with-suexec-bin="${PREFIX_RELDEST}/sbin/suexec"
.endif

ここで、suEXECを有効にするために次の2つのオプションを指定する。
1つは、suEXECを有効にするためのオプションであり、
もう1つは、CGIがあるディレクトリにあわせてdocrootを設定しなければならない。
今回は次のようになった。
# make clean (2回目以降の場合のみ)
# make WITH_SUEXEC=yes SUEXEC_DOCROOT=/home
# apachectl stop (安全のためサーバを一時停止)
# make deinstall (以前にportsをインストールしてある場合のみ)
# make reinstall (同上。そうでない場合は、make installでよい)
# apachectl startssl (SSLを利用しない場合は、apache startでよい)

コンパイルオプションの詳細を知るには、また、FreeBSD以外の場合は、
http://httpd.apache.org/docs-2.0/ja/suexec.htmlを参照されたい。

「設定内容を確認」

多くの場合、/usr/local/sbin/suexec にsuexecというプログラムがインストールされる。
これをVオプションで実行すると、コンパイル時の設定を確認できる。

ichi# suexec -V
-D AP_DOC_ROOT="/home"
-D AP_GID_MIN=1000
-D AP_HTTPD_USER="www"
-D AP_LOG_EXEC="/var/log/httpd-suexec.log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=1000
-D AP_USERDIR_SUFFIX="public_html"

「suEXECの設定」

suEXECの設定は、httpd.confファイルで行う。
1.mod_suexecが組み込まれていることを確認する。
2.SuexecUserGroupディレクティブで実行権限を設定する。
また、特に権限をもつユーザとグループが指定されていない場合で、ユーザディレクトリが参照されるURLの場合は、そのユーザが実行権限となる。
例えば、http://www.toshikazu.org/~post/cgi-bin/へのアクセスでは、CGIはユーザpost権限で実行される。

# vi /usr/local/etc/apache2/httpd.conf

LoadModule info_module libexec/apache2/mod_info.so
LoadModule suexec_module libexec/apache2/mod_suexec.so
LoadModule cgi_module libexec/apache2/mod_cgi.so


ServerName www.toshikazu.org
DocumentRoot "/home/ichi/public_html"
...
SuexecUserGroup ichi ichi

「既存環境の変更」

suEXECは次のような条件を満たさなければ、CGIを実行しない。
また、CGIの権限が変わることで、CGIが使っていたファイルとの関係が変わる。
よって、既存環境で動作しているCGIやファイルの所有者や権限を変更する必要がある。

対象となるユーザ/グループがCGIプログラムのユーザ/グループと同じでなければ動かない
# find . -user www -print | xargs chown post:post (ユーザpost権限で動く)

ディレクトリを他のユーザが書き込めては動かない
対象となる CGI/SSI プログラムファイルが他アカウントから書き込めては動かない
# find . -perm +022 | xargs chmod go-w

「SuexecUserGroupによる指定 と UserDirによる変更 が共存できないという問題」

残念ながら、apache-2.0.48では、
SuexecUserGroupディレクティブを使ってしまうと、UserDirへのアクセスもそこで指定された実行権限になってしまう。
SuexecUserGroupディレクティブをDirectory、File、Locationといったディレクティブで限定することもできない。
apache-1.x系では共存できたのだが、2.x系は作りこみがまだ不十分なのだろう。
現時点でこの問題を解決する方法は、
1、SuexecUserGroupディレクティブをDirectoryの中で使えるようにするパッチを当てるか
2、強引に別名のバーチャルホストとするか
しかない。

1は[Patch] Allow SuexecUserGroup in にある。
2の例

SuexecUserGroup user group
UserDir disable
ServerName xxx2
DocumentRoot /home/vhost/xxx


UserDir public_html
ServerName xxx
DocumentRoot /home/vhost/xxx
Redirect /hoge.cgi http://xxx2/hoge.cgi

「補足」

CGIプログラムの実行権限をWebサーバ以外のものとする方法は、suEXECモジュールを使う方法だけではない。
プログラムの実効ユーザIDと実効グループIDを設定することで、プログラムの実行時の権限を変えられる。
具体的には、ファイル属性のsetuidビットとsetgidビットを立てることで、そのファイルの所有者ユーザ・グループを実行されるプログラムの権限にすることができる。
# chmod ug+s file

suEXECモジュールでは、phpの実行権限を変えることはできない。

また、suEXECモジュールの導入によって、既に設置されているCGIプログラムの中に、ファイルモードや所有者を操作するコマンドが含まれていて、それが不要になる場合もある。そのヒントを得るには次のコマンドを実行してみる手がある。
# find ~/public_html -print | xargs grep chmod
# find ~/public_html -print | xargs grep chown