Debian + Apache 2.2 + FastCGI + PHP 5 + suEXEC the easy way

Mr. DOS

Member
Messages
230
Reaction score
5
Points
18
Preamble
There are literally hundreds of tutorials and howtos to be found on the web that purport to walk the budding sysadmin through configuring a web server in the best way possible. Many of these extol the benefits of running PHP through CGI under suEXEC or adding suPHP to the mix so PHP scripts run with the same permissions as the script owner. These lists of instructions wind on for pages and pages with little to no explanation as to what the procedure is doing to your precious server and are very often out-of-date or tailored for a different operating system than they claim. The purpose of this guide is to provide a concise, clear, and up-to-date tutorial on installing Apache 2.2 and FastCGI-based PHP 5 under suEXEC on a modern Debian installation. It assumes some pre-existing familiarity with *nix-based systems and Apache virtual hosts.

The Goals

  • Content stored in separate home directories and entirely owned by the user associated with the home directory
  • Web-facing PHP scripts executed by the file owner, not www-data
  • No part of the webserver configuration accessible or modifiable by the end-user

Installing Everything
Assuming a Debian 5- or 6-based system with no existing webserver installed, we need to install Apache 2.2 in worker mode (apache2-mpm-worker). Additionally, we will need some form of FastCGI for Apache (libapache2-mod-fcgid), the suEXEC wrapper (apache2-suexec-custom), and PHP itself (php5-cgi).
Code:
# apt-get install apache2-mpm-worker libapache2-mod-fcgid apache2-suexec-custom php5-cgi
suEXEC can be a bit fiddly in that the document root (the parent folder for all web content) is generally set at compile-time. This has led to numerous guides giving instructions for retrieving the Apache source and reconfiguring and recompiling the suEXEC module to set /home as the document root instead of /var/www. Fortunately for us, Debian provides a custom suEXEC package, apache2-suexec-custom, which has been modified to permit post-compilation reconfiguration by way of a simple configuration file.

Global Configuration

System
Site data will be stored in /home/user/public_html. However, this location doesn’t exist by default and it’s just one more step to forget when adding a new user. Every time a new user is created, the contents of /etc/skel/ are copied into his home directory. Therefore, if you create /etc/skel/public_html/ as root, a public_html directory will be placed into each new user’s home directory. You may also want to place a stub index.html file into /etc/skel/public_html/ to provide some placeholder content for new users.

Apache
Apache’s out-of-box configuration suitable for our needs as far as the scope of this tutorial is concerned, and therefore, will not be modified. You may wish to change things yourself at a later point, such as the DirectoryIndex directive. However, we do need to enable a few extra modules:
Code:
# a2enmod fcgid suexec actions
suEXEC
The default configuration file for apache2-suexec-custom is /etc/apache2/suexec/www-data. The first line of the file must be changed to reflect the non-default document root.

FastCGI

We will create a configuration file telling FastCGI how to run PHP in /etc/apache2/conf.d/. As any files in /etc/apache2/conf.d/ are automatically included by Apache, it can be named anything you like; this tutorial will use php5-fcgid.conf. Its contents should look something like this:
Code:
AddType application/x-httpd-php .php

AddHandler php-fcgi .php
Action php-fcgi /fcgi-bin/php5-fcgi

Alias /fcgi-bin/ /home/www-data/

<Location /fcgi-bin/>
        SetHandler fcgid-script
        Options +ExecCGI
</Location>
The first line simply tells Apache what MIME type to send for PHP files. Lines two and three establish that PHP files should be handled by an executable at /fcgi-bin/php5-fcgi, a location which is aliased in line four. Lines five through eight then configure everything in the aliased /fcgi-bin/ to be treated as a FastCGI script and to allow those scripts to execute.
You may have noticed that /fcgi-bin/ points to a location that probably doesn’t exist on your system – /home/www-data/. While this location will be overridden by individual virtual hosts, it should still be defined here as a fallback. You should also create the folder and chown it to www-data.

You may also have noticed that the handler inside /fcgi-bin/, php5-fcgi, doesn’t exist either. This file is simply a wrapper for the actual PHP 5 CGI binary (/usr/bin/php5-cgi), but because suEXEC requires all files run through it (including CGI binaries) to be inside its document root, we can’t point directly to the PHP binary. Create the file /home/www-data/php5-fcgi with these contents:
Code:
#!/bin/sh
exec /usr/bin/php5-cgi
As you can see, it’s just a simple wrapper for the actual PHP binary. Set the executable bit on the file and chown it to www-data.

Site Configuration
Having finished the global configuration, you should now be able to start adding sites and configuring virtual hosts. As an example, we will set up the server to handle requests for example.com.

System
For consistency’s sake, you should determine a scheme for mapping system users to virtual hosts. For the rest of this tutorial, we will use the second-level domain of the virtual host’s fully-qualified domain name as the account username (e.g., example for example.com).
Add the system user example with home directory /home/example/.

