日本語の記事は こちら
1. Environment
| Item | Value | 
|---|---|
| OS | Rocky Linux 9 | 
| HTTP Server | Apache/2.4.62 | 
2. Problem
# ...
RewriteEngine On
RewriteMap some_map "prg:/usr/bin/php /usr/local/bin/some_map.php" apache:apache
RewriteCond ${some_map:} ^(.*)$
RewriteRule ^ -
Now, suppose /usr/local/bin/some_map.php has the following settings:
| Item | Setting | 
|---|---|
| Owner | root:myapp | 
| Permission | 770 | 
And the user apache belongs to the group myapp.
At first glance, this setup should work without permission errors when invoking some_map:
- The third argument (username:groupname) inRewriteMapmeans the program runs asapache:apache.
- The user apachebelongs to the groupmyapp.
- Therefore, /usr/local/bin/some_map.php(770,root:myapp) should be accessible.
However, in reality:
Could not open input file: /usr/local/bin/some_map.php
appears, and the call to some_map fails.
How to get the error log
- Set LogLevel rewrite:trace5inhttpd.conf
- Run sudo tail /var/log/httpd/error_log | grep map
You’ll get an output like this:
[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
(End of error log instructions)
3. Cause
In short:
The external process started by
RewriteMap prg:only reflects the explicitly specified user:group throughsetuid()andsetgid(),
but it does not inherit any secondary groups.
That is, the secondary group information such as "apache belongs to myapp" is completely ignored.
3-1. Why the secondary groups disappear
The reason lies in how Apache launches external programs through the APR (Apache Portable Runtime) library.
Specifically, in the function apr_proc_create() within apache/apr/threadproc/unix/proc.c:
Here’s what happens:
| Operation | Executed? | Remarks | 
|---|---|---|
| setgid(attr->gid) | Yes | Only the explicitly specified group changes * | 
| setuid(attr->uid) | Yes | Switches user | 
| initgroups(user, gid) | No | !!! The root of the issue !!! | 
* When the third argument of RewriteMap is username:groupname, only the groupname is set.
3-2. The role of initgroups()
In Unix/Linux systems:
| System call | Effect | 
|---|---|
| setuid() | Switches user | 
| setgid() | Switches to the specified (or primary) group | 
| initgroups(user, gid) | Registers all secondary groups the user belongs to in the kernel | 
In other words, setgid() / setuid() alone do not reflect secondary groups.
That’s exactly what happens here.
4. Verifying the behavior
4-1. Goal
Let’s verify the user and group under which some_map.php runs by dumping /proc/self/status to a regular file from within some_map.php.
(We’ll temporarily make some_map.php accessible to everyone for this test.)
4-2. Steps
- Temporarily allow anyone to access some_map.php:sudo chmod 777 /usr/local/bin/some_map.php
- Create a writable /var/lib/status.txtfile:sudo touch /var/lib/status.txt sudo chmod 777 /var/lib/status.txt
- Modify /usr/local/bin/some_map.phpto write its own/proc/self/status:/usr/local/bin/some_map.php<?php file_put_contents('/var/lib/status.txt', file_get_contents('/proc/self/status'));
- Restart Apache.
 
- Check /var/lib/status.txt; you’ll see something like this (excerpt):/var/lib/status.txt (excerpt)Uid: 48 48 48 48 Gid: 48 48 48 48 FDSize: 64 Groups:
4-3. Interpretation
Since Groups: is empty, it’s clear that no secondary groups are loaded.
Here, Uid: and Gid: of 48 correspond to the user and group IDs of apache.
The four numbers represent:
- Real UID/GID
- Effective UID/GID
- Saved set-UID/GID
- Filesystem UID/GID
5. Workaround
Use sudo -u username -g groupname.
That is, replace:
RewriteMap some_map "prg:/usr/bin/php /usr/local/bin/some_map.php" apache:apache
with:
RewriteMap some_map "prg:/usr/bin/sudo -u apache -g apache /usr/bin/php /usr/local/bin/some_map.php"
If the third argument of RewriteMap is omitted, the external program runs with Apache’s startup privileges (usually root).
By invoking sudo -u username -g groupname, the privilege switch happens inside sudo, not Apache, so secondary groups are correctly initialized.
After applying this change and checking /var/lib/status.txt again, you’ll see (excerpt):
Uid:    48      48      48      48
Gid:    48      48      48      48
FDSize: 64
Groups: 48 1001
1001 is the group ID of the secondary group myapp.
