HowTo: Apache2, SuExec, PHP5 and FastCGI for Virtual Domains

оригинал статьи тут

These notes are specific to Ubuntu 7.10 (Gutsy) but can be easily adjusted for other distributions.

Running PHP v5 on Apache v2.x requires the use of CGI and php-cgi if the recommended apache2-mpm-worker configuration is to be maintained. This prevents the use of libapache2-mod-php5 because that module requires apache2-mpm-prefork — these two mpm’s are mutually exclusive.

Check what is installed; first Apache:

Code: $ dpkg-query -l ‘apache2*’ | egrep ‘^ii’

ii apache2 2.2.4-3build1 Next generation, scalable, extendable web server

ii apache2-mpm-worker 2.2.4-3build1 High speed threaded model for Apache HTTPD

ii apache2-utils 2.2.4-3build1 utility programs for webservers

ii apache2.2-common 2.2.4-3build1 Next generation, scalable, extendable web server

And now PHP:

Code: ~$ dpkg-query -l ‘*php*’ | egrep ‘^(ii|un)’

un libapache2-mod-php4 <none> (no description available)

un libapache2-mod-php5 <none> (no description available)

un php-doc <none> (no description available)

un php-pear <none> (no description available)

un php3 <none> (no description available)

ii php5-cgi 5.2.3-1ubuntu6.2 server-side, HTML-embedded scripting languag

ii php5-common 5.2.3-1ubuntu6.2 Common files for packages built from the php

un php5-json <none> (no description available)

ii php5-mysql 5.2.3-1ubuntu6.2 MySQL module for php5

un php5-mysqli <none> (no description available)

un phpapi-20060613 <none> (no description available)

The FastCGI module:

Code: $ dpkg-query -l ‘*fcgi*’ | egrep ‘^(ii|un)’

ii libapache2-mod-fcgid 1:2.1-2 an alternative module compat with mod_fastcgi

The ii at the beginning of each line above denotes installed. Some package have dependencies on others. Use apt-get to install the packages and their dependencies if they aren’t already:

Code: $ sudo apt-get install apache2 php5-cgi libapache2-mod-fcgid

When using the Virtualmin virtual hosting management tool the home directories of each domain are under /home/. This differs from the Apache convention of /var/www/ and causes issues with suexec refusing to execute CGI programs due to permissions problems.

Two examples of errors from /var/log/apache2/suexec.log:

Code: uid: (1014/intuitivenipple) gid: (1013/1013) cmd: php-fcgi-wrapper

cannot stat program: (php-fcgi-wrapper)

uid: (1014/intuitivenipple) gid: (1013/1013) cmd: php-fcgi-wrapper

target uid/gid (1014/1013) mismatch with directory (0/0) or program (0/0)

The first error is caused because ‘php-fcgi-wrapper’ is a symbolic link to /usr/bin/php-cgi and suexec doesn’t allow sym-links.

The second error is caused because the directory and the executable do not have owner and group that match the ones set in the site’s VirtualHost configuration (/etc/apache2/sites-available/domain.net.conf):

Code:

<VirtualHost domain.net>

SuexecUserGroup «#1014» «#1013″

The suexec program has settings built in when it is compiled to prevent it being reconfigured to avoid the security restrictions it is designed to enforce.

If you are using Virtualmin (or more accurately, not using /var/www/ as the root for your virtual domains) you will need to build a custom version of apache’s suexec to adjust those built-in settings. You can view them like this:

Code: $ sudo /usr/lib/apache2/suexec -V

-D AP_DOC_ROOT=»/var/www»

-D AP_GID_MIN=100

-D AP_HTTPD_USER=»www-data»

-D AP_LOG_EXEC=»/var/log/apache2/suexec.log»

-D AP_SAFE_PATH=»/usr/local/bin:/usr/bin:/bin»

-D AP_UID_MIN=100

-D AP_USERDIR_SUFFIX=»public_html»

