suEXEC HowTo for Apache on MacOS X Server

The suEXEC module of Apache enables cgi scripts to be executed with the permissions of another user that is different from the default owner of the httpd process. This is safer than executing cgi scripts with Apache’s permissions.

This tutorial describes how to enable the suEXEC module on MacOS X Server 10.6.

suEXEC Wrapper

Check the version of Apache httpd that you have installed.

  • httpd -v
  • In MacOS X Server 10.6.8, this shows httpd is version 2.2.24.

Grab the source for the corresponding version of Apache httpd and compile it with suexec support, then copy the suexec binary to the expected location (as super user):

>mkdir -p /usr/local/src
>cd /usr/local/src
>curl -O http://archive.apache.org/dist/httpd/httpd-2.2.24.tar.gz
>tar zxvf httpd-2.2.24.tar.gz
>cd httpd-2.2.24
>./configure --with-layout=Darwin --enable-suexec --with-suexec-caller=_www --with-suexec-uidmin=500 --with-suexec-gidmin=20 --with-suexec-docroot=/srv/ --with-suexec-userdir=Sites
>sudo cp support/suexec /usr/sbin/
>sudo chown root:_www /usr/sbin/suexec
>sudo chmod 4750 /usr/sbin/suexec

The last two lines make suexec owned by user root, group _www, and make it setuid root, but only executable by root and users in the group _www.

Now restart Apache with:

>/usr/sbin/apachectl -k graceful

Check that you see a line like this in your Apache httpd error log, by default /var/log/apache2/error_log:

[Wed May 28 13:17:22 2014] [notice] suEXEC mechanism enabled (wrapper: /usr/bin/suexec)

suEXEC Config

Of course, that did not work, because we haven’t told Apache to allow the suEXEC module for a certain virtual host and cgi directory, yet.

To avoid interference with existing cgi scripts, we add a new virtual host, let’s say for port 3689 and document root /srv/www3689, something like the following:

<VirtualHost *:3689>
        # document root
        DocumentRoot "/srv/www3689"
        DirectoryIndex index.html index.php
        <Directory "/srv/www3689">
                Options All -ExecCGI -Includes FollowSymLinks
                AllowOverride None
        # cgi directory
        ScriptAlias /cgi/ /srv/www3689/cgi/
        <Directory "/srv/www3689/cgi">
                Options +ExecCGI
                AllowOverride None

Also add “Listen *:3689″ to /etc/apache2/sites/virtual_host_global.conf.

The cgi scripts in the “cgi” directory of the virtual host “/srv/www3689″ are enabled by the option “+ExecCGI”. That’s the standard CGI setup.

But instead of the standard setup, which executes the scripts with the owner _www, we would like the cgi scripts to be executed by a dedicated user named “cgi-user” using the suEXEC module. So we enable the suEXEC module in the virtual host definition:

        SuexecUserGroup cgi-user _www # user and group

suEXEC Module

And of course, that did not work, because the installed version of Apache on MacOS X 10.6 does neither have the “mod_suexec” module loaded nor installed. We have to compile and load the module as shared object by ourselves.

This means, we use the apache tool “apxs” to compile the shared module. More about the tool on the apxs man page.

For a module “mod_x” to be compiled as shared object, the apxs usage is as follows:

apxs -i -a -c mod_x.c

The tool automatically copies the compiled module to the system’s libexec directory.

So, in order to install the mod_suexec module, we use the following command line:

apxs -i -a -c /usr/local/src/httpd-2.2.24/modules/generators/mod_suexec.c

Finally, we load the module in /etc/apache2/httpd.conf:

LoadModule suexec_module libexec/apache2/mod_suexec.so

Restarting the server gave the following error_log:

Warning: SuexecUserGroup directive requires SUEXEC wrapper.

That’s because Apache assumes the suexec wrapper to be installed in some specific path, but apparently it can’t find the wrapper there. What is worse, we do not know the actual path where Apache is searching for the wrapper.

So we need to find out where httpd assumes the suexec binary to reside:

httpd -V | grep SUEXEC_BIN

This yields /usr/bin/suexec on MacOS X Server 10.6.8. That’s quite different from a default Apache installation, which uses /usr/sbin/ or /usr/local/apache2/sbin/. But well, let’s copy the wrapper there and give it appropriate permissions again.

Restarting the server

sudo apachectl -l graceful

now yields

[Wed May 28 13:17:22 2014] [notice] suEXEC mechanism enabled (wrapper: /usr/bin/suexec)

in the /var/log/apache2/error_log.


For the suexec wrapper to work, the suexec log directory needs to be present so that suexec is able to write its logs. But what is tha actual log file path? We find it out as follows:

sudo strings /usr/bin/suexec | grep "suexec_log"

On MacOS X Server 10.6.8 this yields /usr/local/apache2/logs/suexec_log.

So we create the suexec log directory /usr/local/apache2/logs/:

sudo mkdir -p /usr/local/apache2/logs

And then test suEXEC as follows.

suEXEC Test

Supposed we have a simple test script “test” in the cgi script directory (/srv/www3689/cgi):


echo "Content-type: text/html"
echo ""

echo '<html>'
echo '<head>'
echo '<title>suEXEC Test</title>'
echo '</head>'
echo '<body>'
echo '</body>'
echo '</html>'

exit 0

Then we need to give the script and the cgi directory the required ownership and permissions, since the suEXEC module is very strict about those:

sudo chown cgi-user:_www /srv/www3689/cgi
sudo chmod 755 /srv/www3689/cgi

sudo chown cgi-user:_www /srv/www3689/cgi/*
sudo chmod 700 /srv/www3689/cgi/*

And restart Apache:

>/usr/sbin/apachectl -k graceful

Pointing your browser to the www.yourserver.com:3689/cgi/test script should output the current executing user, that is “cgi-user”.

If that’s true, you are done! You now have a working suEXEC. See here for an example, where the cgi-user was chosen to be “subversion”:

If suEXEC is not turned on, however, the output is “_www”, which is the default Apache httpd owner. See here for an example:

suEXEC Fin

Scripts in the cgi directory are now being executed with the permissions of a regular user. They are no longer executed with Apache’s permissions. If the cgi scripts haven’t been tailored very carefully, the latter might pose a security risk.

Using suEXEC, you now have a CGI sandbox to play with.