How To Secure Multiple WordPress Sites On Same Server (LEMP Stack)

Hi :slight_smile:

There is a known issue with installing multiple sites on the same server. i.e. if somebody gains access to one site, then he/she can affect other sites too.

How To Reproduce The Security Issue?

  • Install two WordPress sites on the same server with LEMP or LAMP stack.

  • Login to any site (let site1), install any file manager plugin, go to preferences and change public root path to “/var/www

  • Now you can edit files of all sites hosted on that server.

How To Solve It?

WordPress is a php based CMS, so when we run it using the master PHP-FPM process, it has all access rights which php has. It can access all other scripts/files which are using the same php process.

So here we are going to add different users for each website and create separate php-fpm pools for each website.

This will run a separate PHP-FPM process for each user/site and thus isolate them.

Prerequisites
Here I am assuming that:-

  1. You had already installed two or more sites on LEMP stack. (let site1 and site2).
  2. PHP-FPM version is 7.4

Step 1

  • Create a group site1 and add user site1 to it.
sudo groupadd site1 
sudo useradd -g site1 site1 
  • Create a new pool configuration for user site1
nano /etc/php/7.4/fpm/pool.d/site1.conf
  • Paste configs in file. Press ctrl + o to save and ctrl + x to exit.
[site1]
user = site1
group = site1
listen = /run/php/php7.4-fpm-site1.sock
listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /
  • Disable php-opcache
nano /etc/php/7.4/fpm/conf.d/10-opcache.ini

add at the end of file--->  opcache.enable=0
  • Open server block of site1 (already configured server block of WordPress 1).
nano /etc/nginx/sites-available/site1
  • Edit this line “fastcgi_pass unix:/run/php/php7.4-fpm.sock;” as:-
fastcgi_pass unix:/run/php/php7.4-fpm-site1.sock;
  • Alter permissions of folder where site1 (WordPress1) is installed:-
sudo usermod -a -G site1 www-data
sudo chown -Rf site1:site1 /var/www/site1/
sudo chmod -R 750 /var/www/site1/
sudo find /var/www/site1/ -type f -exec chmod 644 {} \;
  • Restart PHP-FPM and Nginx
service nginx restart
systemctl restart php7.4-fpm.service

Step 2
Repeat same procedure for site2 , site3 … and all,

Step3

Now when you open file manager from site 1 (wordpress1), you should see something like this:-

Clearly, site1 can’t edit site2 and site3. Similarly site 2 can’t edit site1. But both site1 and site2 can edit there own files.

FAQs

I am getting 502 error. Error log says unix:/run/php/php7.4-fpm-site4.sock failed (2: No such file or directory) while connecting to upstream

  • service php7.4-fpm restart

My new site redirecting to first one.

  • nginx -t && service nginx restart

Source:- https://www.digitalocean.com/community/tutorials/how-to-host-multiple-websites-securely-with-nginx-and-php-fpm-on-ubuntu-14-04

Hope you find this tutorial helpful.

Thanks and Regards
Rishi

6 Likes

Adding Multiple WordPress

  1. Follow above tip to create a new PHP Pool Configuration
nano /etc/php/7.4/fpm/pool.d/blog.example.com.conf
  1. Create a new public directory for site 2 (let’s say it is blog.example.com)
cd /var/www/ && mkdir blog.example.com && cd blog.example.com && mkdir public
  1. Download WordPress at /var/www/blog.example.com/public
cd /var/www/blog.example.com/public && wget https://wordpress.org/latest.tar.gz && tar -xvzf latest.tar.gz && mv -v wordpress/* /var/www/blog.example.com/public && rm -rf index.nginx-debian.html latest.tar.gz wordpress
  1. Setup User, Group and Permission
sudo groupadd blog.example.com 
sudo useradd -g blog.example.com blog.example.com
sudo usermod -a -G blog.example.com www-data
sudo chown -Rf blog.example.com:blog.example.com /var/www/blog.example.com/
sudo chmod -R 750 /var/www/blog.example.com/
sudo find /var/www/blog.example.com/ -type f -exec chmod 644 {} \;
  1. Create a unique server block
