English version is available here!
1. 環境
| 項目 | 値 | 
|---|---|
| OS | Rocky Linux 9 | 
| httpサーバ | Apache/2.4.62 | 
2. 問題
# ...
RewriteEngine On
RewriteMap some_map "prg:/usr/bin/php /usr/local/bin/some_map.php" apache:apache
RewriteCond ${some_map:} ^(.*)$
RewriteRule ^ -
として、さらに /usr/local/bin/some_map.php を
| 項目 | 設定 | 
|---|---|
| 所有者 | root:myapp | 
| パーミッション | 770 | 
とした場合を考える。
そして、 ユーザ apache は、 グループ myapp に所属しているものとする。
直感的には、この場合 some_map を呼び出すにあたって権限エラーにはならないように思える。
- 
RewriteMapの第3引数 (username:groupname)により、some_mapの実行者はapache:apacheになる
- ユーザ apacheはグループmyappに所属している
- よって /usr/local/bin/some_map.php(770/root:myapp) にはアクセスできるはず
ところが実際には:
Could not open input file: /usr/local/bin/some_map.php
が発生し、 some_map の呼び出しに失敗する。
エラーログの取得方法
- 
httpd.confでLogLevel rewrite:trace5とする
- 
sudo tail /var/log/httpd/error_log | grep mapを実行する
すると、次のような結果を得る。
[Mon Oct 27 15:26:21.250136 2025] [rewrite:trace5] [pid pppppp:tid tttttt] mod_rewrite.c(505): [client x.x.x.x:x] x.x.x.x - - [wikinebula.org/sid#ssssssssssss][rid#rrrrrrrrrrrr/initial] map lookup OK: map=some_map key= -> val=Could not open input file: /usr/local/bin/some_map.php
(エラーログの取得方法 ここまで)
3. 原因
結論から言うと:
RewriteMap prg:によって起動される外部プロセスはsetuid()/setgid()による「明示されたユーザ:グループのみ」しか反映されず、
ユーザのセカンダリグループは一切引き継がれない。
つまり、「apache は myapp に所属している」というセカンダリグループの情報が無効化されている。
3-1. なぜセカンダリグループが消えるのか
それは、 Apache が外部プログラムを起動する際に使用する APR (Apache Portable Runtime) の実装に原因がある。
該当箇所は apache/apr/threadproc/unix/proc.c の 関数 apr_proc_create() :
ここでは:
| 操作 | 実行される? | 備考 | 
|---|---|---|
| setgid(attr->gid) | される | 明示されたグループのみ変更※ | 
| setuid(attr->uid) | される | 実行ユーザを変更 | 
| initgroups(user, gid) | されない | !!! 問題の核心 !!! | 
※ RewriteMap の第3引数が username:groupname の場合に、 groupname のみが変更される
3-2. initgroups() の役割
Unix / Linux では:
| システムコール | 影響範囲 | 
|---|---|
| setuid() | ユーザを切り替える | 
| setgid() | 明示されたグループ(なければプライマリグループ)に切り替える | 
| initgroups(user, gid) | そのユーザが所属するセカンダリグループ一覧をカーネルに設定する | 
つまり、 setgid() / setuid() だけではセカンダリグループは反映されない。
これが今回の現象そのもの。
4. 実際の挙動を確認する
4-1. やりたいこと
some_map.php 内で /proc/self/status の内容を通常ファイルに書き出すことで、
some_map.php がどのような ユーザ, グループ で実行されているかを確認しよう。
(そのため、一時的に some_map.php のパーミッションを変更して、誰でもアクセス可能にする)
4-2. 手順
- まず、一時的に:
してsudo chmod 777 /usr/local/bin/some_map.phpsome_map.phpに誰でもアクセスできるようにする。
 
- 次に:
を実行して、誰でも読み書き出来るsudo touch /var/lib/status.txt sudo chmod 777 /var/lib/status.txt/var/lib/status.txtを作る。
 
- そして /usr/local/bin/some_map.phpの中で:とすることで、/usr/local/bin/some_map.php<?php file_put_contents('/var/lib/status.txt', file_get_contents('/proc/self/status'));/var/lib/status.txtに プログラムsome_mapを実行しているプロセスのステータス情報を書き込むようにする。
 
- Apache を再起動する。
 
- 
/var/lib/status.txtを見てみると、次のようになる(抜粋):/var/lib/status.txt(抜粋)Uid: 48 48 48 48 Gid: 48 48 48 48 FDSize: 64 Groups:
4-3. 結果について
Groups: が空になっていることから、セカンダリグループが読み込まれていないことが分かる。
ちなみに Uid: や Gid: の 48は、 apache の uid および gid である。
4つ並んでいるのは、順に
- 実 UID/GID
- 実効 UID/GID
- 保存 set-UID/GID
- ファイルシステム UID/GID
である。
5. 対策
sudo -u username -g groupname を利用する。
つまり:
RewriteMap some_map "prg:/usr/bin/php /usr/local/bin/some_map.php" apache:apache
を、次のものに置き換える:
RewriteMap some_map "prg:/usr/bin/sudo -u apache -g apache /usr/bin/php /usr/local/bin/some_map.php"
RewriteMap では、第3引数が省略されると、 Apache 起動時の権限 (通常 root) でプログラムを起動する。
このプログラムで sudo -u username -g groupname を利用すれば、Apache ではなく sudo のほうで権限の切り替えが起こるので、セカンダリグループが正しく読み込まれる。
実際、上記の置き換えを行って、再度 /var/lib/status.txt を出力させると、次のようになる(抜粋):
Uid:    48      48      48      48
Gid:    48      48      48      48
FDSize: 64
Groups: 48 1001
1001 は、セカンダリグループ myapp の グループID である。
