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.
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
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
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.
Once the script is tailored to your needs, initialise it:
~/bin/cert-ctl.sh init
You can view the current state of the certificate and expiry:
~/bin/cert-ctl.sh cert
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
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.
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]