How to Isolate Apache Virtual Hosts Using PHP-FPM and mpm-itk

It is common to run multiple virtual hosts on a single Apache instance. However, if one site is compromised (e.g., through a WordPress plugin vulnerability), the attacker could potentially access the files of every other site because they all run under the same apache user.

This guide explains how to isolate each virtual host under its own system account. If one host is breached, the attacker remains trapped within that specific user’s environment.

Note: These instructions are written for AlmaLinux using Apache and PHP-FPM, executed with root privileges.

 

1. Create a Dedicated Account for Each Virtual Host

First, create a dedicated group and a system user for the specific virtual host.

Create the group:

groupadd www-vhost1

Create the system user belonging to the group: (No password, no home directory, and no shell access)

useradd -M -g www-vhost1 -s /sbin/nologin www-vhost1

2. Configure PHP-FPM

To isolate PHP processes, you must create a unique PHP-FPM pool for each virtual host.

  1) Copy the default pool configuration:

cp /etc/php-fpm.d/www.conf /etc/php-fpm.d/www-vhost1.conf

  2) Prepare session and cache directories:

mkdir -p /var/lib/php/{session-vhost1,wsdlcache-vhost1,opcache-vhost1}
chown -R www-vhost1:www-vhost1 /var/lib/php/*-vhost1
chmod -R 700 /var/lib/php/*-vhost1

  3) Edit /etc/php-fpm.d/www-vhost1.conf: Update the following parameters to ensure the process runs as the new user:

/etc/php-fpm.d/www-vhost1.conf
#Change the pool name at the top
[www-vhost1]

# Set the user/group under which the process will run
user = www-vhost1
group = www-vhost1

# Set the socket with matching socket file user and permissions
listen = /run/php-fpm/www-vhost1.sock
listen.owner = www-vhost1
listen.group = www-vhost1
listen.mode = 0660

# Make sure the following is commented out because otherwise listen.owner and listen.group will be ignored
;listen.acl_users
;listen.acl_groups

# Set custom paths at the end of the file
php_value[session.save_path]    = /var/lib/php/session-vhost1
php_value[soap.wsdl_cache_dir]  = /var/lib/php/wsdlcache-vhost1
php_value[opcache.file_cache]  = /var/lib/php/opcache-vhost1

  4) Restart PHP-FPM

systemctl restart php-fpm
3. Install and Configure mpm-itk

The mpm-itk module allows Apache to run each virtual host as a specific system user.

  1) Download and Extract: Download the latest source from the mpm-itk project page. For example http://mpm-itk.sesse.net/mpm-itk-2.4.7-04.tar.gz

Replace ‘version’ below with the actual version number.

wget http://mpm-itk.sesse.net/mpm-itk-version.tar.gz
tar -xvzf mpm-itk-version.tar.gz
cd mpm-itk-version

  2) Build and Install:

./configure
make
make install

This should create /usr/lib64/httpd/modules/mpm_itk.so

If dependencies are missing, you may need to install httpd-devel and libcap-devel.

dnf install httpd-devel
dnf install redhat-rpm-config

  3) Enable the Module: Create /etc/httpd/conf.modules.d/00-mpm-itk.conf and add:

LoadModule mpm_itk_module modules/mod_mpm_itk.so

Ensure that mpm_prefork_module is enabled and other MPMs (like event or worker) are commented out in your Apache config.

After restarting Apache you can confirm that the itk module is loaded:

httpd -M
4. Virtual Host Configuration

Now, apply the user isolation and PHP-FPM socket to your Virtual Host configuration file.

  1) Update the Virtual Host file:

<VirtualHost *:80>
    ServerName www.vhost1.com

    <IfModule mpm_itk_module>
        AssignUserId www-vhost1 www-vhost1
    </IfModule>

    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php-fpm/www-vhost1.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

  2) Secure the webroot: Ensure only the designated user can access the files:

chown -R www-vhost1:www-vhost1 /var/www/vhost1_public_html
find /var/www/vhost1_public_html -type d -exec chmod 700 {} \;
find /var/www/vhost1_public_html -type f -exec chmod 600 {} \;

  3) Restart Apache:

systemctl restart httpd

Leave a Reply

Your email address will not be published. Required fields are marked *