WordPress on LAMP with Session Encryption and Backup

Overview

Zen Astronave is a personal blog site.

In this guide, we will implement a Linux, Apache, MariaDB, and PHP (LAMP) system and install WordPress for zenastronave.com. Since we are interested in the Confidentiality, Integrity, and Availability (CIA) triad, we will also implement a firewall, Transport Layer Secuirty (TLS) certificates, and daily backups.

This example uses AlmaLinux but should also be useful for administrators of RedHat Linux and any other RedHat-style distribution including Fedora, CentOS, and Rocky Linux. Additionally, administrators can substitute MySQL for MariaDB with only minor adjustments.

Assumptions

This guide assumes you have a domain name (substitute your domain for zenastronave.com in the examples); root shell access to a public-facing AlmaLinux version >=9.0 system; and SELinux is in enforcing mode.

Environment

This section provides insight into the Zen Astronave infrastructure to demonstrate systems administration best practices. The detail here may not be applicable to your environment; however, the rest of the guide should be useful for any AlmaLinux system.

There are three systems involved in this implementation: (a) a Linux KVM hypervisor providing a bridge for guest networking and ZFS zvols for guest disks, (b) a ZFS backup system, and (c) an AlmaLinux virtual machine with Apache, MariaDB, Firewalld, and the EFF‘s Certbot (TLS certificates) to host zenastronave.com.

The zvol (disk) for the AlmaLinux virtual machine was created on the Linux KVM hypervisor system by configuring and executing OZO Znap and Zhip on the ZFS backup system. OZO Znap and Zhip remotely executed commands on the Linux KVM hypervisor to create the zvol, then created and shipped the origin snapshot and the first incremental snapshot. Every time OZO Znap and Zhip is executed on the ZFS backup system (daily), it creates and ships another incremental snapshot. So, before the virtual machine has even been created, we already have a mechanism in place to perform a daily backup of the VM’s entire system disk.

The virtual machine was defined on the Linux KVM hypervisor system using OZO Virt-Install and a kickstart. The OZO Virt-Install configuration file and kickstart files on the hypervisor (which is also backed up daily) can be used to easily recreate the virtual machine in the event it is damaged or deleted. The kickstart implements an AlmaLinux virtual machine (guest) with Firewalld enabled (permitting SSH) and SELinux in enforcing mode.

The rest of this guide details the process of configuring the AlmaLinux LAMP system and installing and configuring WordPress.

Implement the LAMP System

Install Packages

Here we use dnf to install the packages required for this guide.

dnf -y install certbot git httpd mariadb mariadb-server mod_ssl php php php-gd php-intl php-mysqlnd php-pecl-imagick php-pecl-mailparse php-pecl-zip python3-certbot-apache

Set Permissions and Ownership for the Apache Document Root

In this step, we will set ownership and permissions for the Apache document root, /var/www/html/www.zenastronave.com.

chown -R apache:apache /var/www/html/www.zenastronave.com
chmod 775 /var/www/html/www.zenastronave.com

Create Group and Apply Filesystem ACLs

In this step, we will create a group and apply filesystem ACLs that will allow the www group to modify all files in the Apache document root. Adding your own user to this group allows you to modify the files in the document root without using sudo. Substitute your username for USERNAME.

We will also ensure that the apache user and group will have read and write access to the entire directory structure.

groupadd www
usermod -a -G www USERNAME
setfacl -m d:u:apache:rwx,d:g:apache:rwx,d:g:www:rwx,g:www:rwx /var/www/html/www.zenastronave.com

Log out and back in to make this group membership change effective.

Configure SELinux

We must enable some SELinux booleans to permit Apache to load modules (like the PHP module) and network connect (like when WordPress checks for updates). For more information, please see Security Enhanced Linux Policy for the httpd processes.

setsebool -P domain_kernel_load_modules 1
setsebool -P httpd_can_network_connect 1

We also need to create an SELinux fcontext for the wordpress directory we will be creating later. Adding it now ensures it will be applied when the directory is created.

semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/*/wordpress(/.*)?"

Start and Configure MariaDB

In this step, we will enable and start the MariaDB server and set a password for the root user. Substitute your own strong password for PASSWORD. Note: You may wish to run mysql_secure_installation for a more comprehensive process.

systemctl enable --now mariadb
mysqladmin -u root password 'PASSWORD'

Implement OZO MariaDB Backup

This is important to implement even if your environment includes something like the ZFS backup process detailed in “Environment”, above. Snapshots taken while certain daemons e.g., relational databases are active may result in inconsistencies in the backup set. OZO MariaDB Backup uses MariaDB methods to dump the database contents to a file on the system disk where it can be included in a snapshot (or other backup mechanism) with greater consistency. It also means that some database backup history is available on the local system disk which supports faster recovery time in certain circumstances.

Implement OZO MariaDB Backup to perform a daily dump of all databases to the system disk so we have a backup mechanism in place even before we’ve created our WordPress database. As detailed in the OZO MariaDB Backup readme.md, set a user and strong password in /etc/ozo-mariadb-backup.conf and use that information in the MariaDB shell to create the backup user, substituting your strong password for PASSWORD.

mysql -u root -p
GRANT SELECT, RELOAD, LOCK TABLES, SHOW VIEW ON *.* TO 'mysql-backup'@'localhost' IDENTIFIED BY 'PASSWORD';
flush privileges;
quit;

Configure Apache

We will create a directory for Virtual Host configurations and create a basic site configuration that we will use to obtain TLS certificates.

echo 'IncludeOptional vhost.d/*.conf' >> /etc/httpd/conf/httpd.conf
mkdir /etc/httpd/vhost.d
vi /etc/httpd/vhost.d/www.zenastronave.com.conf

Contents of /etc/httpd/vhost.d/www.zenastronave.com.conf:

<Virtualhost *:80>
  ServerAdmin noreply@zenastronave.com
  ServerName zenastronave.com
  ServerAlias www.zenastronave.com
  DocumentRoot "/var/www/html/www.zenastronave.com"
  CustomLog "logs/access_log" combined
  ErrorLog "logs/error_log"

  <Directory "/var/www/html/www.zenastronave.com">
    Options +Indexes +FollowSymLinks -MultiViews
    AllowOverride All
    Require all granted
  </Directory>

  <IfModule dir_module>
    DirectoryIndex index.php index.html
  </IfModule>

  <Files ".ht*">
     Require all denied
  </Files>
</VirtualHost>

Create Firewall Rules

These firewall rules will allow network requests to reach Apache on port 80 (HTTP) and 443 (HTTPS).

firewall-cmd --permanent --zone=public --add-service=http --add-service=https
firewall-cmd --reload

Enable and Start Apache

We will start Apache with the current configuration so Certbot to can validate domain ownership and generate certificates. Later, we will use Apache RewriteRules to force all traffic to HTTPS.

systemctl enable --now httpd

Obtain TLS Certificates

The Certbot implementation on AlmaLinux cannot automagically configure our VirtualHost, so we will use a command that simply generates and saves the certificates. This command also includes multiple hosts in a single certificate. In a later step, we will reconfigure Apache to use the certificates and rewrite www.zenastronave.com to zenastronave.com.

certbot certonly --webroot -w /var/www/html/www.zenastronave.com -d www.zenastronave.com -d zenastronave.com

The certbot command generated a certificate and private key at the following paths:

  • /etc/letsencrypt/live/www.zenastronave.com/fullchain.pem
  • /etc/letsencrypt/live/www.zenastronave.com/privkey.pem

Automate Certificate Renewal

To make sure our certificates are renewed before they expire, we can enable the certbot-renew timer:

systemctl enable --now certbot-renew.timer

Install WordPress

Create a WordPress Database

Open the MariaDB shell and create a database + user + password for WordPress, substituting your own strong password for PASSWORD. Keep this information handy for the next steps.

mysql -u root -p
create database wordpress;
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost' IDENTIFIED BY 'PASSWORD';
flush privileges;
quit;

Obtain and Extract WordPress

Download the latest version of WordPress to the root user home directory and extract it to /var/www/html/www.zenatronave.com. Once extracted, set the permissions and ownership for Apache.

cd ~
wget https://wordpress.org/latest.zip
cd /var/www/html/www.zenastronave.com
unzip ~/latest.zip
rm ~/latest.zip
chown -R apache:apache wordpress
find wordpress -type d -exec chmod 775 {} \;
find wordpress -type f -exec chmod 664 {} \;

Create the Default .htaccess File

Create a default .htaccess file in the wordpress directory.

vi /var/www/html/www.zenastronave.com/wordpress/.htaccess

Copy-Paste this content into .htaccess:

# BEGIN WordPress

RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

# END WordPress

Change the user and group ownership of .htaccess:

chown apache:apache /var/www/html/www.zenastonave.com/wordpress/.htaccess

Verify Filesystem Permissions, Ownership, and ACLs

At this point we can use commands to verify that the SELinux fcontext is correct and the filesystem ownership, permissions, and www ACL are correct.

restorecon -R /var/www/html/www.zenastronave.com
ls -hlZ /var/www/html/www.zenastronave.com/
drwxrwxr-x+ 5 apache apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 wordpress
getfacl /var/www/html/www.zenastronave.com/wordpress
# file: wordpress
# owner: apache
# group: apache
user::rwx
group::rwx
group:www:rwx
mask::rwx
other::r-x
default:user::rwx
default:group::rwx
default:group:www:rwx
default:mask::rwx
default:other::r-x

Configure WordPress

Follow the steps in Editing wp-config.php using the database + user + password you created above.

mv /var/www/html/www.zenastronave.com/wordpress/wp-config-sample.php /var/www/html/www.zenastronave.com/wordpress/wp-config.php
vi /var/www/html/www.zenastronave.com/wordpress/wp-config.php

You will set values for DB_NAME, DB_USER, and DB_PASSWORD; and will use the WordPress Secret Key Service to set unique values for AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY, AUTH_SALT, SECURE_AUTH_SALT, LOGGED_IN_SALT, and NONCE_SALT (the output generated by the key service will completely replace the existing corresponding lines).

Reconfigure and Restart Apache

Since we have our TLS certificates, we can reconfigure Apache to rewrite all traffic to HTTPS and use the certificates to provide a secure session for our site visitors. Edit /etc/httpd/vhost.d/zenastronave.com.conf as follows (changes in bold):

<Virtualhost *:80>
  ServerAdmin noreply@zenastronave.com
  ServerName zenastronave.com
  ServerAlias www.zenastronave.com
  DocumentRoot "/var/www/html/www.zenastronave.com/wordpress"
  CustomLog "logs/access_log" combined
  ErrorLog "logs/error_log"

  # Rewrite all traffic to HTTPS
  RewriteEngine On
  RewriteCond %{HTTPS} off
  # Prevent certbot verfication requests from being rewritten to HTTPS
  RewriteCond %{REQUEST_URI} !^/.well-known($|/) [NC]
  RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301]

  <Directory "/var/www/html/www.zenastronave.com/wordpress">
    Options +Indexes +FollowSymLinks -MultiViews
    AllowOverride All
    Require all granted
  </Directory>

  <IfModule dir_module>
    DirectoryIndex index.php index.html
  </IfModule>

  <Files ".ht*">
     Require all denied
  </Files>
</VirtualHost>

<Virtualhost *:443>
  ServerAdmin noreply@zenastronave.com
  ServerName zenastronave.com
  ServerAlias www.zenastronave.com
  DocumentRoot "/var/www/html/www.zenastronave.com/wordpress"
  CustomLog logs/ssl_request_log "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
  ErrorLog "logs/ssl_error_log"

  RewriteEngine On
  RewriteCond %{HTTP_HOST} ^www\.zenastronave\.com [NC]
  RewriteRule ^(.*)$ http://zenastronave.com/$1 [L,R=301]

  SSLEngine on
  SSLHonorCipherOrder on
  SSLCipherSuite PROFILE=SYSTEM
  SSLProxyCipherSuite PROFILE=SYSTEM
  SSLCertificateFile /etc/letsencrypt/live/www.zenastronave.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/www.zenastronave.com/privkey.pem

  <Directory "/var/www/html/www.zenastronave.com/wordpress">
    Options -Indexes +FollowSymLinks -MultiViews
    AllowOverride All
    Require all granted
  </Directory>

  <IfModule dir_module>
    DirectoryIndex index.php index.html
  </IfModule>

  <Files ".ht*">
     Require all denied
  </Files>

  <FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
  </FilesMatch>

  <Directory "/var/www/cgi-bin">
    SSLOptions +StdEnvVars
  </Directory>

</VirtualHost>

Restart Apache

Restarting Apache will make the configuration changes effective.

systemctl restart httpd

Initialize WordPress

Finally, we visit https://zenastronave.com and provide an application login username and password to the WordPress setup wizard. It is now possible to install plugins, themes, and create pages and posts. Recommended plugins:

  • Code Syntax Block
  • Embed Plus YouTube
  • Envira Gallery
  • Media Sync by Erol Živina
  • W3 Total Cache
  • WordPress Importer
  • WP Mail SMTP
  • WPForms Lite

Conclusion

Using a firewall, TLS certificates, and leveraging SELinux in enforcing mode provides strong confidentiality. Routine backups of the database to the system disk and a routine backup of the system disk to a geographically distinct backup system provides high data integrity. Running AlmaLinux in a Linux KVM hypervisor with a public IP address provides high availability.

Hopefully, some or all of this guide has been helpful for you. If you do not have the opportunity to leverage Linux KVM + ZFS for provisioning guest disks with backup, you can use OZO Rdiff-Backup to back up your AlmaLinux system from any internet-connected Linux system with SSH.

Happy WordPress-ing!