This article is outdated and may not work correctly for current operating systems or software.
Given OpenBSD's penchant for security, it only makes sense to power your WordPress website with it, especially because WordPress and PHP tend to be moving targets for script kiddies. Since OpenBSD's httpd is designed principally to serve up static pages, POST operations are reserved for the fastcgi and slowcgi processes. This makes it harder for a rogue actor to potentially break the webserver's process and gain access to your server. POST operations are piped to the fastcgi process and use an external interpreter. This article will discuss not only setting up your WordPress site but some basic maintenance techniques and how to backup and restore your site and its database. Wherever you see example.com
as the domain, please replace it with your domain.
If you have not already done so, you will need to create a /etc/doas.conf
file. The doas command is OpenBSD's easy replacement for sudo. For convenience, I have added the nopass option so that you won't have to re-type your password when using doas. If you prefer not to have this, simply omit nopass.
su -
echo "permit nopass keepenv :wheel" > /etc/doas.conf
Depending on how OpenBSD was packaged for deployment, sometimes the package manager might not have a repository configured. To configure the OpenBSD official repository, we have to create the /etc/installurl
file.
doas su
echo "https://cdn.openbsd.org/pub/OpenBSD" > /etc/installurl
exit
Now we have to add PHP and some extra modules that WordPress will need in order to handle things like images and encryption. When prompted, choose to install the newest package of PHP. One thing you have to do is to copy the module ini files from the sample directory to the main one. This has to be done in order to enable the additional PHP modules.
doas pkg_add -r mariadb-client mariadb-server php php-curl php-mysqli php-zip pecl73-mcrypt pecl73-imagick wget
doas cp /etc/php-7.3.sample/* /etc/php-7.3/.
In today's world, websites must be served up via SSL or face being downranked by search engines. Fortunately, OpenBSD has a great application called acme-client. The acme-client will automatically generate a new private key and request a new fully valid certificate. The acme-client depends upon having a web server in place so we will need to create a quick default server definition.
With your favorite editor, create /etc/httpd.conf
. We will add the other server definitions to the file later. For now this will be enough so that acme-client functions properly.
prefork 5
types { include "/usr/share/misc/mime.types" }
server "default" {
listen on egress port 80
root "/htdocs"
directory index "index.html"
location "/.well-known/acme-challenge/*" {
request strip 2
root "/acme"
}
}
Also using your favorite editor, create /etc/acme-client.conf
.
authority letsencrypt {
api url "https://acme-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-privkey.pem"
}
authority letsencrypt-staging {
api url "https://acme-staging-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-staging-privkey.pem"
}
domain example.com {
alternative names { www.example.com }
domain key "/etc/ssl/private/example.com.key"
domain full chain certificate "/etc/ssl/example.com.crt"
sign with letsencrypt
}
Now we have to enable and start httpd. Once we do this, then we can run acme-client and wait for it to obtain our fresh, new certificate. After we do this, we'll add a cron job to automatically request a new one every 7 days so we do not have to worry about expiration.
doas rcctl enable httpd
doas rcctl start httpd
doas acme-client -v example.com
Now we create the cron job. Add this line below the very last entry. In this case, we are telling acme-client to request the new certificate at 1:00AM every Saturday.
doas crontab -e
0 1 * * 6 acme-client -F example.com && rcctl reload httpd
Now it is time to setup httpd for WordPress. Rather than place our website's definition directly in /etc/httpd.conf
, we are going to place it in a separate file called /etc/httpd.conf.example.com
and include it in the main configuration file. It is generally a good practice to separate the two, keeping site-wide definitions in your main configuration file and domain-specific settings in a different one.
Add the following line to the bottom of your /etc/httpd.conf
file:
include "/etc/httpd.conf.example.com"
Now using your favorite editor, create your /etc/httpd.conf.example.com
. For convenience, we are going to create separate log files for your domain. This makes it easier when trying to chase down potential problems with your site.
server "example.com" {
listen on egress port 80
alias "www.example.com"
# Automatically redirect to SSL
block return 302 "https://$SERVER_NAME$REQUEST_URI"
log {
access "access-example.com"
error "error-example.com"
}
}
server "example.com" {
listen on egress tls port 443
alias "www.example.com"
root "/htdocs/wordpress"
directory index "index.php"
log {
access "access-example.com"
error "error-example.com"
}
tcp { nodelay, backlog 10 }
tls {
certificate "/etc/ssl/example.com.crt"
key "/etc/ssl/private/example.com.key"
}
hsts {
# max-age value is the number of seconds in 1 year
max-age 31556952
preload
subdomains
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
location "/posts/*" {
fastcgi {
param SCRIPT_FILENAME \
"/htdocs/wordpress/index.php"
socket "/run/php-fpm.sock"
}
}
location "/page/*" {
fastcgi {
param SCRIPT_FILENAME \
"/htdocs/wordpress/index.php"
socket "/run/php-fpm.sock"
}
}
location "/feed/*" {
fastcgi {
param SCRIPT_FILENAME \
"/htdocs/wordpress/index.php"
socket "/run/php-fpm.sock"
}
}
location "/comments/feed/*" {
fastcgi {
param SCRIPT_FILENAME \
"htdocs/wordpress/index.php"
socket "/run/php-fpm.sock"
}
}
location "/wp-json/*" {
fastcgi {
param SCRIPT_FILENAME \
"htdocs/wordpress/index.php"
socket "/run/php-fpm.sock"
}
}
location "/wp-login.php*" {
authenticate "WordPress" with "/htdocs/htpasswd"
fastcgi socket "/run/php-fpm.sock"
}
location "*.php*" {
fastcgi socket "/run/php-fpm.sock"
}
}
For greater security, we are going to implement an additional prompt for a username and password when logging in to the WordPress administration site. Since script kiddies like to repeatedly try and bruteforce WordPress's login, we create an additional login at the web server level. Typically, they get about 5 guesses before WordPress throws a 401 Unauthorized error.
doas su
cd /var/www/htdocs
doas htpasswd htpasswd <user>
doas chown www:www htpasswd
doas chmod 0640 htpasswd
doas rcctl reload httpd
We have to make a change in php so that your WordPress installation can send emails. WordPress and some plugins rely on the ability to send emails notifying you of upgrades, alerts, and changes. The inability to send emails can break certain features of WordPress. Since httpd runs in chrooted environment, we have to tell php how to send emails. Furthermore, we have to do some performance tweeks to php-fpm.
Look for the sendmail_path
line in /etc/php-7.3.ini
and make the following change:
; For Unix only. You may supply arguments as well (default: "sendmail -t -i").
; sendmail_path =
sendmail_path = /bin/femail -t -i
Look for the following lines in /etc/php-fpm.conf
and change them as follows:
pm.start_servers = 5
pm.min_spare_servers = 1
pm.max_spare_servers = 6
The next step is to enable and start php-fpm.
doas rcctl enable php73_fpm
doas rcctl start php73_fpm
MariaDB is a drop-in replacement fork of MySQL. We need to do some initial configuration and database preparation work for WordPress.
Before we can use MariaDB effectively, we need to allow the mysql daemon to use more resources than the default. In order to do this, make the following changes to/etc/login.conf
by adding this entry at the bottom.
mysqld:\
:openfiles-cur=1024:\
:openfiles-max=2048:\
:tc=daemon:
We have to make some changes in the MariaDB configuration file, /etc/my.cnf
. By having the mysql client and server communicate via UNIX domain socket instead of TCP, the memory usage of your server can be kept lower. You do not have to make all of the changes suggested below. The two important ones to change are the socket
line and to comment out the bind-address
line. This moves the socket inside of the /var/www
chroot environment so WordPress can connect to the database. By commenting out the bind-address
line, we prevent MariaDB from listening on a TCP port.
[client-server]
socket=/var/www/var/run/mysql/mysql.sock
#port=3306
# This will be passed to all MariaDB clients
[client]
#password=my_password
# The MariaDB server
[mysqld]
# To listen to all IPv4 network addresses, use "bind-address = 0.0.0.0"
#bind-address=127.0.0.1
# Directory where you want to put your data
#data=/var/mysql
# This is the prefix name to be used for all log, error and replication files
#log-basename=mysqld
# Logging
#log-bin=/var/mysql/mariadb-bin
#max_binlog_size=100M
#binlog_format=row
#expire_logs_days = 7
#general-log
#slow_query_log
query_cache_type = 1
query_cache_limit = 1M
query_cache_size = 16M
Now we need to run the MariaDB install binary and enable and start MariaDB. This procedure will set a root password and optionally drop the test database. It's a good idea to follow all of the suggestions in the secure installation stage.
doas mysql_install_db
doas rcctl enable mysqld
doas rcctl start mysqld
doas mysql_secure_installation
Create the WordPress database and database user. Remember to replace <wp_user>
with your choice of database username and <password>
with a complex password of your choosing.
mysql -u root -p
CREATE DATABASE wordpress;
GRANT ALL PRIVILEGES ON wordpress.* TO '<wp_user>'@'localhost' IDENTIFIED BY '<password>';
FLUSH PRIVILEGES;
EXIT
WordPress has not had an official OpenBSD port for quite some time because it pretty much works right out of the box. Download, extract, and move the WordPress installation folder.
cd /tmp
wget https://wordpress.org/latest.tar.gz
tar xvfz latest.tar.gz
doas mv wordpress /var/www/htdocs/.
doas chown -R www:www /var/www/htdocs/wordpress
doas chmod 0755 /var/www/htdocs/wordpress
cd /var/www/htdocs/wordpress/
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
We have to copy /etc/resolv.conf
and /etc/hosts
to a directory we are going to create called /var/www/etc
. This is so that WordPress can successfully reach the marketplace. You will need this in order to download plugins and themes via the WordPress admin site. It is also important for the Jet Pack plugin to work properly.
doas mkdir /var/www/etc
doas cp /etc/hosts /var/www/etc/.
doas cp /etc/resolv.conf /var/www/etc/.
From here, browse to your WordPress website using https via the URL that you specified in the web server definition. If everything is working correctly, you should see the WordPress install wizard. When you get to the option to specify a database server, you should use localhost:/var/run/mysql/mysql.sock
Once WordPress has been installed, it's time to set up the permalinks so that they look more SEO friendly. From the WordPress admin screen, go to Settings -> Permalinks
. Click on Custom Structure and type /posts/%postname%
. After making this change, click the Save Changes button. You now have much nicer looking links. For example, a permalink will look like this: https://example.com/posts/example-blog-post
From here, you should have a basic website ready to go. Make certain you install plugins like Jet Pack and WP-Super Cache. The WP-Super Cache plugin helps speed up your website by caching web pages and eliminating constant database lookups and JetPack gives you some excellent viewership statistics.
It should go without saying that backing up your website and database is very important. Thankfully, this is a relatively easy thing to do. Backup both to your home directory and then you can copy them via scp to another location. You can also create a snapshot via the Vultr Control Panel. It is a good idea to do both.
cd /var/www/htdocs
tar cvfz wordpress.tgz wordpress/
cp wordpress.tgz /home/user
mysqldump -u root -p wordpress > wordpress.sql && gzip wordpress.sql
If your database became corrupted and a restore is necessary, perform the following:
gunzip wordpress.sql.gz
mysql -u root -p wordpress
DROP USER '<user>'@'localhost';
DROP DATABASE wordpress;
CREATE DATABASE wordpress;
GRANT ALL PRIVILEGES ON wordpress.* TO '<wp_user>'@'localhost' IDENTIFIED BY '<password>';
FLUSH PRIVILEGES;
EXIT
mysql -u root -p wordpress < wordpress.sql
If you've made a change to a WordPress script file that broke something, you can always reinstall WordPress via the admin control panel. Look for the Updates
section and click on the link. Look for a button labeled Re-install Now
. This will at least fix what is broken but most of your configuration should remain intact.
If your database is in good shape, but you've accidently edited a file and broke things to the point where you cannot even get to the WordPress admin console, then do the following:
rm /var/www/htdocs/wordpress
cp /home/user/wordpress.tgz /tmp
tar xvfz wordpress.tgz
mv wordpress /var/www/htdocs/.
chown -R www:www /var/www/htdocs/wordpress
cd /var/www/htdocs/wordpress
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;