This guide is aim to hosting a secure WordPress site, powered by fast nginx web server with Web Application Firewall and brotli compression, on modern, secured PHP 7.3 (Part 1)
Nowadays, WordPress is the most widely used cms (content management system), but as the saying goes, comfortability has to be followed by security to confirm its comfort. In this article, we want to write about how to build a hosting securely WordPress site based on FreeBSD 12.0
Before continuing this article, you probably want to know what you should do after installing FreeBSD 12.0 in your system; you can refer to this article.
Some factors needed for improving the level of security on your site is Web Application Firewall (WAF) and secured the PHP environment. Brotli compression is an excellent addition as well. Brotli is allowing you to speed up the transfer of your text files by up to 20% in comparison to gzip compression. This means lower transfers and faster loading times. This howto covers installing a full web server and PHP language and free Let’s Encrypt SSL certificate, WordPress, and also some WordPress – specific security tips. Upon finishing, you should have a secure WordPress site with a green padlock visible in your browser.
This tutorial is designed for someone with basic UNIX administration knowledge and will require you to have a root account access on a FreeBSD operating system.
FreeBSD’s default shell – csh – is used here, but I rather to use bash rather than csh, so If you want to use shell bash as well, you can refer to this document.
Usually, as a Unix user, we are accustomed to using the vi editor. but i would prefer to use nano editor as my favorite editor, if you want to use the nano editor as well, again, you can refer to this document.
Why FreeBSD and will this work on Linux?
I most often am choosing FreeBSD for my web hosting needs as I treasure its minimalism and ease of use. If you prefer Linux or another UNIX-like operating system however, this howto will not reflect the differences, and you are required to customize compilation options and files’ locations to adequate to your OS and environment.
Why Nginx and not Apache HTTP Server?
I prefer Nginx to Apache HTTP Server simply because it is fast as hell (for example doesn’t search every parent directory for .htaccess file) and can be easily used as a load balancer and proxy. We will, however, be installing Apache HTTP Server for a moment, as its libraries are needed to compile Nginx with ModSecurity.
Brotli compression
When modern browsers such as Chrome or Firefox send a request to an HTTPS-enabled website, they include “Accept-Encoding” header with values “gzip, deflate, br” – meaning that they will accept an answer (web page, file) compressed with either gzip, deflate or brotli algorithm. Brotli-enabled Nginx will choose gzip over deflate and brotli over gzip. Just like Deflate and gzip, brotli is a lossless algorithm.
You can test Facebook using cURL, and you will notice different “Content-Encoding” headers showing you which compression server chose. Your Nginx will behave in the same manner.
Brotli compression does not improve security, but it does performance. Files sent to brotli-enabled browsers are smaller, thus will consume less bandwidth and will be transferred faster. If you want to know how performance comparison between brotli and gzip, you can refer to this post.
Let’s do it!
Before starting this installation process, we need a domain name for our SSL certificate, because we will run HTTPS rather than HTTP in our server. So you have to prepare your domain name before starting.
Escallate privileges
FreeBSD by default comes without sudo – you are supposed to su to another user to issue commands as him. Building software itself does not require root privileges, but editing root-owned config files, start scripts, installing packages – does, so we’ll just escalate our privileges at the start and leave it this way. How to install sudo ? you can refer to this post
So, we will start from installing PHP 7.3 in our system, this is the command
pkg install php73 php73-mysqli php73-session php73-xml php73-hash php73-ftp php73-curl php73-tokenizer php73-zlib php73-zip php73-filter php73-gd php73-openssl
Add user that will run PHP processes
Please note that the command below adds a user named “myweb”. You probably want to change it but you will have to reflect this change also in the PHP and nginx configuration files below.
pw groupadd myweb && pw useradd myweb -g myweb
Configure PHP
php.ini
You will need to copy the default php.ini file and edit it:
cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
Edit this file in text editor of your choice: nano, vi, ee or other:
nano /usr/local/etc/php.ini
For security reasons it is advised to disallow some functions which may be abused. Find the line that says:
disable_functions =
and change it to:
disable_functions=shell_exec,system,passthru,exec,curl_exec,proc_open,parse_ini_file,popen,eval,show_source,highlight_file
If your theme or any of your plugins require any of the above functions – consider replacing them with something with safer code if possible.
To hide the PHP version from HTTP requests, find the line that says
expose_php = On
and change it to:
expose_php = Off
You may want to increase the time alloted for PHP scripts to finish. If so, change (these are seconds):
max_execution_time = 30
to:
max_execution_time = 120
or more, even 300 or 600 seconds. This may be required by some time consuming scripts.
You can also increase max input time. Change:
max_input_time = 60
to:
max_input_time = 120
but carefully read the commented description in the file – it may be safer for you to leave this at the default value.
Depending on the amount of plugins and visitors, it it is recommended to increase the memory allocation of PHP processes. If you are on a rather smallish VPS – it will probably be better to leave this as it is.
Change:
memory_limit = 128M
to something higher – for example:
memory_limit = 256M
Increase max POST size (amount of data that PHP can receive from the user):
post_max_size = 8M
to:
post_max_size = 32M
or more – if needed – e.g. 64M or even 4G. Depends on your needs, really.
You probably also want to be able to send larger files to your WordPress. If so, change:
upload_max_filesize = 2M
to:
upload_max_filesize = 32M
or more.
Next, close down PHP to only be able to access /tmp (for session storing) and WordPress directories. This is a security feature that will hide the rest of your filesystem from a successful attacker. Find the following line:
;open_basedir =
and change it to:
open_basedir = /usr/local/www/wordpress:/tmp
If you are not low on RAM – and this is is advised and will boost your performance greatly – enable OPcache: find the below line. Please note the semicolon (;). This is a comment sign and has to be removed:
;opcache.enable=1
change it to:
opcache.enable=1
You probably will also like to increase other OPcache values in this section of the config file over time, but for start you can stay with the defaults.
PHP user pool
Now to the second part of PHP configuration – FPM. Pool is a process or number of PHP processes assigned to one user. For security reasons it is always preferred to create separate pools for different websites and to cut them down with PHP’s open_basedir, or chroot/chdir settings. We are creating only one site here, so we will be using standard “www” pool.
We will not be running the pool as “www” user, but rather as our new non-system user with UID above 1000.
If you have named your user other than “mysite” – it is necessary to reflect these changes here:
vi /usr/local/etc/php-fpm.d/www.conf
Nginx will be connecting to local FPM over UNIX socket so doing it over TCP is not necessary. Find the line:
listen = 127.0.0.1:9000
and change it to:
listen = /var/run/php-wordpress.sock
Next, find the below lines
user = www group = www
and change them to
user = myweb group = myweb
We also need to uncomment and change socket’s ownership variables. Change:
;listen.owner = www ;listen.group = www ;listen.mode = 0660
to:
listen.owner = myweb listen.group = myweb listen.mode = 0600
Another thing that we will want to change is the list of file extensions that will be parsed with PHP. Again – you might encounter a weird theme or plugin that would require you to add additional extensions here. Find the line:
;security.limit_extensions = .php .php3 .php4 .php5 .php7
and uncomment it by removing semicolon:
security.limit_extensions = .php .php3 .php4 .php5 .php7
And let’s enable PHP FPM and start it:
sysrc php_fpm_enable=YES php_fpm_enable: -> YES service php-fpm start Performing sanity check on php-fpm configuration: [11-Jan-2019 04:38:42] NOTICE: configuration file /usr/local/etc/php-fpm.conf test is successful Starting php_fpm.
Download required software
We will need Apache HTTP Server’s libraries and a few other utilities to compile nginx with ModSecurity. We will delete these packages at the end, as they are only needed for nginx compilation. We’ll also need minimal git to grab and update ngx_brotli, ModSecurity and it’s rules from GitHub.
pkg install git-lite libtool automake autoconf curl libnghttp2 apache24 mkdir /root/build && cd /root/build fetch https://nginx.org/download/nginx-1.17.3.tar.gz fetch https://www.modsecurity.org/tarball/2.9.3/modsecurity-2.9.3.tar.gz git clone https://github.com/SpiderLabs/owasp-modsecurity-crs git clone https://github.com/google/ngx_brotli cd ngx_brotli && git submodule update --init && cd .. tar -zxf nginx-1.17.3.tar.gz && rm -f nginx*tar.gz tar -zxvf modsecurity-2.9.3.tar.gz && rm -f modsecurity-*.tar.gz
List directory contents and make sure that all four things have been downloaded:
ls -l total 34 drwxr-xr-x 14 root wheel 26 Feb 14 10:06 ModSecurity drwxr-xr-x 8 root wheel 13 Apr 17 17:22 nginx-1.17.3 drwxr-xr-x 5 root wheel 10 Feb 14 10:06 ngx_brotli drwxr-xr-x 7 root wheel 18 Feb 14 10:06 owasp-modsecurity-crs