Nginx and PHP-FPM, bash script for creating new vhost’s under separate fpm pools
Using worker pools in PHP-FPM can allow you to easily separate out and isolate virtual hosts that make use of PHP. PHP-FPM allows you to run multiple pools of processes all spawned from the master one and each pool can run as a different user and/or group. Each pool can be further isolated by running in a chroot environment and by overriding the default php.ini values on a per pool basis.
Running PHP for each vhost under a different user/group can help to stop a vulnerability in one site potentially exposing another vhost, it can also stop one malicious owner of a vhost from been able to use PHP to access the files of another site owned by someone else on the same server (in a shared hosting environment).
The process of setting up the web server config and a new PHP-FPM pool for each new vhost on a server can become a rather time consuming and boring process. However as this process follows a fairly standard set of steps it can be easily scripted.
The following bash script was designed for use on Debian and Ubuntu, however it only needs a few modifications to use it on CentOS (the lines should be commented in the script).
What does the script do:
- Creates a new system user for the site
- Creates a new vhost config file for nginx using a basic template
- Creates a new PHP-FPM pool with the uid and gid set to that of the new system user
- Creates a new directory for the site, within the new users home directory
- Reloads Nginx to allow the new vhost to be detected
- Restarts PHP-FPM to generate the new pool of PHP workers
How it works:
Nginx:
Under your Nginx config directory (probably /etc/nginx) you need to create two new directories (if you don’t already have them):
In the “sites-available” dir goes the the individual config files for each vhost, each new vhost using a new file. Under the “sites-enabled” directory goes a symlink to the config file in the sites available directory, this gives you the ability to disable sites without removing the contents of the site or its config. You will also need to add the following (within the http section) to main Nginx config file (nginx.conf – lives under /etc/nginx/nginx.conf on debian) to get Nginx to automatically look in the sites-enabled directory.
http { ... # Load all vhosts ! include /etc/nginx/sites-enabled/*.conf; }
Replacing the path of the your nginx configuration if needed.
The web root of the new site will be /home/<new user>/public_html if you’re setting up hosting for a PHP site that holds most of its code outside of the web root (most MVC frameworks do this such as Symfony and FuelPHP) then you can specify the web root directory interactively when the script runs.
PHP-FPM
By default the script will put the config file for new pools under /etc/php5/fpm/pool.d, however you can easily change this at the top of the script to where ever you want. You will need to add the following line to your php-fpm.conf file (lives under: /etc/php5/fpm/php-fpm.conf typically on Debian):
include=/etc/php5/fpm/pool.d/*.conf
This will tell PHP-FPM where to look for other config files, which in this case will be the individual pool config files (one per pool).
How to use it:
Simply download the tar file at the end of the this article and extract it. If your not sure how to extract a tar file either read the man pages(man tar) or use the command below:
tar -xzf create_php_vhost.tar.gz
Once you have extracted the archive you will need to change the permissions on the create_php_site.sh file to make it executable (if it isn’t already):
chmod u+x create_php_site.sh
Then run the create_nginx_site.sh script passing to it the domain name as the only parameter.
./create_php_site.sh example.com
The script will then take you through the steps required to setup the Nginx configuration, the new system user and the PHP-FPM pool. Once all that is complete it will restart Nginx and PHP-FPM ready for your new vhost.
An example screen session is shown below:
./create_php_site.sh example.com Creating hosting for: example.com Please specify the username for this site? example Adding user `example' ... Adding new group `example' (1000) ... Adding new user `example' (1000) with group `example' ... Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully Changing the user information for example Enter the new value, or press ENTER for the default Full Name []: Example User Room Number []: Work Phone []: Home Phone []: Other []: Is the information correct? [Y/n] y Would you like to change to web root directory (y/n)? y Enter the new web root dir (after the public_html/) web How many FPM servers would you like by default: 2 Min number of FPM servers would you like: 1 Max number of FPM servers would you like: 5 Reloading nginx configuration: nginx. Restarting PHP5 FastCGI Process Manager: php5-fpm. Site Created for example.com with PHP support
The Script:
#!/bin/bash # @author: Seb Dangerfield # http://www.sebdangerfield.me.uk/?p=513 # Created: 11/08/2011 # Modified: 07/01/2012 # Modified: 27/11/2012 # Modify the following to match your system NGINX_CONFIG='/etc/nginx/sites-available' NGINX_SITES_ENABLED='/etc/nginx/sites-enabled' PHP_INI_DIR='/etc/php5/fpm/pool.d' WEB_SERVER_GROUP='www-data' NGINX_INIT='/etc/init.d/nginx' PHP_FPM_INIT='/etc/init.d/php5-fpm' # --------------END SED=`which sed` CURRENT_DIR=`dirname $0` if [ -z $1 ]; then echo "No domain name given" exit 1 fi DOMAIN=$1 # check the domain is valid! PATTERN="^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])quot;; if [[ "$DOMAIN" =~ $PATTERN ]]; then DOMAIN=`echo $DOMAIN | tr '[A-Z]' '[a-z]'` echo "Creating hosting for:" $DOMAIN else echo "invalid domain name" exit 1 fi # Create a new user! echo "Please specify the username for this site?" read USERNAME HOME_DIR=$USERNAME adduser $USERNAME # ------- # CentOS: # If you're using CentOS you will need to uncomment the next 3 lines! # ------- #echo "Please enter a password for the user: $USERNAME" #read -s PASS #echo $PASS | passwd --stdin $USERNAME echo "Would you like to change to web root directory (y/n)?" read CHANGEROOT if [ $CHANGEROOT == "y" ]; then echo "Enter the new web root dir (after the public_html/)" read DIR PUBLIC_HTML_DIR='/public_html/'$DIR else PUBLIC_HTML_DIR='/public_html' fi # Now we need to copy the virtual host template CONFIG=$NGINX_CONFIG/$DOMAIN.conf cp $CURRENT_DIR/nginx.vhost.conf.template $CONFIG $SED -i "s/@@HOSTNAME@@/$DOMAIN/g" $CONFIG $SED -i "s#@@PATH@@#\/home\/"$USERNAME$PUBLIC_HTML_DIR"#g" $CONFIG $SED -i "s/@@LOG_PATH@@/\/home\/$USERNAME\/_logs/g" $CONFIG $SED -i "s#@@SOCKET@@#/var/run/"$USERNAME"_fpm.sock#g" $CONFIG echo "How many FPM servers would you like by default:" read FPM_SERVERS echo "Min number of FPM servers would you like:" read MIN_SERVERS echo "Max number of FPM servers would you like:" read MAX_SERVERS # Now we need to create a new php fpm pool config FPMCONF="$PHP_INI_DIR/$DOMAIN.pool.conf" cp $CURRENT_DIR/pool.conf.template $FPMCONF $SED -i "s/@@USER@@/$USERNAME/g" $FPMCONF $SED -i "s/@@HOME_DIR@@/\/home\/$USERNAME/g" $FPMCONF $SED -i "s/@@START_SERVERS@@/$FPM_SERVERS/g" $FPMCONF $SED -i "s/@@MIN_SERVERS@@/$MIN_SERVERS/g" $FPMCONF $SED -i "s/@@MAX_SERVERS@@/$MAX_SERVERS/g" $FPMCONF MAX_CHILDS=$((MAX_SERVERS+START_SERVERS)) $SED -i "s/@@MAX_CHILDS@@/$MAX_CHILDS/g" $FPMCONF usermod -aG $USERNAME $WEB_SERVER_GROUP chmod g+rx /home/$HOME_DIR chmod 600 $CONFIG ln -s $CONFIG $NGINX_SITES_ENABLED/$DOMAIN.conf # set file perms and create required dirs! mkdir -p /home/$HOME_DIR$PUBLIC_HTML_DIR mkdir /home/$HOME_DIR/_logs mkdir /home/$HOME_DIR/_sessions chmod 750 /home/$HOME_DIR -R chmod 700 /home/$HOME_DIR/_sessions chmod 770 /home/$HOME_DIR/_logs chmod 750 /home/$HOME_DIR$PUBLIC_HTML_DIR chown $USERNAME:$USERNAME /home/$HOME_DIR/ -R $NGINX_INIT reload $PHP_FPM_INIT restart echo -e "\nSite Created for $DOMAIN with PHP support"
Download:
Please note both downloads contain the same files, so you only need to download one.