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:
/~<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 default
public_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
SuexecUserGroupdirective. 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
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 '
/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
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
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
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
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,
to the CGI's directory and do a
chdir() to either
the docroot or the user's home directory plus the userdir and
getcwd(). Compare the two directory paths and fail if
the first is not underneath the second. Because it uses
all symlinks involved in either path will wind up getting fully