AppArmor is the default Mandatory Access Control module on Ubuntu, SuSE and other Linux distributions. Thanks to it, you can limit the filesystem access of a process. There are already some good howtos for getting a basic setup of Apparmor for Php-fpm. Though, in cases such as that of running multiple sites securely with Php-fpm, you will need more than a simple configuration.

When you configure each of your sites to run in a different Php-fpm pool (as described in the link above) with its corresponding system user, naturally you will wish for each site to have a separate Apparmor profile also. This will allow you to accomplish much more than the simple user permissions in Linux give and also provide an additional layer of security.

The challenge to accomplish the latter comes from the fact that Apparmor cannot differentiate the different Php-fpm pools because they all appear to be run by the binary with system path /usr/sbin/php-fpm7.0. If you didn't know, that's how Apparmor works - you define a profile based on the full system path of a process such as /usr/sbin/php-fpm7.0 in the case of Php-fpm.

Luckily, there is an advanced Apparor functionality called ChangeHat. It allows you to further fine tune a process based on a so called hat. Not surprisingly, the sophisticated software Php-fpm is, it also supports ChangeHat for Apparmor. Thus, you can define a separate subprofile or ChangeHat for each Php-fpm pool
with its own specific Apparmor settings.

Step one: Specify the Apparmor hat for the Php-fpm pool. Open the pool config file (e.g. /etc/php/7.0/fpm/pool.d/sitea.conf) and add an unique apparmor_hat value like this:

; pool name
apparmor_hat = sitea

In the above example, we are setting the Apparmor hat to 'sitea'. Restart Php-fpm for this change to take effect.

Step two: Create the Apparmor profile with the ChangeHat settings.

You can begin with a simple, rather generic AppArmor profile. You should set the new profile to complain only mode, i.e. only logging without blocking actions so that you can better understand what your Php scripts are trying to access throughout your filesystem. Unless your site is already compromised, this could be used as the basis of the profile.

Start by creating a file called /etc/apparmor.d/usr.sbin.php-fpm7.0 which contains:

#include <tunables/global>

/usr/sbin/php-fpm7.0 flags=(complain) {
#include <abstractions/base>
#include <abstractions/php5>

capability setgid,

/etc/php/7.0/** r,
/run/php/** rw,
/tmp/** rw,
/usr/sbin/php-fpm7.0 mr,
/var/lib/php/sessionss/** rw,
/var/www/html/** rw,

^sitea flags=(complain) {
#include <abstractions/base>
capability setgid,

deny /bin/dash r, #deny access to dash
deny /var/www/html/sensitivesite/* rw, #deny access to a sensitive site

/dev/null a,
/etc/ r,
/lib/x86_64-linux-gnu/ld-*.so r,
/lib/x86_64-linux-gnu/libc-*.so r,
/run/mysqld/mysqld.sock rw,
/run/php/php7.0-fpm-sitea.sock rw,
/tmp/** rwk,
/usr/share/zoneinfo/** r,
/var/www/html/sitea/** rw, #allow access to the sitea's own directory


Here are a few important points to note in the above configuration:

  1. The above configuration starts with a generic Php-fpm profile for all Php-fpm pools and contains one additional ChangeHat profile for sitea.
  2. Both the generic profile for Php-fpm and that for sitea are set to complain mode (flags=(complain)).
  3. The ChangeHat configuration for sitea starts with ^sitea.
  4. We are denying access to /var/www/html/sensitivesite/ (presumably the web root of a site with sensitive information) to sitea.

For the above setting to take effect you will have to reload Apparmor (systemctl reload apparmor.service).

After reloading Apparmor and ensuring that you have changed the above settings to your own site's paths and names, you can check that the respective profiles and ChangeHats have been loaded by using the command aa_status. You should see a similar output:

apparmor module is loaded.
8 processes are in complain mode.
/usr/sbin/php-fpm7.0 (129448) 
/usr/sbin/php-fpm7.0//sitea (129455) 

Wait for some time for the new Apparmor to gather enough information about the normal behaviour of your site. After that use the command aa-logprof to see what changes are suggested based on the gathered logs in /var/log/syslog and approve the ones you think are safe. Example:

Reading log entries from /var/log/syslog.
Updating AppArmor profiles in /etc/apparmor.d.
Complain-mode changes:

Profile:  /usr/sbin/php-fpm7.0^sitea
Path:     /usr/share/zoneinfo/Zulu
Mode:     r
Severity: 2

  1 - #include <abstractions/base> 
  2 - #include <abstractions/gnome> 
  3 - #include <abstractions/kde> 
  4 - #include <abstractions/ubuntu-browsers.d/chromium-browser> 
  5 - #include <abstractions/ubuntu-browsers.d/kde> 
  6 - #include <abstractions/ubuntu-browsers.d/mailto> 
  7 - #include <abstractions/ubuntu-gnome-terminal> 
  8 - #include <abstractions/ubuntu-konsole> 
  9 - #include <abstractions/ubuntu-unity7-base> 
  10 - /usr/share/zoneinfo/Zulu 
 [11 - /usr/share/zoneinfo/**]
[(A)llow] / (D)eny / (I)gnore / (G)lob / Glob with (E)xtension / (N)ew / Abo(r)t / (F)inish / (M)ore

As you can see, the above changes are for the Php-fpm profile with ChageHat /usr/sbin/php-fpm7.0^sitea. Similarly you can fine tune the Apparmor settings for each of your sites creating a new, unique ChangeHat.