We need to change to AP_DOC_ROOT=»/home» for Virtualmin.

Install the tools we need:

Code: $ sudo apt-get install build-essential binutils

Create a directory to keep source-code separate and then fetch the source for apache2.2-common (which contains the suexec code):

Code: $ mkdir SourceCode

$ cd SourceCode

$ sudo apt-get build-dep apache2.2-common

$ apt-get source apache2.2-common

Reading package lists… Done

Building dependency tree

Reading state information… Done

NOTICE: ‘apache2’ packaging is maintained in the ‘Svn’ version control system at:

svn://svn.debian.org/pkg-apache

Need to get 6485kB of source archives.

Get: 1 http://archive.ubuntu.com gutsy/main apache2 2.2.4-3build1 (dsc) [1235B]

Get: 2 http://archive.ubuntu.com gutsy/main apache2 2.2.4-3build1 (tar) [6366kB]

Get: 3 http://archive.ubuntu.com gutsy/main apache2 2.2.4-3build1 (diff) [118kB]

Fetched 6485kB in 3s (1849kB/s)

gpg: Signature made Thu 04 Oct 2007 19:28:33 BST using DSA key ID 0A0AC927

gpg: Can’t check signature: public key not found

dpkg-source: extracting apache2 in apache2-2.2.4

dpkg-source: unpacking apache2_2.2.4.orig.tar.gz

dpkg-source: applying ./apache2_2.2.4-3build1.diff.gz

Check the Apache 2.2 documentation for details of all the suexec configure options and also Linode-member Internat’s guide to rebuilding the apache2.2-common Debian/Ubuntu package.

Move into the source-code directory and configure for our custom requirements:

Code: $ cd apache2-2.2.4

$ ./configure —prefix=/usr —enable-suexec —with-suexec-caller=www-data —with-suexec-userdir=public_html —with-suexec-docroot=/home —with-suexec-uidmin=100 —with-suexec-gidmin=100 —with-suexec-logfile=/var/log/apache2/suexec.log —with-suexec-safepath=’/usr/local/bin:/usr/bin:/bin’

The configure script is responsible for checking that the host PC has all the tools and code-library headers installed. If configure reports an error and mentions a missing library, then you need to install the development headers for that library. Usually the package name has the -devel suffix so, for example, if «libsomething» is missing, the first thing to try is

Code: $ sudo apt-get install libsomething-devel

If this doesn’t solve it, search for the correct development library name using:

Code: $ apt-cache search libsomething

If you still can’t find it, try a search engine or look at the Ubuntu package repositories.

Assuming configure completed successfully you should see:

Code: …

config.status: creating support/apxs

config.status: creating support/apachectl

config.status: creating support/dbmmanage

config.status: creating support/envvars-std

config.status: creating support/log_server_status

config.status: creating support/logresolve.pl

config.status: creating support/phf_abuse_log.cgi

config.status: creating support/split-logfile

config.status: creating build/rules.mk

config.status: creating build/pkg/pkginfo

config.status: creating build/config_vars.sh

config.status: creating include/ap_config_auto.h

config.status: executing default commands

With everything configured we can build the package:

Code: $ make

Assuming make completes without errors, check that suexec was built and has the correct configuration:

Code: $ ls support/suexec

support/suexec

$ sudo support/suexec -V

-D AP_DOC_ROOT=»/home»

-D AP_GID_MIN=100

-D AP_HTTPD_USER=»www-data»

-D AP_LOG_EXEC=»/var/log/apache2/suexec.log»

-D AP_SAFE_PATH=»/usr/local/bin:/usr/bin:/bin»

-D AP_UID_MIN=100

-D AP_USERDIR_SUFFIX=»public_html»

Now we need to back-up the installed suexec and put this new version in its place:

Code: $ sudo mv /usr/lib/apache2/suexec /usr/lib/apache2/suexec-var-www

$ sudo cp support/suexec /usr/lib/apache2/suexec

