This post will show you how to add SSL capabilities to an nginx site on Ubuntu 16.04. It can also be used as a reference for other webserver and system combinations-you might just need to run slightly different commands in some cases.

I know that there are lots of guides around but I'll attempt to explain each step of the process in more detail, provide further explanation on what each step does and give some insight into my experience with the whole process. I also try to cover the entire process including setting up an automated process for renewals. In a separate post for the future, I hope to add notes on hardening the SSL configuration.


To get started, first install EFF's certbot agent (previously called the letsencrypt agent).

Ubuntu 16.04 currently doesn't contain the newer certbot agent package. We'll use the older letsencrypt agent for simplicity as functionally they're equivalent. Install the agent by running sudo apt-get install letsencrypt


Make sure your site can serve up the ACME challenge/response files when the agent requests a certificate from Let's Encrypt's servers.

Add this to the http server section of the nginx configuration to serve the challenge/response files. We're going to redirect all traffic to HTTPS as well.

# serve up ".well-known" for letsencrypt certbot
location ~ ^/.well-known {  
    root /var/www/ghost;
}

# redirect everything else to https
location / {  
return 301 https://blog.justthespoils.com$request_uri;  
}

Restart nginx systemctl restart ghost # assuming systemd init system is in use


Now request the certificate. In the future, there will be the capability to integrate directly with nginx and other systems, via plugins, to automate the certificate installation. Some of these features are currently in testing.

For now, we're going to use the certonly and webroot options. The certonly option means that we are only going to obtain the certificate and store it locally. We'll then manually set it up in nginx. The webroot option specifies where to store the challenge/response files that will be served by our webserver.

Continuing our example, run letsencrypt certonly --webroot -w /var/www/ghost -d blog.justthespoils.com with the path to your nginx site's webroot and the domain (specified by -d) you want listed on the certificate (you can pass in -d multiple times if you require Subject Alternate Names).

If everything is successful, your certificate files should be at /etc/letsencrypt/live in a directory named after your domain.


Now add the configuration to nginx to serve up your site via HTTPS

server {  
    listen 443 ssl;
    server_name blog.justthespoils.com;

    ...

    ssl on;
    ssl_certificate /etc/letsencrypt/live/blog.justthespoils.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blog.justthespoils.com/privkey.pem;
    ssl_prefer_server_ciphers On;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
}

Finally, let's set up a certificate renewal process since certificates are only good for 90 days. Instead of using cron, let's use systemd timers.

Create a systemd timer configuration to run the renewal task /lib/systemd/system/letsencrypt-renewal.timer

[Unit]
Description=Perform renewal of letsencrypt certificates

[Timer]
OnCalendar=weekly      # run this timer weekly

[Install]
WantedBy=timers.target # when systemd timers are invoked, evaluate this timer as a dependency  

Create a corresponding systemd service to run when the above timer is invoked /lib/systemd/system/letsencrypt-renewal.service

[Unit]
Description=Perform renewal of letsencrypt certificates

[Service]
Type=simple  
ExecStart=/usr/bin/letsencrypt renew  

Enable the timer by running systemctl enable letsencrypt-renewal.timer

That's it! Your site should now be working over HTTPS and certificates should automatically renew!


In the near future, I plan to add some notes on hardening the nginx SSL settings, but for now here's some reading

References
  1. Certbot documentation: https://certbot.eff.org/all-instructions
  2. Certbot documentation for nginx on Ubuntu 16.04: https://certbot.eff.org/all-instructions/#ubuntu-16-04-xenial-nginx
  3. Let's Encrypt documentation site: https://letsencrypt.org/docs/
  4. SSL for Ghost blogs: https://robertnealan.com/setting-up-ssl-for-ghost-on-digitalocean-with-lets-encrypt
  5. systemd timers: https://mjanja.ch/2015/06/replacing-cron-jobs-with-systemd-timers