Some notes on Apache's suexec
We've recently been wrestling with suexec in an attempt to get it to do something that it seemed that suexec would do. As a result of that learning experience, I feel like writing down some things about suexec. You may wish to also read the official Apache documentation on suexec, but note that you may have to pay close attention to some of the things that it says (and a few things appear to be outright wrong).
Suexec has two modes:
- Running
/~<user>/...
CGIs as the particular user involved. This needs no special extra configuration for suexec and simply just happens. Per-user CGIs must be located under a specific subdirectory in the user's Unix home directory, by defaultpublic_html
; suexec documentation calls this subdirectory name the userdir. - Running CGIs for a virtual host as a particular user and group.
This must be configured with the
SuexecUserGroup
directive. All virtual host CGIs must be located under a specific top level directory, by default often/var/www
; suexec documentation calls this directory the docroot.
(Suexec also does various ownership and permissions checks on the CGIs and the directory they are directly in. Those are beyond the scope of these notes.)
The first important thing here is that the suexec docroot and
userdir are not taken from the Apache DocumentRoot
and UserDir
settings; instead, they're hard coded into suexec itself. Any time
that suexec logs errors like 'command not in docroot', the docroot
it means is not the Apache DocumentRoot
you've configured. It
pretty much follows that if your Apache settings do not match the
hardcoded suexec settings, suexec will thumb its nose at you.
(Also, the only form of UserDir
directive that will ever work
with suexec is 'UserDir somename
'. You cannot use either 'UserDir
/some/dir
' or 'UserDir /some/*/subdir
' with suexec. The suexec
documentation notes this.)
The second important thing is that Apache and suexec explicitly
distinguish between the two modes based on the incoming request
itself, not the final paths involved, and these two modes are
exclusive. If you make a request for a CGI via a /~user/...
URL,
the only thing that matters is if the eventual path is under the
user's home directory plus the suexec userdir. If you make a
request to a virtual host with a SuexecUserGroup
directive, the
only thing that matters is if the eventual path is under the suexec
docroot. In particular, you cannot configure a virtual host for
a user, point its DocumentRoot
to that user's userdir, and have
suexec run CGIs. This path would be perfectly acceptable if the
CGIs were invoked via /~user/... URLs, but when invoked for a plain
virtual host, suexec will reject these requests because the paths
aren't under its docroot.
(Mechanically, Apache prefixes the user name it passes to the suexec
binary with a ~
if it is a UserDir request. This is undocumented
behavior reverse engineered from the code, so you shouldn't count
on it.)
The third important thing is that suexec ignores symlinks in all
of this checking; it uses only the 'real' physical paths, after
symlinks have been traversed. As a result you cannot fool suexec
by, for example, putting symlinks to elsewhere under what it considers
its docroot. However it is fine for user /etc/passwd
entries
to include symlinks (as we do); suexec will not
be upset by that.
Normally the suexec docroot and userdir are set when suexec
is compiled and are fixed afterwards, which obviously creates some
problems if you need something different. Debian and Ubuntu provide
a second version of suexec that can look these up at runtime from
a configuration file (this is the apache2-suexec-custom package).
Failing this, well, you'll be arranging (somehow) for all of your
virtual hosts to appear under /var/www
(or at least all of the
ones that need CGIs).
(You can determine the userdir and docroot settings for your
suexec with 'suexec -V
' as root. You want AP_DOC_ROOT
and
AP_USERDIR_SUFFIX
.)
Sidebar: what 'command not in docroot' really means
The suexec error 'command not in docroot' is actually generic and is used for both modes of requests. So what suexec means by 'docroot' here is either the actual docroot, for a virtual host request, or the user's home directory plus the userdir subdirectory, for a /~user/... request. Unfortunately you cannot tell from suexec's log messages whether it was invoked for what it thought was a user home directory request or for a virtual host request; that has to be obtained from the Apache logs.
The check is done by a simple brute force method: first, chdir()
to the CGI's directory and do a getcwd()
. Then chdir()
to either
the docroot or the user's home directory plus the userdir and
do another getcwd()
. Compare the two directory paths and fail if
the first is not underneath the second. Because it uses getcwd()
,
all symlinks involved in either path will wind up getting fully
expanded.
|
|