A reasonably secure server for WordPress

This post was created to assist some colleagues in Afghanistan, who are creating a server for running Laravel. My experience is with WordPress, but the principles should be the same.

These instructions apply to Ubuntu server. The version is Ubuntu 19.04, there may be slight variations between Ubuntu versions, but the principles apply. Note that if you are doing this for practice purposes, it may be a good idea to create a practice subdomain in your DNS manager, for example, practice.example.com, and then every time you delete your practice server and create a new one, you can just change the IP address in your DNS manager (e.g. Namecheap, GoDaddy, DogitalOcean, etc).

STEP 1: Make sure everything is up to date.

sudo apt update
sudo apt upgrade -y

STEP 2: Install Apache web server and configure service

sudo apt install -y apache2 apache2-utils
sudo chown www-data:www-data /var/www/html/ -R
sudo systemctl stop apache2.service
sudo systemctl start apache2.service
sudo systemctl enable apache2.service

STEP 3: Create a temporary index until done with WordPress install

sudo cd /var/www/html
sudo rm index.html
sudo echo "<h1>Working here. Nothing here yet.</h1>" > index.html

STEP 4: Install a digital certificate software and generate a certificate for enabling HTTPS

The technology we will use is from the Electronic Frontier Foundation, and it is called “Let’s Encrypt”. The software that is used to install and manage certificates is called “certbot”. I recommend that you visit the Let’s Encrypt website and learn about certificates, the certbot technology, and how to manage HTTPS on your website.

sudo apt install software-properties-common -y
sudo add-apt-repository universe -y
sudo add-apt-repository ppa:certbot/certbot -y
sudo apt install certbot python-certbot-apache -y
sudo certbot --apache -d example.com -d www.example.com
sudo certbot renew --dry-run

STEP 5: More secure with a UFW firewall

sudo apt install -y ufw
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
sudo ufw status verbose

STEP 6: Install PHP

sudo apt install -y php libapache2-mod-php php-common php-mysql php-gmp php-ldap php-curl php-intl php-mbstring php-xmlrpc php-gd php-bcmath php-xml php-cli php-zip php-soap
sudo nano /etc/php/7.2/apache2/php.ini

Change
   post_max_size = 100M
   upload_max_filesize = 100M
   memory_limit=256M

STEP 7: Install MariaDb

Note that I am using MaridDb, the same commands work for MySQL.

sudo apt install -y mariadb-server mariadb-client
sudo systemctl stop mariadb.service
sudo systemctl start mariadb.service
sudo systemctl enable mariadb.service
sudo mysql_secure_installation

Make sure you choose a good password for the root database user when prompted.

STEP 8: Set time and timezone

The time and timezone settings should always be set correctly, and an NNTP daemon should be running. On Ubuntu, this is usually the case after an install, but it is still worth checking. Incorrect time settings can provide an attack surface for attackers. Set the timezone that corresponds to either your server location, or the main time zone where it will be used, depending on your needs.

sudo timedatectl set-timezone Africa/Johannesburg

You should check the date as well just to be sure.

date

You should get something like
   Sat Feb 23 17:59:06 SAST 2019

Make sure that timesyncd is running so that the server time is always updated from an Internet time server.

timedatectl

You should see something like

               Local time: Sat 2019-02-23 17:59:45 SA
           Universal time: Sat 2019-02-23 15:59:45 UTC
                 RTC time: Sat 2019-02-23 15:59:46
                Time zone: Africa/Johannesburg (SAST, +0200)
System clock synchronized: yes
systemd-timesyncd.service
                   active: yes

If timesyncd isn’t active, turn it on with timedatectl (in the example, it is already running).

sudo timedatectl set-ntp on

STEP 9: Install WordPress

You are probably not going to use WordPress, but this would probably be the point at which you install Laravel. Since I prefer WordPress for content sites, and Laravel for doing development of totally new stuff, I will stick with WordPress here. Also, I do not know Laravel, I leave that to colleagues for now. Just consider this the point at which you install whatever PHP application you are using.

sudo cd /var/www
sudo mkdir downloads
cd downloads
sudo wget https://wordpress.org/latest.tar.gz
sudo tar -xzvf latest.tar.gz -C /var/www/html/
cd ../html/wordpress 
sudo mv * ..
cd ..
sudo rm wordpress -R

