TLS-enable your Horizon application with Let's Encrypt

Transport Layer Security (TLS) helps protect network communication from eavesdropping and man-in-the-middle attacks. Enabling TLS everywhere is increasingly regarded as a best practice, but it’s especially important in applications that accept sensitive information or perform user authentication.

In the past, the cost and difficulty of obtaining certificates created barriers to ubiquitous TLS adoption. That changed when a group of companies and non-profit organizations launched Let’s Encrypt, a largely automated system that dispenses certificates at no cost. In this tutorial, I’ll describe how you can TLS-enable your Horizon application in a matter of minutes using a free certificate from Let’s Encrypt.

Deploy a Horizon application on your server

To deploy my Horizon application, I set up a Digital Ocean droplet that runs Ubuntu 16.04. I performed the following steps to obtain all of the necessary dependencies:

  • Installed RethinkDB from our APT repository
  • Installed Node.js and npm from Ubuntu’s Universe repository
  • Created a symbolic link at /usr/bin/node and pointed it to /usr/bin/nodejs
  • Globally installed Horizon from npm
  • Used hz init to create a new Horizon project
  • Set up a DNS record for my domain to point it to the server’s public IP address

If you need more help figuring out how to install the dependencies and create a Horizon project, you can refer to our installation docs. After I installed the dependencies and created the project, I ran the application without TLS to ensure that everything worked properly:

hz serve –dev –bind 0.0.0.0 –port 80

The standard Horizon application template includes an index.html file that displays a little message when it successfully establishes a WebSocket connection to the backend. When I visited my new application in the browser, the message displayed correctly. If you don’t see it, you might be able to find out what went wrong by adding the --debug flag to hz serve or looking at any error messages that appear in the browser’s JavaScript console.

Use certbot to get a certificate

Under the hood, Let’s Encrypt is powered by the Automatic Certificate Management Environment (ACME), which provides a standardized protocol that clients can use to request certificates. The EFF maintains a high-quality ACME client called certbot that users can run at the command line. The certbot client is highly portable because it’s written in Python. On an Ubuntu server, you can install it from the distro package repository where it’s called letsencrypt.

Certbot has several built-in mechanisms that it can use to verify your ownership of a given domain, but the webroot method is best-suited for bare Horizon applications. When you tell certbot to use the webroot method, it plants a file in the directory served by your Horizon application so that the ACME backend can see it when it hits the corresponding URL on your domain. To get your certificate, execute the following command while your Horizon application is running in the background on port 80:

letsencrypt certonly –webroot -w dist -d mydomain.com

The -w parameter tells certbot to plant its file in the dist folder. The -d parameter specifies the domain name for your application. When you run the command, certbot will prompt you to provide your e-mail address and agree to the Let’s Encrypt terms of service. After you enter that information, certbot will download your certificates and display a message that tells you where they are stored on the filesystem.

The Let’s Encrypt certificates are valid for 90 days. You can use certbot to renew them from the command line by periodically running letsencrypt renew. The command checks to see if your certificates are expiring and performs the renewal process as needed. Let’s Encrypt recommends setting up a cron job that runs the command twice a day.

Configure Horizon to use the certificate

Now that you have a certificate, you have to configure Horizon so that it knows where to find the key and certificate on the filesystem. Certbot stores everything in /etc/letsencrypt, creating a special subdirectory for each domain. If your domain is mydomain.com, the files are located in /etc/letsencrypt/live/mydomain.com/.

In the directory for your domain, you will find privkey.pem and cert.pem. Edit your .hz/config.toml and set the key_file and cert_file options so that they point to those files accordingly:

secure = true
key_file = "/etc/letsencrypt/live/mydomain.com/privkey.pem"
cert_file = "/etc/letsencrypt/live/mydomain.com/cert.pem"

You can also take the opportunity to set the bind and port values in the config file so that you don’t have to specify them manually at the command line when you run hz serve. To make sure your application is accessible at an HTTPS address, use port 443 instead of 80:

bind = ["0.0.0.0"]
port = 443

When you finish editing your .hz/config.toml, run your Horizon application with hz serve. You should be able to access it by visiting your domain with the HTTPS protocol. If everything worked as expected, your browser will display the lock sigil typically associated with a TLS-enabled website. When TLS is enabled, Horizon uses it for WebSocket connections in addition to serving static files with HTTPS. If you look in the Network panel in Chrome’s developer tools, you will see that Horizon’s WebSocket connection uses the WSS protocol, the WebSocket equivalent of HTTPS.

Alternative approaches

This tutorial focuses on how to use certbot to enable TLS for a bare Horizon application running directly on port 443. There are a number of other deployment scenarios that you might want to consider. If you use NGINX as a reverse proxy in front of your Horizon application, for example, you could configure TLS in your NGINX vhost instead of your .hz/config.toml. You could also use a reverse proxy that has a built-in ACME client so that you don’t need to run certbot to get your certificates. Caddy is a good piece of software for users who want that kind of setup.

Ready to start building your application with Horizon? Check out our getting started guide to learn more.

Resources: