When you're running an Apache Web server for yourself, you don't think about the user that's running the Apache server itself (typically nobody on Linux systems). But what if you're an ISP with multiple companies being hosted on your system? Or an educational institution with faculty who want to be able to execute their own scripts?
The Apache Web server, like most if not all of the others in common use
today, lets you execute arbitrarily complex operations through the use of CGI
scripts. These can involve database lookups, system administration functions,
real-time control of machinery, online payments, or almost anything else you
can think of.
Ordinarily, all of these things occur in the context of the user running
the Apache server itself (typically
nobody on Linux systems). This
is fine when you're using a system that is owned and used by a single
entity...but what if you're an ISP with multiple companies being hosted on your
system? Or an educational institution with faculty who want to be able to
execute their own scripts? Either everything has to be accessible to the Apache
nobody user, or you have to run multiple instances of Apache on
multiple ports and IP addresses, one of each per user, with the
concomitant confusion of configuration files.
On the other hand, if the server is to be allowed to change its identity,
it needs to be done in a controlled manner, so that the chance of compromising
your system's security is kept to a minimum. (Remember, Apache is usually
root and only changes to
suexec (pronounced 'SUE-ex-Ek') tool helps
make this possible. It's found in the
src/support/ directory under
your Apache source tree.
Assumptions in This Article
For the rest of this article, I'm going to make the following assumptions:
your Apache source tree starts at
your Apache ServerRoot is
your Apache DocumentRoot is
the username under which Apache runs (the value of the
directive in your
httpd.conf file) is
All of the
cd and other shell commands in this article that
refer to directories use these locations.
How Does Suexec Work?
suexec works by wrapping an operation up in a package
executed under a different username, it's called a wrapper. In order to
execute a script under the auspices of the wrapper, the Apache server creates a
child process running the
suexec binary and passes the particulars
to it. The wrapper verifies that all the security requirements are met, edits
the list of environment variables so that only the ones on its 'trusted' list
are available, closes its logfile, and calls some flavour of
execv(2) to load the script into the edited process environment,
suexec is used to run applications on your system on behalf
of arbitrary people out on the Web, it's very paranoid about doing anything
that might compromise your system's security. Here is a list of the conditions
suexec requires to be met before it will proceed; if any don't
measure up, the wrapper will log an error and not execute the script.
suexec must be invoked with the correct number of arguments.
If it isn't, it assumes someone is trying to penetrate your system by running
it outside the Apache environment.
The username/UID invoking
suexec must be a valid user; that
is, it must be listed in the
/etc/passwd file. If it isn't,
something's not quite right--and when in doubt, punt.
The username executing the wrapper must be the one that was compiled into
it when it was built. Again, a mismatch here is interpreted as someone trying
suexec in other than the prescribed way.
The requested script must be a valid Web-space reference relative to the
user's directory or the DocumentRoot; it cannot be an absolute filesystem path
, it cannot start with a "
") and cannot
include any up-level references (i.e.
, no "
The username and group under which the script is to be run must be valid,
cannot be '
root', and must be above the minimum UID and GID values
(set with the
options to the
configure script, which both default to 100). In
addition, the group must be a valid name, and not just a numeric GID.
The wrapper must be able to change its idenity to the requested username
The script (and obviously the directory in which it lives) must actually
exist and the wrapper must be able to
chdir() to the directory.
If the script isn't from a
~username request, the script
directory must be under the directory specified by
(defined by the
--suexec-docroot option to
The permissions on the specified script and its parent directory must not
allow write access to either the
group or the
The script file cannot be setuid or setgid.
The script and the directory must be owned by the user and group as
which it is to be executed.
The script must be executable by the user.
suexec must be able to allocate memory in which to reproduce
the environment variable list.
As you can see, the requirements for execution are pretty stringent. The
sheer number of things that can go wrong argues for the use of the wrapper only
when it's really necessary.
suexec wrapper isn't turned on or off by any particular
Apache directive setting. Instead, when the Apache server is compiled, one of
the constants set (
SUEXEC_BIN) is a string pointing to the
location of the
suexec binary. When the server starts, it looks
for the binary at that location; if it's found,
enabled--not otherwise. This is very important.
This means that even a normal Apache build that was performed without any
thought given to using the wrapper can suddenly become
suexec-enabled if a properly protected
is put into place between server restarts. In the master sources, the default
SUEXEC_BIN is set to
/sbin/suexec"; the default value of
HTTPD_ROOT is platform-specific:
||Default value of
You may change the values of either--or both--of the
SUEXEC_BIN constants when you
recompile the Apache server.
If Apache does
find the wrapper, it reports it in the server error
log like this:
[Thu Dec 30 01:24:43 1999] [notice] suEXEC mechanism enabled
Up until Apache version 1.3.11, there was no way to be sure where a
compiled Apache server is going to be looking for the
binary. As of 1.3.11, though, it's part of the 'compiled modules' report
displayed by the '
% /usr/local/web/apache/bin/httpd -l
suexec: enabled; valid wrapper /usr/local/web/apache/bin/suexec
enabled; valid' notation means that the wrapper is
actually present in the indicated location, and the permissions are correct. If
the wrapper isn't there, or the permissions are wrong, the output will indicate
suexec is disabled.
Because most of
suexec's control parameters are defined at
compile-time, the only way to change them is to recompile. And since the
wrapper works very closely with the Apache Web server--to the point of both
applications having to share some compile-time definitions--the way to
suexec is to recompile all of Apache. If you've never
done this before, you can see a brief treatment of the process in the
"Building Apache at Lightspeed" section of this article.
There are several
suexec-specific options to the
apache-1.3/configure script. Here they are:
- The presence of this option on the command line simply informs the
configure script that you want the wrapper to be built as well.
Without this option,
suexec will not be built, even if there are
suexec options on the command line.
- This mustbe the username under which your Apache server runs; that
is, the one specified on the
User directive outside all
<VirtualHost> containers. If
suexec is invoked
by any other user, it assumes it's some sort of probing attempt and fails to
execute (after logging the user mismatch).
The default username is
- This specifies the ancestor directory under which all CGI scripts need to
reside in order to be acceptable to
suexec. (This restriction
doesn't apply to scripts activated by
~username-style URLs.) If
you have multiple virtual hosts using
suexec, their DocumentRoots
(if you're using
.cgi files) must all be located somewhere in the
hierarchy under this directory, or else the wrapper will assume someone is
trying to execute something unexpected and will log it as an intrusion attempt.
ScriptAliased directories must be under this hierarchy as well,
and this is in fact more important for them since they commonly aren't
under the DocumentRoot.
The default value for this option is
, where 'PREFIX' comes from the value of the
option, explicit or implied.
Another one of
suexec's restrictions is that the user it's
being asked to execute the script as mustn't be considered 'privileged.' On
Linux and other Unix-like systems this generally means that it mustn't be the
root user, but
suexec takes this a step further and
will refuse to execute as any user with a group ID less than the value of this
The default value for this option, if not specified, is
This specifies the name of the file to which the wrapper will report
errors and successful invocations. It is opened and accessed as
root, but closed before control is passed to the script.
default for this option is PREFIX
'PREFIX' is the value from the
Not only is the list of environment variables examined and sanitized before
the script is invoked, but the default
PATH is set to a known list
of directories as well. This list is hard-coded at compile-time, and is defined
by this option.
The default value for
As with the
--suexec-gidmin option described earlier, this
option is used to inform
suexec of forbidden UID values. If a
request is made that would result in the execution of a script by a user with a
UID equal to or less than this value, the wrapper will log the fact and not
process the request. This foils things like a request for
The default value for this option is
This option defines the default permission mode to be applied to files
created by the script (if it doesn't explicitly set them itself). The umask is
specified as a three-digit octal number indicating which permission bits should
not be set; see the description of the
umask(1) command for
If this option isn't defined at compile-time, at run-time the
suexec wrapper will inherit the umask setting from the parent
Apache server process.
This option specifies the subdirectory underneath a user's home directory
suexec will use to find scripts for
~username-style URLs. This needs to match the setting of the
UserDir directive in your server configuration files.
suexec can only handle simple subdirectory expressions. The
more complex pattern-handling capabilities of the
module (which implements the
UserDir directive) cannot be used
--suexec-userdir setting is
If you want to change the location of the
suexec binary, you
can do so by adding a new definition of
SUEXEC_BIN to the
% env CFLAGS="-Wall -DSUEXEC_BIN=\"/usr/local/web/apache/suexec\"" \
> ./configure --enable-suexec ...
You should be extremely cautious about changing other definitions, such as
HTTPD_ROOT, however, since
suexec isn't the only part
of Apache that uses them.
User IDs Suexec Will Use
Since the point of
suexec is to handle certain Web requests
under a different identity than the Apache server user, there needs to be some
way to specify just which user. There are two places from which Apache
will draw this information:
The username from URLs such as
Group directives in the server
The username to use is determined by checking these in the above order.
Group directives are ordinarily
<VirtualHost> containers, but in a
suexec-enabled server they take on new meaning for the virtual
host, defining the identity under which CGI scripts requested through that host
will be executed. If a virtual host doesn't have a
it inherits the server-wide value (which defines the username under which the
server itself is running) which will probably result in normal,
Incorporating Suexec Into Your Apache Server
If you have an Apache 1.3 server binary, it's capable of using a
suexec wrapper if it finds one in the expected place. (Until
Apache 1.3.11, there was no convenient way to find out what the 'expected
place' is; as of version 1.3.11, you can find out the value of the
SUEXEC_BIN compile-time constant, and whether there's a valid
wrapper at that location, with the '
httpd -l' runtime
If you're working with an Apache server that you inherited, or installed as
part of a package, you might not be sure whether
suexec is in
place or being used. If you want to be sure about it, the best thing to
do is to use the Apache build procedure, which will dot the Is and cross the Ts
when you '
The main mechanism
suexec uses to ensure safety is to rely on
a bunch of settings made at compile-time. Likewise, the only way Apache can be
made to even think about using
suexec is it if has been
compiled with that in mind. This means that you'll probably need to compile
both the Apache server and
suexec yourself. This is easily done as
part of the normal Apache build. Just use the following command and the rest is
% cd ./apache-1.3/
% ./configure \
> --enable-shared=max \
> --enable-module=most \
> --with-layout=Apache \
> --prefix=/usr/local/web/apache \
> --with-port=80 \
> --suexec-enable \
> --suexec-caller=nobody \
- The Red Hat 6.1 Apache RPM actually installs
default, which may cause you problems. If you don't want it, you'll need to
either rebuild Apache or disable the
If your Apache installation is currently
suexec-enabled, it's very
simple to turn the wrapper off. Just do one or more of the following to the
Change the owner to be someone other than
Delete or rename it
and then restart the Apache server. Doing any one of these will render the
facility unusable, and Apache won't even try to involve it.
To verify that your action has had the desired effect, verify (if you're
running Apache 1.3.11 or later) with the
" command. If
the output says
is enabled, you haven't done enough yet.
Testing Your Installation
The simplest way to verify that
suexec is functioning properly
is to install a script that will tell you the username under which it's being
# cd /usr/local/web/apache/cgi-bin/
# cat > showuser.cgi << EOS
echo "Content-type: text/plain"
# chmod 755 showuser.cgi
# chown user1.group1 . ./showuser.cgi
(By calling it "
showuser.cgi" you can copy it
directly into a user's directory without having to rename it. Filename
extensions on scripts in
ScriptAliased directories are ignored, so
it does no harm to keep the
Note that the
cgi-bin/ directory isn't under the DocumentRoot,
which is why the
--suexec-docroot value was bumped up one
level--that way it covers both the ServerRoot (including the
cgi-bin/ directory) and the DocumentRoot.
Since there are two ways in which
suexec can be invoked, you
should test both of them:
- First, create a
<VirtualHost> container (or use an
existing one) in your server configuration files, and add
Group directives to it. Pick some username and group that are
different from the normal server user. Next, make sure that you have a
ScriptAlias directive that points to the directory where you put
your test script. Next, make sure that the
cgi-bin/ directory and
the test script are owned by the user and group you've chosen, and are mode
755. Finally, (re)start the Apache server and request the test script with some
you get an error, examine the server error log and the
- User directory
- To test that
suexec will properly handle a CGI script in a
user's directory, copy your
showuser.cgi script into that user's
public_html/ directory, make sure that both the script and the
public_html/ directory itself are mode 755 and owned by the user,
and then request the script with a URL such as
<URL:http://myhost/~user/showuser.cgi>. If you get an
error page, look at the Apache and
suexec problem can be frustrating, particularly
since almost any problem with a CGI script in a
suexec-enabled environment turns out to be related to the wrapper.
The typical warning signal of a
problem is a request
for a CGI script that results in a '500 Internal Server Error' page. The
appropriate response behaviour to such an error is to look in the server's
error log. Unfortunately, because the wrapper is applying its own restrictions
and rules on the script, the server log may be quite unrevealing, containing
only a single line such as the following for the failed request:
[Sun Dec 26 20:02:55 1999] [error] [client n.n.n.n] Premature
end of script headers: script
The real error message will be found in your
(which is located at
according to the assumptions section of this
suexec error message may look like this:
[1999-12-26 20:02:55]: uid: (user/user) gid: (group/group) cmd: test.cgi
[1999-12-26 20:02:55]: command not in docroot (/home/user/public_html/test.cgi
Here are a couple of other common
suexec error messages:
directory is writable by others: (path)
target uid/gid (uid-1/gid-1) mismatch with directory
(uid-2/gid-2) or program (uid-3/gid-3)
If it's still not clear what's going wrong, review the list of requirements
and make sure they're all being met.
"Danger, Will Robinson!"
suexec-enable your Apache Web server, a lot of
CGI scripts in
ScriptAliased directories will be executed
under the identity of the username specified in the
CGI scripts in user directories (as specified by the
USERDIR_SUFFIX definition, set by the
--suexec-userdir option) will be executed as the owning user if
and only if
the script was requested using the
all of the ownership and permission requirements are met
~username URL format is used but the
permissions/ownerships aren't correct, the result will be a '500 Internal
Server Error' page, not the script being executed by the server user as
in a non-
CGI scripts in all user directories accessed through
~username URLs will go through the
process--even those that you didn't consider or expect.
One effect of these changes is that previously-functioning user scripts may
suddenly begin to fail, giving the visitor the fatal '500 Internal Server
Error' page, and giving you, the Webmaster, an unrevealing
Premature end of script headers" message in the server
error log. This is where it becomes easy to get frustrated by simply forgetting
to check the
suexec error log.
Another aspect of the use of
suexec is that, if you have
virtual hosts with different
they cannot share
ScriptAliased directories--because one of the
requirements is that the script and the directory must be owned by the user and
suexec is being told to use. So you may have to duplicate a
lot of your
cgi-bin/ stuff into per-vhost directories that
are owned and protected appropriately.
Frequently Asked Suexec Questions
wrapper isn't perfect, and some aspects of its
design result in it being less than ideally suited to all environments. Here
are some of the more common questions, changes, and enhancements that come up
again and again:
- The single
--suexec-docroot value is irksome. I have 50
virtual hosts with
DocumentRoot values like
/vhost2, and so on. The only way I can get
work with these is to use
--suexec-docroot=/, which hardly seems
- This is unfortunately the way it is with the
suexec that comes
with Apache up through version 1.3.11. The value you specify for
--suexec-docroot must be an ancestor of all of the
~username documents that use it. This restriction
may be lifted in a future version, but even then it would require
settings specified at compile-time, such as with something like
- I only want
suexec to be used in certain directories or
- As of Apache 1.3.11,
suexec is an all-or-nothing proposition.
If it's available and enabled, it will be used in all cases when a CGI script
is invoked. A future version of Apache may provide a means of controlling this
with greater granularity.
- Why don't the Apache CGI error messages say there's a problem with
- Because Apache really doesn't know that for a fact. All it knows is that
called an internal function to invoke the CGI, and the interaction with the
script failed as described in the error message. The error might have been
caused by a failure to meet
suexec's requirements, or it may have
been the result of a bona fide error in the script itself.
- Why aren't
suexec's error messages logged in the Apache
- In order for the messages from
suexec to appear in the main
server's log, they would have to actually be passed to Apache so that Apache
did the logging. Not only is this inappropriate for the Web server to do, but
there would be additional confusion about into which error log the
messages should go.
There are a few articles on the Web about working with the
suexec wrapper. Don't neglect the
man page included
with the source; you can view it directly with
% cd ./apache-1.3/src/support/
% man ./suexec.8
You can also find some documentation at the following URLs:
suexec application is a double-edged sword. It allows you to
execute scripts under other personæ than the basic server
user--but it can also cut you unexpectedly if you're not careful. A single
misconfiguration can break all of your CGI scripts, so consider and plan
carefully, and test thoroughly, before implementing the wrapper on your
Got a Topic You Want Covered?
If you have a particular Apache-related topic that you'd like covered in a
future article in this column, please let me know; drop me an email at <coar@Apache.Org
>. I do
answer my email, usually within a few hours (although a few days may pass if
I'm travelling or my mail volume is way up). If I don't respond within what
seems to be a reasonable amount of time, feel free to ping me again.
About the Author
Ken Coar is a member of the Apache Group
and a director and vice president of the Apache Software Foundation. He is also a core
member of the
open-source Java compiler project, a contributor to the
PHP project, the author of
Apache Server for Dummies,
and a contributing author to Apache
Server Unleashed. He can be reached via email at <email@example.com>.
Appendix: Building Apache at Lightspeed
If you need to build Apache from source in order to add or change the
suexec parameters, you can use the following commands as a
quick-start. You should download the latest released version of the Apache
tarball and unpack it into a working directory. The top-level directory will
./apache-1.3, which matches assumption #1 described earlier.
% cd ./apache-1.3
% env CC=gcc CFLAGS="-O2 -Wall" \
> ./configure --enable-shared=max --enable-module=most \
> --with-layout=Apache --prefix=/usr/local/web/apache \
> --with-port=80 \
> --enable-suexec \
> --suexec-caller=nobody \
> --suexec-docroot=/usr/local/web \
Configuring for Apache, Version 1.3.12-dev
+ using installation path layout: Apache (config.layout)
+ Warning: You have enabled the suEXEC feature. Be aware that you need
+ root privileges to complete the final installation step.
Creating Configuration.apaci in src
[more configuration output]
[lots of compilation output]
% make install
[lots more output describing file placement]
% /usr/local/web/apache/bin/apachectl start
If you didn't encounter any errors, you should now have a working Apache
installation in the location that matches assumption
#2 described earlier. It's been built to include
you should verify that this is the case:
% /usr/local/web/apache/bin/httpd -l
suexec: enabled; valid wrapper /usr/local/web/apache/bin/suexec
It's far beyond the scope of this article to give any more information
about building Apache. If you'd like to see an article in this column about the
details of building Apache, let me know.