$ sudo chown root:www-data /usr/lib/apache2/suexec

$ sudo chmod 4750 /usr/lib/apache2/suexec

Now that suexec is ready it is time to configure the virtual domains. The directory structure is usually something like this:

/home/domain.net/

/home/domain.net/logs/

/home/domain.net/public_html/

/home/domain.net/cgi-bin/

/home/domain.net/domains/

To those we’ll add conf/ (for custom php.ini) and lib/ (for files accessible to php-cgi but outside the web-root).

/home/domain.net/conf/

/home/domain.net/lib/

As an example the pastebin package configuration and code libraries would be installed in lib/.

You will need to repeat the following steps for each virtual domain that needs FastCGI access to PHP (modifying base path and user/group settings for each). The result is that each virtual domain’s CGI scripts run as the correct user. See the following article for how to add these values to the Virtualmin domain templates so new domains are created with PHP enabled.

To execute the CGI properly the executable, and the directory it is in, must have the same user and group as those set in the site’s SuexecUserGroup statement (referred to earlier). The usual way to assure this is to set SuexecUserGroup to match the user and group of the home directory of the user.

The user’s home directory and all directories below it should already meet those requirements so we can install a script in cgi-bin/ that will call the /usr/bin/php-cgi executable.

Change to the user’s home directory and create the directories:

Code: $ cd /home/<virtual-domain-user>

$ mkdir conf

$ mkdir lib

$ mkdir -p cgi-bin/php5-default

Create conf/php.ini with this content. Make sure to adjust the paths to match the domain:

Code: include_path = «.:/home/intuitivenipple.net/lib»

open_basedir «/home/intuitivenipple.net:/tmp»

Create the script cgi-bin/php5-default/php-fcgi-wrapper with the following contents:

Code: #!/bin/sh

# Wrapper for PHP-fcgi

# This wrapper can be used to define settings before launching the PHP-fcgi binary.

# Define the path to php.ini. This defaults to /etc/phpX/cgi.

export PHPRC=/home/intuitivenipple.net/conf

# Define the number of PHP child processes that will be launched.

# This is low to control memory usage on a server that might launch

# these processes for lots of domains.

# Leave undefined to let PHP decide.

export PHP_FCGI_CHILDREN=1

# Maximum requests before a process is stopped and a new one is launched

export PHP_FCGI_MAX_REQUESTS=5000

# Launch the PHP CGI binary

# This can be any other version of PHP which is compiled with FCGI support.

exec /usr/bin/php5-cgi

Change the PHPRC path to match the user.

Make sure the directories and the script have the correct user and group ID. Replace <user> and <group> with your virtual domain’s user and group respectively:

Code: $ sudo chown -R <user>:<group> cgi-bin/php5-default

$ sudo chown -R <user>:<group> conf

$ sudo chown -R <user>:<group> lib

You can use these same steps to install other CGI scripts.

Now the virtual domain’s Apache configuration needs a block of directives adding that will activate the FastCGI PHP handler.

Many guides and tutorials show this done on a per-server basis by adding a file to /etc/apache2/conf.d/. The file is often called php-fcgid.conf.

