Setting up SSL everywhere on Nginx & Ghost with LetsEncrypt

I've been busy recently, so this is my first post in a very long time. Studying for exams doesn't leave you much spare time for writing. But I finally decided it had taken me entirely too long to make the move to HTTPS only; and given the fact I recently qualified as an OSCP I figured I should probably take my site's security a little more seriously (not that anyone reads it mind you).

LetsEncrypt is a new open SSL certificate authority (CA) that is open, automated, and most importantly, free. Should you wish to support their work, you can donate to them here.

This article assumes you have already setup a Ghost blog running on Digital Ocean using their pre-built image. If you haven't done so already, then go and do that now. If you don't have an account, then click here and tell 'em I sent ya. You'll get $10 free credit and if you stay long enough I'll get some credit too.

Once everything is up and running, SSH into your server, and before starting anything else run the following to make sure everything is up to date:

$ sudo apt-get update && sudo apt-get upgrade -y

Then before we start modifying the Nginx config, lets take a backup just in case.

$ cp /etc/nginx/sites-available/ghost ~/ghost.bak

Now fire up your favourite editor, and lets get started on these changes.

$ sudo nano /etc/nginx/sites-available/ghost

We need to modify this file in stages, so we can correctly generate the certificates. First remove everything in there, and make it look like the following - replacing yourdomain.com with your site's domain name #obvs :

server {  
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location ~ ^/.well-known {
        root /var/www/ghost;
    }

    location / {
        return 301 https://$server_name$request_uri;
    }
}

Save the file, and then restart the Nginx service.

$ sudo service nginx restart

If the service fails to restart then run sudo nginx -t to discover where the issues are.

Once the service has restarted correctly, now's the time to download LetsEncrypt's certbot software from the Electronic Frontier Foundation's mirror site. This is the software which will generate and manage your certificates. Obviously remember to replace yourdomain.com with your site's domain name.

$ cd /opt
$ wget https://dl.eff.org/certbot-auto
$ sudo chmod a+x certbot-auto
$ sudo ./certbot-auto certonly --webroot -w /var/www/ghost -d yourdomain.com -d www.yourdomain.com

This will generate a .well-known hidden folder in your /var/www/ghost folder which is used to verify that you own the domain. Ensure that your site is currently available over port 80 otherwise this step will fail and the certificates will not be generated.

On successful completion of the cert generation, we need to modify our Nginx config file again. Add the following below the lines you added earlier (again, replacing yourdomain.com with your site's domain name).

server {  
    listen 443 ssl;
    server_name yourdomain.com www.yourdomain.com;

    root /var/www/ghost;
    index index.html index.htm;
    client_max_body_size 10G;

    location / {
        proxy_pass http://localhost:2368;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
    }

    ssl on;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    ssl_prefer_server_ciphers On;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;

    location ~ ^/(sitemap.xml|robots.txt) {
        root /var/www/ghost/public;
    }

    location ~ ^/.well-known {
        root /var/www;
    }
}

Save the file and run sudo service nginx restart once more for the config changes to take effect.

Provided it all went smoothly, you should now be able to browse to https://yourdomain.com to verify that your site is now running under HTTPS. Congrats!

Lastly, we don't want to have to remember to renew our certs ourselves all the time, so we need to set them up to autorenew. Fortunately, certbot makes that nice and easy for us.

First, lets try a dry run to ensure that the autorenew will work:

$ /opt/certbot-auto renew --dry-run 

If all is well, we can setup a cron job to take care of the renewal for us. Enter the following:

$ sudo crontab -e

Now add the following line to the bottom of the file:

0 0 1 */2 * /opt/certbot-auto renew --quiet --no-self-upgrade  

This will autorenew your certificates every other month. All done!