Now you need to create the database for WordPress. Note that you can use ‘mysql -u root’ or ‘mysql -u root -p’ depending on your password settings and whether you are logged in as root user.

mysql -u root -p

Once the database is running, you can enter the commands below. Change users and passwords according to what you require.

CREATE DATABASE mywpdatabasename;
GRANT ALL PRIVILEGES ON mywpdatabasename.* 
   TO "whateverusernameyouwant"@"localhost"
   IDENTIFIED BY "top-secret-password";
FLUSH PRIVILEGES;
EXIT;

Next you need to set up your WordPress config file and edit some setting in it. Also, you can remove the index.html file that you created earlier so the WordPress PHP index is then the default index file for the server.

sudo mv wp-config-sample.php wp-config.php
sudo nano wp-config.php
sudo mv index.html index.tmp

Next you need to configure WordPress database settings. To do this you need to edit wp-config.php and make some changes therein.

sudo nano wp-config.php

Insert the database settings that you set for your MySQL settings earlier. Note that you can also define your preferred character set here.

/** The name of the database for WordPress */
define( 'DB_NAME', 'mywpdatabasename' );

/** MySQL database username */
define( 'DB_USER', 'whateverusernameyouwant' );

/** MySQL database password */
define( 'DB_PASSWORD', 'top-secret-password' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', 'utf8mb4_unicode_ci' );

You also want to set up WordPress so that it can be updated and plugins installed without having to go through FTP or manual uploads. To do this you need to edit wp-config.php again.

sudo nano wp-config.php

Add this to the end of the file:

/** Set up direct method for updating plugins */
define('FS_METHOD','direct');

Once you have done the above steps, you can open WordPress in your browser, for example https://practice.example.com.

STEP 10: A little more secure hardening

We can hide the Apache Version and Operating System

sudo cp /etc/apache2/apache2.conf /etc/apache2/apache2.conf.bak
sudo cp /etc/apache2/conf-enabled/security.conf /etc/apache2/conf-enabled/security.conf.bak
sudo nano /etc/apache2/conf-enabled/security.conf

Set the following:

   ServerSignature Off
   ServerTokens Prod

Then reload Apache to pick up the settings.

systemctl reload apache2

Another useful thing to do is install mod_evasive for denial of service (DOS) attack protection.

apt install libapache2-mod-evasive -y
nano /etc/apache2/mods-enabled/evasive.conf
mkdir /var/log/mod_evasive 
chown -R www-data:www-data /var/log/mod_evasive
systemctl restart apache2

Something else that is a good idea to install is intrusion prevention, such as Fail2Ban. Fail2Ban is an intrusion prevention software framework that protects computer servers from brute-force attacks. Fail2Ban is a Python application that operates by monitoring log files (e.g. /var/log/auth.log, /var/log/apache/access.log, etc.) for selected entries and running scripts based on them. Most commonly this is used to block selected IP addresses that may belong to hosts that are trying to breach the system’s security. Install it as follows:

sudo apt install -y fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

In that file, enter the following if it is not already there.

Insert
   [sshd]
   enabled = true
   port = 22
   filter = sshd
   logpath = /var/log/auth.log
   maxretry = 3

Then restart fail2ban service.

sudo systemctl restart fail2ban

If you ever need to unban an IP address, you can use:

sudo fail2ban-client set sshd unbanip IP_ADDRESS

There are other things that can be done to harden and secure your server further, but this will probably be enough for now. I will try to keep this document updated. Please ask on the Slack channel if you have any questions.

If you want to read more on server hardening, Geekflare has a good article at: https://geekflare.com/apache-web-server-hardening-security/

STEP 11: Making sure file permissions are correct

Run these commands inside the folder of the website
/var/www/html/ or /var/www/example.com/

cd /var/www/html
find . -type f -exec chmod 664 {} +
find . -type d -exec chmod 775 {} +
chmod 660 wp-config.php

What this does is find all files and change them to 664 permissions (), and find all folders (directories) and change them to 775 permissions (). Then the config file, wp-config.php, is given a 660 permission. More on Linux file and directory permissions at https://www.linux.com/learn/getting-know-linux-file-permissions