New hosting

The day has come and I have to say, it wasn't as painful as I was fearing. The Gray Matter blog is now running on Google's Cloud Platform, and the results are really encouraging so far. In this post, I'm going to explain how I shed my old school hosting provider and embraced "the cloud"!

First of all, some prerequisites:

  1. You need a Google account. Gmail/Youtube/etc...all that stuff requires a Google account, so for most of the population, this is covered.

  2. You need a basic understanding of how a "LAMP" stack works (Linux, Apache, MySQL and PHP). For this blog, I skipped the MySQL and PHP as I don't use them.

  3. Comfort on the command Linux line.

This is basic knowledge for most entry-level system administrators and web developers so, there's nothing particularly onerous or new here.

For this blog I had a few design goals in mind, which I'm pleased to say I've tackled and will document the steps involved:

  • Run a modern Linux distribution (Debian 10 in my case)

  • Automate most of the ongoing management

  • Host it locally (in Australia)

  • Keep it lean!

The high-level process is:

  1. Spin up a virtual machine with your preferred operating system.

  2. Get remote shell access (either gcloud or ssh)

  3. Configure Apache

  4. Configure DNS

  5. Set up SSL via letsencrypt.org

  6. Redirect non-encrypted requests to https

1. Virtual Machine Setup

Firstly, the whole process of setting up a LAMP instance on Google Cloud Platform has been done to death and I simply worked from the wonderful guide published by Chris Titus (thanks Chris!). Keep in mind, I didn't install the tasksel package or any of the MySQL and PHP stuff; no need for a static site like mine. In Chris' process, I skipped steps 3, 5 and 6.

When setting up I selected a local zone australia-southeast1-b. Latency went from ~400ms to my existing provider (in Utah, USA) to ~15ms at Google in Australia! WIN!! Of course, the opposite is also true; latency for USA visitors just went up. 🤷🏻‍♂️

If you're running MacOS with Homebrew on your desktop, you can easily install the Google Cloud command line tools by running:

brew install google-cloud-sdk

Now you can use any gcloud commands suggested in Google's Cloud Platform or other sources etc.

The whole setup process took about 10 minutes and in the end I had a basic Debian 10 system running Apache with remote access via gcloud beta compute --project "project-foo" ssh --zone "australia-southeast1-b" "my_vm_name"..but that's a realy ugly command line. Lets make it better with plain old SSH!

2. Remote Shell access

Your freshly-minted virtual machine will have a public IP address and you can access it with Google's gcloud command line tool (as explained in Chris Titus' documentation), or with a little effort, you can copy your existing public ssh keys to the VM and use standard tools like ssh and rsync etc.

To accomplish this simply:

  • copy your existing public key(s) to the clipboard

  • log into your VM with gcloud command line tool

  • edit the ~/.ssh/authorized_keys file on the VM and paste your key(s)

  • log out

You can now log into your VM by simply:

ssh user@111.222.333.444

Where user is the username you configured for your VM and 111.222.333.444 is the public IP of your VM.

2.1 Tightening up shell access

Now you have access to your remote host via SSH. Unfortunately everyone else on the Internet can also try to log in too. The good news is, Google's default images have a tight SSH configuration out of the box and deny password based authentication; key based authentication only! Nice work Google!

However, the SSH daemon runs on the standard port of TCP/22 and as a result is a big target for script kiddies. You can relocate the daemon to run on a non-standard port, but in my experience this of limited used. Instead, I simply leave it on the standard port and install SSHGuard via Debian package:

sudo apt install sshguard

The defaults for SSHGuard are suitable for most hosting arrangements, but if you regularly connect from a static IP (home/office/etc) add that IP to the /etc/sshguard/whitelist file. For instance, if your static home IP is 444.333.222.111 you would add the following line to the SSHGuard whitelist:

444.333.222.111/32

These lines a are simply a single IP or network range in standard CIDR notation.

3. Configure Apache

