How to set up suexec to work with virtual hosts and PHP

Introduction

Suexec is a mechanism supplied with Apache that allows to execute CGI scripts as the user they belong to, rather than Apache's wwwrun user. This improves security in situations where multiple mutually distrusting users have the possibility to put CGI content on the server.

Example:
http://www.lll.lu/~alain/myscript.cgi would be executed as user alain rather than wwwrun. Hence, the script could not spy on private data of http://www.lll.lu/~transfai/petition.cgi belonging to a different user.

Suexec and virtual hosts

By default, mod_suexec's SuexecUserGroup directive only works for directories under the global document root (such as /src/www/htdocs):
<VirtualHost *>
 Servername alain.hitchhiker.org.lu
 SuexecUserGroup alain users
 DocumentRoot "/srv/www/htdocs/alain"
</VirtualHost>
It does not usually work for sites kept under a user's private root:
<VirtualHost *>
 Servername alain.hitchhiker.org.lu
 SuexecUserGroup alain users
 DocumentRoot "/home/alain/public_html"
</VirtualHost>
In order to allow putting virtual hosts into a user's personal directory, apply the mod_suexec.patch, and then use the SuexecUserdir directive to set the user name:
<VirtualHost *>
 Servername alain.hitchhiker.org.lu
 SuexecUserdir alain users
 DocumentRoot "/home/alain/public_html"
</VirtualHost>
N.B. the patch has been submitted to the Apache bugzilla as a feature request (number 43652), and may be downloaded as attachment 22641.

 

Alternatively, if you don't feel like patching existing Apache code, you can download the mod_mysuexec module together with mod_suexec.h, which can be compiled using:

apxs2 -c mod_mysuexec.c
After compiling this, copy the resulting .libs/mod_mysuexec.so file to your Apache modules directory (/usr/lib/apache2/modules/ on Ubuntu), and use it using the SuexecUserdir directive.

Moreover, the mod_mysuexec module supports its SuexecUserdir also in <Directory> contexts, in addition to <VirtualHost>.

Suexec functionality for PHP

On a standard Apache installation, suexec does not work for PHP, as PHP is implemented using a dynamically loadable module that runs within the context of Apache. All PHP scripts thus run under the same user as Apache itself runs as (www-data)

In order to allow users to safely execute PHP scripts, this paradigm must be changed: rather than using mod_php, user's PHP scripts should be executed via php-cgi. There are two steps necessary to make this happen:

  1. Disable mod_php
  2. Enable php-cgi
These 2 steps will be described in the next sections.

Disabling mod_php

The easiest method is to simply not install libapache2-mod-php5. However, php-cgi is less performant than mod_php and you might want to keep mod_php for safe system-wide web applications such as mediawiki, Horde/IMP, etc. Thus a solution is needed to only disable php for the users' home directories. This can be achieved using the following Apache config:
<Directory /home>
 php_admin_flag engine off
</Directory>
Using php_admin_flag rather than simply php_flag makes sure that users can't re-enable php using their .htaccess file.

Enabling php-cgi

There are two ways to do it: suphp or suexec

Suphp

As of September 2008, the most recent released version of suphp does not yet support public_html. However, there is an unreleased snapshot that does:
http://www.suphp.org/download/suphp-SNAPSHOT-2008-03-31.tar.gz

Unpack this and compile it:

tar xfzv suphp-SNAPSHOT-2008-03-31.tar.gz
cd suphp-SNAPSHOT-2008-03-31
./configure --with-apxs=/usr/bin/apxs2 --with-setid-mode=owner
make
make install
and activate it by putting the following into Apache's config:
LoadModule suphp_module /usr/lib/apache2/modules/mod_suphp.so
<Directory /home>
AddHandler application/x-httpd-php .php .php3 .php4 .php5 .phtml
suPHP_AddHandler application/x-httpd-php
suPHP_Engine on
</Directory>
and the following in /usr/local/etc/suphp.conf:
[global]
webserver_user=www-data
docroot=/var/www:${HOME}/public_html
check_vhost_docroot=false

[handlers]
;Handler for php-scripts
application/x-httpd-php="php:/usr/bin/php-cgi"

Suexec

Another possibility is to instruct Apache to handle PHP scripts through suexec, just like any other scripts:
<Directory /home>
AddHandler cgi-script .php .php3 .php4 .php5 .phtml
</Directory>
In this mode, all PHP script need to have their execute bit set:
find /home -name '*.php' -print0 | xargs -0 chmod u+x
The suexec binary usually only handles executables or scripts specifying their interpreter using a hash-bang line. Such a line is usually not specified in PHP scripts. Fortunately, on Linux, you can specify the interpreter using /proc/sys/fs/binfmt_misc
echo ':PHP:E::php::/usr/bin/php-cgi:' > /proc/sys/fs/binfmt_misc/register
Note: On some distributions, such as CentOS, it may be necessary to set cgi.force_redirect=0 in the php.ini file for CGI in order to avoid REDIRECT_STATUS CGI security alerts.

testing it

You may test correct operation with the following php script:
<?
print "hello world<p>\n";
system("id");
?>
Valid HTML 4.01 Transitional