# Set unique Cache path and Zone name
fastcgi_cache_path /var/www/blog.example.com/cache levels=1:2 keys_zone=blog_example_org:100m inactive=6h;

# do not repeat these
#fastcgi_cache_key "$scheme$request_method$host$request_uri";
#fastcgi_cache_use_stale error timeout invalid_header http_500;
#fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

# HTTP to HTTPS
server {
    server_name .blog.example.com;
    server_tokens off;
    listen         80;
    return 301 https://$host$request_uri;
}
server {
server_name .blog.example.com;
listen 443;
server_tokens off;
index index.php index.html index.htm;

client_max_body_size 0;

## Assuming you have wildcard SSL for example.com
## or get one https://help.gulshankumar.net/t/how-to-install-lets-encrypt-ssl-on-ubuntu-18-04-lamp-lemp/3122
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

# define public path
root /var/www/blog.example.com/public;

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;


set $skip_cache 0;
    if ($request_method = POST) {set $skip_cache 1;}
    if ($request_uri ~* "/wp-admin/|/xmlrpc.php|/wp-.*.php|index.php|sitemap(_index)?.xml") {set $skip_cache 1;}
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {set $skip_cache 1;}

location / {
        try_files $uri $uri/ /index.php?$args;
}

# Pass all .php files onto a php-fpm or php-cgi server
location ~* \.php$ {
        try_files                       $uri =404;
        include                         /etc/nginx/fastcgi_params;
        fastcgi_param                   SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass                    unix:/run/php/php7.4-fpm-blog.example.com.sock;   # Use unique Socket
        fastcgi_index                   index.php;
                fastcgi_cache_bypass $skip_cache;
                fastcgi_no_cache $skip_cache;
                fastcgi_cache blog_example_org;
                fastcgi_cache_valid 200 301 24h;
                add_header X-Cache $upstream_cache_status;

}

location = /favicon.ico {
log_not_found off;
access_log off;
}

location = /robots.txt {
log_not_found off;
access_log off;
allow all;
}

location ~* .(css|gif|ico|jpeg|jpg|js|png)$ {
expires 1y;
log_not_found off;
}

# Enable Gzip compression.
gzip on;

# Disable Gzip on IE6.
gzip_disable "msie6";

# Allow proxies to cache both compressed and regular version of file.
# Avoids clients that don't support Gzip outputting gibberish.
gzip_vary on;

# Compress data, even when the client connects through a proxy.
gzip_proxied any;

# The level of compression to apply to files. A higher compression level increases
# CPU usage. Level 5 is a happy medium resulting in roughly 75% compression.
gzip_comp_level 5;

# Compress the following MIME types.
gzip_types
 application/atom+xml
 application/javascript
 application/json
 application/ld+json
 application/manifest+json
 application/rss+xml
 application/vnd.geo+json
 application/vnd.ms-fontobject
 application/x-font-ttf
 application/x-web-app-manifest+json
 application/xhtml+xml
 application/xml
 font/opentype
 image/bmp
 image/svg+xml
 image/x-icon
 text/cache-manifest
 text/css
 text/plain
 text/vcard
 text/vnd.rim.location.xloc
 text/vtt
 text/x-component
 text/x-cross-domain-policy;
}
  1. Symlink
ln -s /etc/nginx/sites-available/blog.example.com /etc/nginx/sites-enabled/
  1. Configure NGINX Cache Directory for NGINX Helper Plugin
define('RT_WP_NGINX_HELPER_CACHE_PATH', '/var/www/blog.example.com/cache');
  1. Create a new Database
sudo mysql -u root -p
create database blog_example_com;
grant all on blog_example_com.* to blog_example_com@localhost identified by 'TYPE PASSWORD';
flush privileges;
exit;
  1. Point DNS & visit /wp-admin/ for installation

  2. For security reasons, you can move wp-config.php one level up.

cd /var/www/blog.example.com/public
mv wp-config.php /var/www/blog.example.com/
1 Like