That file will work fine with mod_user (allows access to user’s public_html via http://server.domain.net/~user) but won’t work with virtual domains where the CGI script has to be owned by the SuexecUserGroup of each Virtual Host.

If you have that already installed in /etc/apache2/conf.d/ you should consider removing it and also deleting the default FastCGI directories at /var/www/fcgi-bin.d/.

Add the following block of directives to the virtual domain’s Apache configuration file. The files are usually in the directory /etc/apache2/sites-available/.

For my site, the file is /etc/apache2/sites-available/intuitivenipple.net.conf

The file will start something like:

Code: <VirtualHost 67.18.187.60:80>

SuexecUserGroup «#1014» «#1013»

ServerName intuitivenipple.net

ServerAlias www.intuitivenipple.net

Ensure the SuexecUserGroup IDs are correct. Here, they are specified as the UID and GID numbers rather than the names (saves Apache time having to look them up). You can get them like this:

Code: $ grep intuitivenipple /etc/passwd

intuitivenipple:x:1014:1013::/home/intuitivenipple.net:/bin/sh

The first number is the UID, the second is the GID. You can cross-reference the GID to its name like this:

Code: $ grep 1013 /etc/group

intuitivenipple:x:1013:www-data

This shows that the group has www-data as a member. That is the identity that Apache runs as.

Now add the FastCGI conditional statements to the end of the virtual domain configuration file, inserting them in front of the closing </VirtualHost> directive, so it looks like this:

Code: <IfModule !mod_php4.c>

<IfModule !mod_php4_filter.c>

<IfModule !mod_php5.c>

<IfModule !mod_php5_filter.c>

<IfModule !mod_php5_hooks.c>

<IfModule mod_actions.c>

<IfModule mod_alias.c>

<IfModule mod_mime.c>

<IfModule mod_fcgid.c>

# Define a new handler «php-fcgi» for «.php» files, plus the action that must follow

AddHandler php-fcgi .php

Action php-fcgi /fcgi-bin/php-fcgi-wrapper

# Define alias «/fcgi-bin/». The action above is using this value, which means that
# you could run another «php5-cgi» command by just changing this alias
Alias /fcgi-bin/ /home/intuitivenipple.net/cgi-bin/php5-default/
# Turn on the fcgid-script handler for all files within the alias «/fcgi-bin/»
<Location /fcgi-bin/>
SetHandler fcgid-script
Options +ExecCGI
</Location>
# ensure no access to the script source code
ReWriteEngine On
ReWriteRule ^/fcgi-bin/[^/]*$ / [PT]
</IfModule>
</IfModule>
</IfModule>
</IfModule>
</IfModule>
</IfModule>
</IfModule>
</IfModule>
</IfModule>

</VirtualHost>

Make sure you set the Alias path to the correct directory! E.g.

Alias /fcgi-bin/ /home/domain.net/cgi-bin/php5-default/

Save the file then restart Apache:
Code: $ sudo /etc/init.d/apache2 restart

Add a simple PHP test file to the web-root:
Code: $ echo «<?php phpinfo() ?>» > public_html/info.php
$ chown <user>:<group> public_html/info.php

You can test it from a browser or even from the command line using wget to ensure the server returns a 200 OK response:
Code: $ wget -O — http://domain.net/info.php | head
—21:59:07— http://domain.net/info.php
=> `-‘
Resolving domain.net… 10.1.2.3
Connecting to domain.net|10.1.2.3|:80… connected.
HTTP request sent, awaiting response… 200 OK

If you see the PHP configuration reported in your browser, starting with something like «PHP Version 5.2.3-1ubuntu6.2», then you’ve successfully installed SuExec protected FastCGI access to PHP 5.

Remember to repeat the per-virtual-domain steps for each domain that requires PHP support. You can automate that to some degree by adding the directories and files to the /etc/skel/ directory so when a user is created they are copied and permissions set correctly.

If you’re working with sub-domains you can copy the parent’s cgi-bin directory to the sub-domain (assuming the parent already has the PHP CGI script installed!):
Code: $ pwd
/home/domain.net

$ cp -a cgi-bin domains/subdomain.domain.net/

and then add the FastCGI configuration directives to the sub-domain’s apache configuration by editing /etc/apache2/sites-available/subdomain.domain.net.conf to add the Fast CGI directives as described previously.

Make sure to set the Alias path correctly. For sub-domains it is of the form
Alias /fcgi-bin/ /home/domain.net/domains/subdomain.domain.net/cgi-bin/php5-default/

And of course, reload the configuration once more:
Code: $ sudo /etc/init.d/apache2 reload

If you find any mistakes in this guide please report them here and I’ll amend this article.

Comments are closed.