日本語の記事は こちら
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.