Let's Encrypt!

These days, every website should have a digital certificate. HTTPS isn't just for your login and shopping cart checkout pages anymore.

Fortunately, "Let's Encrypt is a free, automated, and open certificate authority (CA), run for the public’s benefit." [1]

Unfortunately, my website is on a shared host (Media Temple) where I can't run the automation tools required for Let's Encrypt.

Here's how I made it work:

Let's Encrypt certificates expire in 90 days. Usually, Certbot and crontab are used to automatically manage and renew the certificates before the 90 days are up. The problem is that on a shared host (like Media Temple) it's not possible to install and run Certbot.

Media Temple does provide a guide to install a Let's Encrypt certificate, but the process is manual and error-prone. So, I built a script to automate as much of the task as possible: cert-ctl.sh.

Requirements

Summary

  • Use SSHFS to mount the remote host as a local directory.
  • Configure Certbot on the local system using webroot.
  • Import the certificate(s) using Media Temple's web interface.
  • Set reminder to renew every 90 days.
  • Additional configuration of the website to use HTTPS.

Procedure

  1. Install Certbot and SSHFS on your local system.

    I used a virtual machine running Debian 9 (Stretch), so installation was straightforward:

    sudo apt-get install certbot sshfs
    
  2. Configure Certbot. This is where the cert-ctl.sh script comes in.

    The script mounts the remote host via SSHFS, then uses Certbot's webroot plugin to place signed files in the appropriate path on the web server. Using certonly causes Certbot to generate and verify the certificate, but not attempt to install it locally.

    Configure the script using your domain name(s) and SSH login information. The paths can be changed, but using user-writeable directories means it can be run without sudo.

  3. Once the script is tailored to your needs, initialise it:

    ~/bin/cert-ctl.sh init
    
  4. You can view the current state of the certificate and expiry:

    ~/bin/cert-ctl.sh cert
    
  5. Import the certificate(s) and key. Media Temple provides a guide to importing SSL certificates.

    To get these in the right format for pasting, I run the following in a terminal:

    ~/bin/cert-ctl.sh key
    cat ~/cert/etc/live/primarydomain.tld/cert.pem
    cat ~/cert/etc/live/primarydomain.tld/chain.pem
    
  1. This is not 100% automated, so I still have a reminder notification set for 10 days before the certificate expires to renew and import. To renew:

    ~/bin/cert-ctl.sh renew
    

    And to import it's the same as above.

  2. I updated my website URLs to use HTTPS. I also added a redirect in my .htaccess file to force clients to HTTPS by default.

Files

cert-ctl.sh

#!/bin/bash

# Certbot
domains=(primarydomain.tld
     secondarydomain.tld)
localTopDir=~/cert
localConfDir="$localTopDir/etc"
localWorkDir="$localTopDir/lib"
localLogDir="$localTopDir/log"
localWebroot="$localTopDir/www"

# sshfs
sshHost=SSH_HOSTNAME
sshPort=22
sshUser=SSH_USERNAME
sshDir=/path/to/web/root
sshMountDir="$localWebroot"

function show_help {
     echo "$0 <cert | init | key | renew>"
}

function mount_remote {
     sshfs -p "$sshPort" \
             "$sshUser@$sshHost:$sshDir" \
             "$sshMountDir"
}

function umount_remote {
     fusermount -u "$sshMountDir"
}

function remote_certbot {
     if [[ $# -gt 0 ]]; then
             mkdir -p "$localConfDir" "$localWorkDir" "$localLogDir"

             command=$1
             shift
             options=$@

             certbot "$command" \
                     --config-dir "$localConfDir" \
                     --work-dir "$localWorkDir" \
                     --logs-dir "$localLogDir" \
                     $options
     else
             show_help
     fi
}

case $1 in
     init)
             mount_remote
             domainOptions=()
             for domain in "${domains[@]}"; do
                     domainOptions+=(-w "$localWebroot/$domain/html" -d "$domain" -d "www.$domain")
             done
             remote_certbot certonly --webroot ${domainOptions[@]}
             umount_remote
     ;;
     renew)
             mount_remote
             remote_certbot renew
             umount_remote
     ;;
     cert)
             remote_certbot certificates
     ;;
     key)
             openssl rsa -in "$localConfDir/live/$domains/privkey.pem" -out -
     ;;
     *)
             show_help
     ;;
esac

.htaccess

RewriteEngine on

# Rewrite to HTTPS.
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Links

Social Media