Apache
We need to create a virtual host for this user. A basic template has been provided below; however, any configuration will do as long as it has the same Alias and SuexecUserGroup directives.
Code:
<VirtualHost *:80>
        ServerAdmin [EMAIL="admin@example.com"]admin@example.com[/EMAIL]
        ServerName example.com
        ServerAlias [URL="http://www.example.com/"]www.example.com[/URL]

        Alias /fcgi-bin/ /home/www-data/example/
        SuexecUserGroup example example

        DocumentRoot /home/example/public_html/
        <Directory /home/example/public_html/>
                Options Indexes FollowSymLinks
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>

        ErrorLog /var/log/apache2/error.log
        LogLevel warn
        CustomLog /var/log/apache2/access.log combined
</VirtualHost>
The Alias directive overrides the alias set in /etc/apache2/conf.d/php5-fcgid to tell FastCGI that the PHP binary is in /home/www-data/example/. We do this for the sake of suEXEC; not only does suEXEC require all files run through it to be inside its document root, but both the file and the parent folder must be owned by the same user as specified in the SuexecUserGroup directive. While neither this directory nor the PHP binary FastCGI expects to find within it currently exist, we’ll create those in the next step.

SuexecUserGroup simply tells suEXEC as what user and group it should execute things.

suEXEC
No site-specific configuration is necessary for suEXEC.

FastCGI
Now we will create the site-specific PHP wrapper. First, create a subfolder of /home/www-data/ for the site (in this case, /home/www-data/example/), then copy /home/www-data/php5-fcgi into it and chown the folder and the script to your new user.

Conclusion
At this point, it should be safe to enable the new site and restart Apache (or reload, if you restarted at any point after enabling the new modules). If you’d like to make sure PHP scripts now run as your new user, you can use this PHP script:
PHP:
<?php system('whoami'); ?>
 

andrew111

New Member
Messages
1
Reaction score
0
Points
0
Great article and absolutely right about the 100 of articles that are there but don't really help.

This one is the most explanatory and in language I can nearly understand that I have seen so far and hoping you might be able to shed light on my little related task- 2 days researching so far with very slow progress due to wading thru so much stuff that ends up not relevant for one reason or another.

Slightly different in that am wanting to user UserDir instead of setting up virtual host for every user. SAAS system and every user gets there own directory under one virtual host.

E.g. wanting to secure this structure /var/www/websitename/username where username could be 1 of hundreds of different users and users shouldn't be able to access websitename but can do whatever they like from within the username directory and below. Using mod_userdir am part way there I think but can't seem to get suexec running at all yet.
 

Mr. DOS

Member
Messages
230
Reaction score
5
Points
18
I think FastCGI is probably the wrong way to tackle your problem. Because of the way FastCGI works, it'll spawn a pool of PHP processes for each user, and maintain them. This means you've either got to have enough memory to have at least one process for each user (which, with even a few dozen users is huge), or you've got to have a minimum connection pool size of 0 which involves a start-up time for PHP every time that user requires something.

I think in your case, you'd be better to approach this with either traditional suEXEC + CGI (not FastCGI), or better, something like mod_ruid2, with which I've had great success. Unlike FastCGI, mod_ruid2 is a mod for Apache 2 that changes the ownership of run-of-the-mill prefork threads or worker processes to a standard user when a request comes in, then puts ownership of them back in the hands of Apache when the request is finished. This gives you a very easy-to-maintain configuration because you can use ordinary mod_php5 with a prefork MPM (and all that that entails, including PHP accelerators), a secure setup because scripts are being run with user-level privileges, and greatly improves memory efficiency because requests are served from a shared thread/process pool instead of many smaller ones. The only thing it lacks in comparison to FastCGI is site-specific pool size, but that's not your issue here (and isn't for most people).
 

jonathonbyrd

New Member
Messages
1
Reaction score
0
Points
0
So I ran through the tutorial like I should, even went and installed a fresh Debian 6 just to make sure I had the right environment for this tutorial :)

Everything seemed to go nicely and I was able to display a "hellow world" index.html. However, I when I try to place in an index.php I get the following message.

404 Not Found
The requested URL /fcgi-bin/php5-fcgi/index.php was not found on this server.

I placed an index.php file into this directory and no change.. I went through the tutorial two and three times but was unsuccessful. Any Ideas for me?
 

multiwebinc

New Member
Messages
1
Reaction score
0
Points
1
Great article and still works with Apache 2.2.22 on Debian, however there is one small thing you didn't mention. The
Code:
/home/www-data/php5-fcgi
and all respective files for each user need to be
Code:
chmod u+x
or else you will receive the following error in your suexec.log file:

file has no execute permission: (/home/www-data/username/php5-fcgi)
 
Top