I have done this a number of times using nginx, lighttpd, and apache. The concept is basically the same in all of them: [Fast]CGI processes that run as the user.
If you want to save yourself a lot of trouble, try messing with permissions as other people have suggested. In particular, if your user websites are owned by the users and your main site is owned by root or similar, you will be in decent shape. But users will still not be segregated and could read eachother's files etc (database passwords?). Running separate virtual servers for each user/site will give you the most security, and it is certainly simpler than trying to segregate users with apache.
The standard accepted way of segregating PHP users in apache is via SuExec (there is also mpm-itk). If you Google for apache fastcgi suexec
or similar, you should get plenty of results. I think it would be impractical to include full directions here. It can vary based on various design decisions and the distribution in use. Instead, I will try to provide some points I consider important and a brief description of my configuration. My setup gets pretty complicated (and on top of this, I run a custom SELinux module). Here is a [hopefully] brief overview:
- Name based virtual hosts by pointing *.somedomain to the server: user.somedomain is the name of the virtual host (I assume you could do the same thing with user directories?).
- One
/etc/httpd/vhosts.d/user.conf
for each user.
- One directory in
/var/www/vhosts_config
for each user.
/var/www/vhosts_config/$USER/wrapper-bin
contains the wrapper to execute PHP (more on this later)
/var/www/vhosts_config/$USER/php/php.ini
: I like to have a separate php.ini
per user.
/var/www/vhosts_config/$USER/php/ext/*.ini
: One ini file per available extension
/var/www/vhosts_config/$USER/php/ext-active/*.ini
: symbolic links back to ../ext
for each enabled extension (so I can enable/disable extensions per user too)
/var/www/vhosts/user
contains the actual content for the vhost. I recommend always using a public_html
subdirectory as the document root so users have a directory that is outside of the document root to store configuration files etc.
- Users should not be able to read each other's files. You need to allow the webserver
+x
on the users'. There are a variety of ways you could do this. I use POSIX ACLs.
The per-user httpd config looks something like this (altered for brevity):
<VirtualHost *:80>
ServerName someuser.somedomain
DocumentRoot /var/www/vhosts/someuser/public_html
SuexecUserGroup someuser someuser
FcgidWrapper /var/www/vhosts_config/someuser/wrapper-bin/php .php
#max of 5 cgis per vhost
FcgidMaxProcessesPerClass 5
FcgidMinProcessesPerClass 0
AddHandler php-fcgi .php
Action php-fcgi /wrapper-bin/php
Alias /wrapper-bin/ /var/www/vhosts_config/someuser/wrapper-bin/
<Location /wrapper-bin/>
Options +ExecCGI
SetHandler fcgid-script
</Location>
</VirtualHost>
suexec
is a setuid
root program that apache invokes to start processes as a given user. It is pretty fussy. It requires that the program it executes is owned by the user and contained in a subdirectory of /var/www
(this varies on different Linux distributions and is set at compile time) directory that is owned by the user. This is why each user needs his own PHP wrapper script.
/var/www/vhosts_config/someuser/wrapper-bin/php
looks something like:
export PHPRC=/var/www/vhosts_config/someuser/php
export PHP_INI_SCAN_DIR=$PHPRC/ext-active/
export PHP_FCGI_CHILDREN=0
umask 0077
exec /usr/bin/php-cgi
PHP_INI_SCAN_DIR
sets a directory to look for additional php.ini
files, which I use for loading extension-specific configuration.
PHPRC
lists the directory in which to find the php.ini
file.
You do not have to use a separate PHP configuration for each user, and doing so certainly complicates things. I like to give users a stripped down configuration and enabled only what is necessary (and a few common extensions by default). I also load suhosion with different configurations for each user (different encryption keys).