The Lugo Labs website and all our product websites now run on HTTPS using the free certificate provided by Let's Encrypt certificate authority. The whole process to install the certificates on a Ubuntu 16.04 server was easy to follow and completed in less than an hour. Here's what we did.

Certbot

We used Certbot to manage the certificate and followed Digital Ocean instructions on how to install it.

Firstly, we added the Certbot repository:

sh
sudo add-apt-repository ppa:certbot/certbot

We pressed ENTER to accept and followed the instructions to complete the installation. The we update the server with:

sh
sudo apt-get update

and installed the Certbot's Nginx package:

sh
sudo apt-get install python-certbot-nginx

That installed Certbox.

Firewall

The we made sure our Firewall let through traffic via HTTPS via ufh:

sh
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'

When we then checked the status with:

sh
sudo ufw status

HTTPS was allowed through:

sh
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)

Let's Encrypt

Certbot makes obtaining certificates from Let's Encrypt very easy:

sh
sudo certbot --nginx -d lugolabs.com -d www.lugolabs.com

Naturally you'd want to replace lugolabs.com with your own domain name. The --nginx tells Certbot to use its Nginx plugin.

We then entered the process of obtaining a certificate by providing an email address. Certbot checked with Let's Encrypt servers and ran a challenge to verify that we controlled the domain name. At the end Certbot asked if we wanted to redirect all requests to HTTPS and we agreed.

At this stage the certificate was obtained successfully and Certbot told us where the certificate and chain had been installed. This is very important for later when we need to update our Nginx configuration.

Auto-Renewal

As part of the above installation, Cerbot also installs a file inside /etc/cron.d which runs a script twice a day and renews a certificate within 30 days of expiration. Let's Encrypt certificates expire after 90 days so this step is quite important.

To test the renewal process, we ran a dry test with Certbot:

sh
sudo certbot renew --dry-run

We saw no errors, which told us that the installation had been completed successfully on this step too.

Nginx

We use Capistrano to deploy our applications as explained in an earlier post. We also use ERB templates inside our repo to configure Nginx, so we first updated our Capistrano deploy.rb file:

ruby
# /config/deploy.rb

set :application,     'lugolabs'
set :full_app_name,    "#{fetch(:application)}_#{fetch(:stage)}"
set :website_url,     'lugolabs.com'
set :enable_ssl,      true
set :ssl_certificate, '[PATH TO LETS ENCRYPT CERTIFICATE]'
set :ssl_key,         '[PATH TO LETS ENCRYPT KEY]'

We copied paths to fullchain.pem and privkey.pem previously told us by Certbot on the step above to [PATH TO LETS ENCRYPT CERTIFICATE] and [PATH TO LETS ENCRYPT KEY] respectively.

Then we added the following block to our Nginx template after the port 80 declarations:

ruby
# /config/deploy/shared/nginx.config.erb

<% if fetch(:enable_ssl) %>
server {
  server_name www.<%= fetch(:website_url) %>;
  listen 443 ssl;
  root <%= fetch(:deploy_to) %>/current/public;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @<%= fetch(:full_app_name) %>_puma;
  location @<%= fetch(:full_app_name) %>_puma {
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    proxy_pass http://<%= fetch(:full_app_name) %>_puma;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;
  ssl_certificate <%= fetch(:ssl_certificate) %>;
  ssl_certificate_key <%= fetch(:ssl_key) %>;
}
<% end %>

We copied the configuration into the server by running our setup_config task:

sh
cap production deploy:setup_config

The task reloaded Nginx, allowing requests via HTTPS.

SSL

We also forced SSL on our Ruby on Rails application using the force_ssl configuration:

ruby
# /config/production.rb

config.force_ssl = true

This way all access to the app will be done over SSL, using Strict-Transport-Security and secure cookies. Nice juice provided by Rails for free with just one line.

We deployed the app again to see our site finally running on SSL.

More subdomains

We then wanted to do the same for our products, Tracker and Iconly, which run on lugolabs.com subdomains. Let's Encrypt does not provide wildcard certificates for *.lugolabs.com, but Certbot makes adding subdomains to the certificate quite easy:

sh
sudo certbot --expand -d lugolabs.com -d www.lugolabs.com -d tracker.lugolabs.com -d iconly.lugolabs.com

We had to include the existing added subdomains too as expected by Certbot.

Then we update the Nginx configs for Tracker and Iconly and forced the SSL too on the respective config/production.rb files. After deploy, both apps were running over SSL nicely.