The best bit about Apache is that its been around forever, and there's a LOT of documentation. The worst part about Apache is that its been around forever, and there's a LOT of (bad/outdated) documentation. However, for my purposes I'm running a single website with a few aliases and that setup is about as basic as it gets.

Here's the high-level steps

  • Create a directory for your content

  • Set appropriate permissions

  • Tell Apache to use your new directory

3.1 Creating a new directory and set permissions

This bit is simple. I usually create a directory with the site name or domain to make it easy to manage:

sudo mkdir /var/www/gray.net.au
sudo chown root:adm /var/www/gray.net.au
sudo chmod 2775 /var/www/gray.net.au

Those three commands create a directory and then set the permissions so that root and anyone in the adm group can create/edit and delete files and directories under /var/www/gray.net.au. The mode I use (2775) also sets the group ID bit so NEW files/directories created will be assigned the adm group.

For a static HTML site, the permissions for Apache only require that the web server process can read the files. So it really doesn't matter what user or group "owns" the file/directory as long as Apache can get to it. This is important to understand as your SSH login logs you in, not the Apache process user, or root etc. By keeping the permissions under my own user name and a group I'm a member of, I don't need to do any special SSH magic or post-upload scripting to set the permissions correctly.

3.2 Telling Apache how to serve my new site

The documentation Chris Titus put together covers most of this in step 4, so I wont go over it in too much more detail here, but I'll show you a few Apache rewrite tricks to make things more consistent in the next section.

BEWARE - Before you move to the next section, you need to understand if you have an existing website, the DNS changes will send all your visitors to the NEW setup on Google Cloud Platform. If this is NOT what you want, then DON'T CHANGE YOUR DNS SETTINGS!! You may want to configure a sub-domain (eg, test.mydomain.tld) but that is beyond the scope of this document.

4. Configure DNS

The Domain Name System is how everyone on the Internet finds your site; it's how a host name (web site) is translated into an IP address which you computer can actually use. Depending on your DNS hosting provider, you may want to set up a few addresses to point at your new Google Cloud Platform host.

I don't intend covering all the different record types in DNS but here's the key ones you will probably want to set up:

  1. An A record for www.yourdomain.tld pointing to the Google Cloud public IP.

  2. An @ record for yourdomain.tld pointing to the Google Cloud public IP.

  3. A CAA record to nominate your SSL certification authority (not all DNS providers support this one yet)

The way these records look in your DNS provider's system could vary considerably. So here's how they look according to DNS after they are configured:

# Resource            TTL Type     Target
www.yourdomain.tld.   120     IN      A    111.222.333.444
yourdomain.tld.       120 IN  A    111.222.333.444
yourdomain.tld.       300     IN      CAA      0 issue "letsencypt.org"
yourdomain.tld.       300     IN      CAA  0 issuewild "letsencrypt.org"

Basically, this sets us up for the next step of configuring SSL/TLS in Apache with letsencrypt.org and also makes the site accessible via either http://www.yourdomain.tld OR http://yourdomain.tld.

4.1 Rewriting requests for fun (and profit?)

A lot of sites (mine included), are dropping the "www" prefix in favour of the plain domain. Apache can make your site more consistent, by sending all requests for "www.yourdomain.tld" to the short-hand "yourdomain.tld". To do this, we'll take advantage of Apache's rewrite module, which should be enabled by default in Debian 10. If not, you can enable it:

sudo a2enmod rewrite

After this, add the following to your /etc/apache2/sites-enabled/yourdomain.tld.conf inside the <VirtualHost *:80> section:

RewriteEngine on

# Redirect www.yourdomain.tld -> yourdomain.tld
RewriteCond %{HTTP_HOST} ^www\.(yourdomain\.tld.*)$ [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,L]

Then reload Apache:

sudo systemctl restart apache2

