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:
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:
sudo apt-get update
and installed the Certbot's Nginx package:
sudo apt-get install python-certbot-nginx
That installed Certbox.
Firewall
The we made sure our Firewall let through traffic via HTTPS via ufh
:
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'
When we then checked the status with:
sudo ufw status
HTTPS was allowed through:
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:
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:
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:
# /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:
# /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:
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:
# /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:
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.