WordPress hosting from scratch

Photo by Vilmar Simion on Unsplash

Alternative title: Why it’s worth it to pay for wordpress hosting.
Alternative title 2: Why is Ansible so complicated?

I have no idea why someone would scan the outside of a PSU, but it makes for a segue to this lede – which promises to be the best part of this post:

Just your typical barcode jam sesh

In setting up this blog, I am using an Ubuntu 20.04 instance, which comes with root access and not much else. A) I’m an engineer and fiddling with things is my torture device of choice and B) I wanted to dip my toes into some DevOps technologies to see how they worked.

I landed on Ansible almost entirely because the setup process is easier than some of the competition. If you have SSH access between two computers, then great! You can run some Ansible on it.

Long story short, that proved … painful. More painful than this graph

Image of a graph describing how much time is spent automating a task. The graph shows the desired time spent approaching 0, but in reality it increases.
https://xkcd.com/1319/

So, I switched over to the usual duck it, try it, debug the error ways of years past. I ended up with a… “working” nginx -> php -> wordpress installation, but it was quite apparent I made some wrong detours. I started over, recording the steps taken (maybe they’ll become Ansible or a Docker image, who knows). Well let’s jump right in.

Create a new user, remove root

Running as root is bad, so let’s get that fixed:

# remove root user
adduser anil
usermod -aG sudo anil
# copy ssh keys over to anil
rsync --archive --chown=anil:anil /root/.ssh /home/anil
# disable root account
passwd -l root

Now, instead of logging in as root@terminal.space, I can log in as anil@terminal.space. When I need to do admin-y things, that’s what sudo is for.

Set up automatic updates

The tutorials out there have lots of bells and whistles. I already get enough emails, I don’t want any more. Here’s the basic setup:

sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

And then add this line to /etc/apt/apt.conf.d/50unattended-upgrades

Unattended-Upgrade::Automatic-Reboot "true";

Copy vim settings over

<insert snarky vim gif>

sudo apt install -y vim
scp .inputrc anil@terminal.space:/home/anil/
scp .vimrc anil@terminal.space:/home/anil/
scp -r .vim anil@terminal.space:/home/anil/

Set up SSH – Change port, 2FA, keypair

I’m a believer of keypair logins for SSH. If someone has your keys, you’re in trouble anyways. Since this isn’t a root account, you still need the password to do anything elevated. As for changing the port, it’s a bit of security-through obscurity to cut down on script attacks in the logs. If you’re a real person, and are too lazy to run a port scanner, my SSH port is 49622. There’s nothing interesting on this webserver, please leave me alone. (No, seriously, I get no traffic and there’s nothing interesting on this server)

Old, broken down, junk car
Live photo of my webserver. Photo by Ante Hamersmit on Unsplash

First, to install 2FA, I mostly followed this tutorial. (DigitalOcean makes some nice tutorials). In fact, I’m not going to re-write all of the steps for google-authenticator. Just go follow that site. Here’s my sshd_config:

# $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $

# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin

Include /etc/ssh/sshd_config.d/*.conf

Port 49622
LogLevel INFO
PermitRootLogin no
StrictModes yes
MaxAuthTries 6
MaxSessions 10

PubkeyAuthentication yes
PasswordAuthentication no
AuthenticationMethods publickey,keyboard-interactive
#AuthenticationMethods publickey
PermitEmptyPasswords no
AllowUsers anil

ChallengeResponseAuthentication yes
UsePAM yes
X11Forwarding yes
PrintMotd no
AcceptEnv LANG LC_*

Subsystem	sftp	/usr/lib/openssh/sftp-server

Don’t forget to sudo systemctl restart sshd, and then make sure you can log in with a new shell, before closing your existing ssh session!

Installing Nginx/MariaDB/Wordpress

The first go-round I did all of this manually, especially since I’ve spent a lot of time fiddling with Nginx configs in the past. It didn’t go the best but then! I found a script by the Nginx folks to do it all for me 😀

Some notes: The passwords can’t have funky characters because the script isn’t all that and a bag of chips (yet). This isn’t really a complaint as it saved me a lot of time anyways. So for this, you’ll want to sudo su in order to export environment variables properly. (Okay there are other ways, but they have drawbacks).

wget https://gist.githubusercontent.com/nginx-gists/bdc7da70b124c4f3e472970c7826cccc/raw/524a83a8ebc32dd969591f1a71d7ec89bcf9bd6c/ubuntu_install.sh
chmod +x ubuntu_install.sh
# set environment variables listed in blog
./ubuntu_install

Note, that if the script doesn’t run to completion, it doesn’t really work to re-run it. I spent some time making snapshots and restoring to previous in order to get it working.

Setting up WordPress

The two plugins I want to call out are

  • Background Update Tester – I messed this up first time and wasn’t about to have WordPress on manual updates.
  • UpdraftPlus – Backups are good.
  • MiNNak theme
    • One thing that bothered me was the full-bleed image of the featured post, so I modified content.php to have if ( has_post_thumbnail() && ( !is_singular() || has_post _format( 'image' ) ) && ! post_password_required() ) { ?> in the appropriate place to avoid displaying minnak_post_thumbnail()

Remote Backups

UpdraftPlus creates files locally, which is better than nothing, but still not much of a disaster recovery plan. I already use Backblaze for my cloud backup provider, and looked around for ways to hook into that. I settled on Restic. The docs are honestly amazing, go read them if you have any questions. First, you need to initialize the directory on the cloud:

sudo apt install -y restic
# Okay maybe be careful about what you export here as it can end up in your shell history
export RESTIC_REPOSITORY=b2:YOUR_BUCKET_HERE:/restic
export RESTIC_PASSWORD=YOUR_BACKUP_PASSWORD
export B2_ACCOUNT_ID=YOUR_ACCOUNT_ID
export B2_ACCOUNT_KEY=YOUR_ACCOUNT_KEY
restic init
restic backup /var/www

Then, I wanted this to run on a schedule, so I created a bash script with these contents:

#! /usr/bin/bash
export RESTIC_REPOSITORY=b2:YOUR_BUCKET_HERE:/restic
export RESTIC_PASSWORD=YOUR_BACKUP_PASSWORD
export B2_ACCOUNT_ID=YOUR_ACCOUNT_ID
export B2_ACCOUNT_KEY=YOUR_ACCOUNT_KEY
restic backup /var/www
# Purge anything older than a month
restic forget --keep-within 1m

and then I changed the permissions so only root could read it. Then, I added it to cron.

sudo chown root:root /root/backup.sh
sudo chmod 700 /root/backup.sh
sudo crontab -e
# run daily
0 1 * * * /root/backup.sh

Logging

I’m surprised there isn’t much progress here (maybe there is but I haven’t found the right rock to look under). So I set up a free Papertrail log. Looks like I already have some php segfaults to figure out… =I

In Conclusion

Don’t try this at home kids. I mean, I did, so it’s not like you _can’t_. But for anything semi-serious, paying $4/month to host your wordpress blog probably is a better idea.