Before, running different PHP applications on the same web server was somewhat hard to secure. Basically, you had only 2 options::
Good performances, but poor security, because all apps ran with the same UID/GID
Good security level, but not so good performances, thanks to suexec or other techno
Nowadays, you can use PHP-FPM. You all know PHP, but not all of you know FPM. FPM stands for "FastCGI Process Manager", it allow you manage PHP process with some interesting features:
specific UID/GID
chroot
specific minimum, maximum and spare process number
Using PHP-FPM, you'll be able to reach a good isolation level for your PHP apps. I'm not going to deeply analyze PHP security features. Instead, I'll just explain a way to get PHP-FPM working.
Preparation
I won't explain in detail howto install different components. Others have already done it, much more better then I could.
We're going to see, in a practical way, how to make the best use of PHP-FPM features to deploy and setup PHP apps:
An application can be domain/vhost specific. If true, it's managed by domain PHP-FPM pool.
Application can be mutualized for many domains/vhosts. It'll have its own PHP-FPM pool.
Each pool have its own UID/GID
If possible, each pool have to run inside a chroot.
chroot setting is not easy. During my tests, I figure that one of my app wasn't able to perform DNS resolution. For sure, one library was missing, so take care of it if you want to try.
Anyway, we need a basic directory tree, whatever could the future app be. Let's say web root is /var/www/.
Then, hosted domains will be in /var/www/domains/, mutualized apps in /var/www/apps/.
For more details, you can refer to my previous publications concerning Apache or Nginx (only in french for the time being, sorry):
As a summary, here is what our structure looks like for domain domain.tld:
For a virtual app appname, no big difference:
Some details:
config directory will host web server and PHP-FPM related config files
private will host specific files from application. No code here, will be in docroot, but PHP sessions for example. This directory will be readable only by app itself or server admins.
tmp... really need explanation ? ;-)
PHP-FPM installation
No surprise here, I use the excellent work from Guillaume Plessis, DotDeb maintainer.
Once installed, it's time to begin configuration. PHP-FPM will start a root process which will manage some pools. Theses pools will run under specified UID/GID, all parameters we are about to define.
PHP-FPM configuration
Under Debian, you'll have to modified file /etc/php5/fpm/php5-fpm.conf, like this:
As you can see, global config use only few options. The real stuff stands in the last 2 lines which allow:
Domain pools activation
Application pools activation
Sample domain pool
First pool to be configured is a domain pool. It'll run with specific UID/GID. By default, it'll be chrooted so that it can only see domain directory tree. Finally, some PHP configuration options will be overwirtten
Domain pools will use UID between 9001 and 9999. That will also be the TCP port which pool will listen on, waiting for requests.
Some explanations:
Pool will listen on loopback interface (127.0.0.1) using TCP port 9001.
Pool will run as user and group domain.tld.
Once started, pool will be chrooted in /var/www/domains/domain.tld.
PHP web root will be /var/www/domains/domain.tld/docroot or better said /docroot if chrooted.
Temporary dir will be /var/www/domains/domain.tld/tmp or /tmp if chrooted.
Each request could not last more than 2 sec (php_value[max_execution_time] and /or request_terminate_timeout options).
Each request will have 1M of memory, as a maximum (php_admin_value[memory_limit] option).
You may ask why slowlog options still use non-chrooted path. Quite simple: long running scripts detection is made by root PHP process and this process is not chrooted.
You could centralise slowlogs into one file, but having it into separate give the webmaster the oportunity to have a look on them if he has a FTP or SSH access, even limited.
OK, now we've configured our pool, let's create associated user domain.tld:
You notice --force-badname use. It's mandatory if you want to use dot in username, which is somewhat usefull as username should reflect domain name. I did not see any side effect for now, but feel free to do it another way.
Sample application pool
This sort of pool will run with specific identity. By default, it will also be chrooted so that it can only access its directory tree. Finally, some of the PHP parms will have dedicated values for this pool.
Applications shared between domains will use gid/uid between 10001 and 10999. This value will also be PHP-FPM TCP port.
Nothing special here. All has already been explained at previous step. Once more, slowlog option is not impacted by chroot. Of course, you have to create the user which will be used:
Well. We now have installed PHP-FPM, setted up the root process, setted up 2 pools, one for domain and the other one for a shared application.It's now time to configure NGinx so that they could be reachable.
Nginx configuration
I won't explian nginx installation, as it's really trivial under Debian. Eventually, you'll have little troubles to get backport version installed instead of stable one. As usual, Google is your best friend ever !
NGinx will be configured as follow:
Default vhost with pool's monitoring URLs
Even if chrooted, it's always a good idea to monitor things.
URLs entry points for your pools.
Theses URLs definition, using Location in Nginx, will be included in each impacted vhost.
As you can see, default chost will load each monitoring URL (file nginx_status.conf) for each PHP-FPM pool. "domain.tld" vhost will load each enabled app configuration file.
For applications, nothing strange or difficult, at least for those how already speak NGinx syntax:
Now you need common FastCGI configuration file:
Tests
Once done, it's time to test. Just create sample "phpinfo" file here: /var/www/domains/domain.tld/docroot/index.php and another one here: /var/www/apps/appname/docroot/index.php with following content:
Then, check with you browser following URLs:
Domain pool
http://domain.tld/
Application pool
http://domain.tld/appname/
You should see the well known PHPInfo. If not then... have a break and take back from the beginning ;-)
I spend most of my free time on the Internet working on GNU/Linux with Debian or CentOS, virtualization with Xen and KVM technology, as well as cluster stacks with corosync and OpenAIS. Particularly interested in Linux, Netfilter, virtualization, monitoring and clusters, most of my personal works are published on this website and others should not delay. By way professional, I manage servers running RedHat or CentOS and VMware ESXi farm. From time to time, I manage to drop my keyboard and read a book while listening to music, but it never lasts long.
e-mail : jean point baptiste point favre arobase gmail.com