How to isolate multiple websites on a the same LEMP stack

Viewed 117

I have NGINX, MariaDB and PHP installed on a dedicated root server running debian and I would like to host multiple websites on it.

What is the best way to make sure that the sites are isolated from each other so that if a website is compromised, the other website are not affected and their data (config-files etc.) cannot be read by the linux user/group running PHP on the compromised website?

Each website has it's own directory:

/var/www/site1.example.com
/var/www/site2.example.com
/var/www/site3.example.com
(...other websites)

Inside of each directory there is a public, cache, backup and log folder - for example:

/public
    config.php **containing database credentials**
    index.php
    (...other public facing files)
/cache 
    (...NGINX fastcgi cache)
/backup
    backup-full_01_01_2000.tar.gz **containing database credentials**
    (...more backups)
/log
    access.log
    error.log

I have already set up individual PHP-FPM pools for each website, which are running on their own users - for example:

/var/run/php-site1.sock site1:site1
/var/run/php-site2.sock site2:site2
/var/run/php-site3.sock site3:site3

The directories like /var/www/site1.example.com are owned by the same user PHP is running as - for example:

/var/www/site1.example.com site1:site1
/var/www/site2.example.com site2:site2
/var/www/site3.example.com site3:site3

NGINX is running on the default nginx:nginx user.

And now comes the part where I can't wrap my head around. What values do I set the permissions to? I assume NGINX needs to be able to read across all websites, and be able to at least write to the /cache and /log folders of each website, but PHP should only be allowed to read and write inside of its own /public folder. Also, there needs to be another user with similar permissions to NGINX which can read across all websites but can only write to the /backup folders so it can create daily backups of the files and databases through a cronjob or something.

If I set the file permissions to 644 for example, then both site1 and nginx can do what they need to do, but site1 is able to read the contents of site2.example.com/public/config.php, which would allow site1 to read site2 database credentials. But if I set it any lower the nginx user can't access it too.

1 Answers

An interesting question! Well, it may sound strange to you, but actually nginx does not need permissions to read the PHP files, as well as any other files that don't needed to be accessed remotely as a static content. The only permission it may actually need is an executable bit being set up on the directory containing those files to perform the stat system call on them, which may be required if you'd use the try_files directive in your PHP handler location (which isn't obligatory too). The only nginx job in this case is to pass the FastCGI request to the PHP-FPM daemon, properly setting FastCGI variable SCRIPT_FILENAME (and other required FastCGI variables, while the SCRIPT_FILENAME is the essential one).

The whole PHP interpretation process is performed by the PHP-FPM, which is running using credentials specified in the PHP-FPM pool configuration file. This is also mean all the other files, read or included by the PHP script, are enough to have 600 permissions only.

The PHP handler nginx location, usually something like

location ~ \.php$ {
    ...
    fastcgi_pass /path/to/php-fpm.socket; # or hostname:port
}

is not obligatory to have any defined root at all, as well as the current $uri pointing to an existed PHP file. While that $document_root, $uri, $request_uri and some other nginx internal variables are used inside the default fastcgi_params file to assign values to all the needed FastCGI variables, excluding only the SCRIPT_FILENAME one, you are free to do all this job yourself.

To understand this better, imagine the situation when some kind of API is being served by the PHP application, which index.php controller located in some directory other than the main website one. Instead of commonly used configuration like

location /api/ {
    root /custom/path/to/the/api/app;
    try_files $uri $uri/ /api/index.php$is_args$args;
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_filename;
        fastcgi_pass /path/to/php-fpm.socket;
    }
}

you can use much more simple

location /api/ {
    include fastcgi_params;
    # redefine FastCGI variables
    fastcgi_param SCRIPT_NAME /index.php;
    fastcgi_param SCRIPT_FILENAME /custom/path/to/the/api/app/index.php;
    fastcgi_pass /path/to/php-fpm.socket;
}

Note that we don't use root, try_files, rewrite or any other similar directives here - the only nginx job is to pass proper FastCGI request to the PHP-FPM daemon. None of file reading operations will be performed by nginx at all.


Conclusion. You can set 600 permissions on any sensible PHP file (or any other file used exclusively inside the PHP code), it won't break your site functioning in any way. Files that should be accessible remotely and served by nginx as the static content, should have at least 644 permissions.

Related