Now when a visitor goes to http://www.yourdomain.tld their browser gets told to try again on http://yourdomain.tld. This is accomplished with a "permanently moved" redirect. Try it for yourself 🙂:

>curl -I http://www.gray.net.au  <-- try the www address
HTTP/1.1 301 Moved Permanently   <-- get told its moved
Date: Mon, 02 Dec 2019 00:58:22 GMT
Server: Apache/2.4.38 (Debian)
Location: http://gray.net.au/    <-- use this address instead
Cache-Control: max-age=600
Expires: Mon, 02 Dec 2019 01:08:22 GMT
Content-Type: text/html; charset=iso-8859-1

5. Setting up encryption with letsencrypt.org

By now, you should be able to access your new site by both http://www.youdomain.tld and http://yourdomain.tld. If not, STOP HERE, go back and fix up your DNS/Apache configurations. The letsencrypt.org process implements some validation steps that will fail without your site working. There are ways around this, but I'm not covering them here; path of least resistance.

The easiest way to configure a free SSL certificate with letsencrypt.org is to use the certbot tool. This will configure Apache, generate and install the certificates etc, and keep it all updated for you.

In my case I simply ran through the instructions for installation on Debian 10. If you're running a different Linux distribution tweak the drop-downs at the top of the page next to "My HTTP website is running...".

5.1 Fine tuning ciphers and protocols

Unfortunately, certbot does a pretty awful job of configuring the encryption in Apache and allows a whole raft of really poor ciphers and protocols (notably TLS 1.0 and TLS 1.1). You don't want them.

There's actually 3 files you need ensure have the same directives, which is a pain. Or you could simplify this task by ripping all the directives out into a single file, then include that file from the 3 locations. I went the latter path.

  1. Create a new file /etc/apache2/my-ssl.conf

  2. Populate the file in #1 with the following:

    SSLProtocol -ALL +TLSv1.2 +TLSv1.3
    SSLOpenSSLConfCmd Curves X25519:secp521r1:secp384r1:prime256v1
    SSLHonorCipherOrder On
    SSLCipherSuite ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS:!AESCCM
  3. Remove those 4 directives from the following files and replace with Include /etc/apache2/my-ssl.conf:

    1. /etc/letsencrypt/options-ssl-apache.conf

    2. /etc/apache2/sites-enabled/yourdomain.tld-le-ssl.conf

    3. /etc/apache2/mods-available/ssl.conf

So basically, you now have a common point to fine-tune the encryption for Apache. Make sure you only have the Include /etc/apache2/my-ssl.conf ONCE in each of those files and the 4 directives it defines DO NOT appear anywhere else in the files listed in 3(a-c).

Assuming the configuration is still sane, you can now restart Apache and enjoy your encrypted site!:

sudo apachectl -k configtest
sudo systemctl restart apache2

5.2 Sending all traffic to the encrypted site

Congratulations! If you've made it this far, you're nearly done. Lets close out the possibility of someone accidentally going to the unencrypted version of the site by leveraging another Apache rewrite rule!

Edit the /etc/apache2/sites-available/yourdomain.tld.conf file again, and add the following BELOW the rewrite rule we added 4.1:

# Send everything over HTTPS
RewriteCond %{SERVER_NAME} =yourdomain.tld
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

Assuming the configuration is still sane, you can now restart Apache and enjoy your FULLY encrypted site!:

sudo apachectl -k configtest
sudo systemctl restart apache2

Test it for yourself 🙂

>curl -I http://gray.net.au     <-- requested the unencrypted site
HTTP/1.1 301 Moved Permanently  <-- told its moved permanently
Date: Mon, 02 Dec 2019 01:42:40 GMT
Server: Apache/2.4.38 (Debian)
Location: https://gray.net.au/  <-- use the encrypted site instead
Cache-Control: max-age=600
Expires: Mon, 02 Dec 2019 01:52:40 GMT
Content-Type: text/html; charset=iso-8859-1

Voila!

Comments

Comments powered by Disqus