HTTP/2, Let’s Encrypt & Dokku: How to Do All Three

Cool stuff on the web is happening. Makers of low-level web tech handed out end of 2015 webdev Christmas gifts recently, with the arrival of HTTP/2 in NGINX and the public beta of Let’s Encrypt. This is how I embraced both in about 25 minutes.

A Primer

If you’re not familiar with HTTP/2 or Let’s Encrypt, then I suggest reading up on them from a more informed source than I. But if you wanted the TL;DR, the following might suffice…

HTTP/2

HTTP/2 is a super fast protocol for connecting to sites. Faster than plain ol’ HTTP, and faster than SPDY which was a SSL-compliant half step kicking around the last few years. Want to know how much faster? Well for some reason there are numerous sites in the wild loading in tiny images on an HTTP and then HTTP/2 connection to show you the difference. Like… loads of sites are doing this. Weird test, but a neat visual short-cut to see the difference. Check out Cloudflare’s demo or loadimpact.com.

Let’s Encrypt

Let’s Encrypt is a group backed by Mozilla, Akamai, and Facebook amongst others, that looked at SSLs and secure connections — the little green HTTPs at the top of the address bar. Though if you need that technical point-of-information, then maybe stop reading after this paragraph. It will only get more technical — they looked at them and said: “huh… why is this expensive and complicated?”

Well since they asked that question SSLs have got cheaper. They can be attained for as low as $10 a year per domain. But you know what’s cheaper than $10 per domain? $Free per domain! So that’s what Let’s Encrypt have done. They've started issuing free SSLs, that work everywhere you need them (including Android, with IE6 I believe to be the only sticky point).

SSLs being cheap and easy is key to the modern web. First of all we’re passing more data between client and server; thanks in part to the explosion of “smart” frontends, decoupled from a pure API backend — Angular + Django, React + Express, trendy-Javascript + trendy-backend. That data needs to be moving securely (duh). Secondly, the invisible gloves controlling the web are all for it. For example, maybe Google will give you a little cold shoulder in their search algorithms. They said so themselves. oAuth might be heading a similar direction.

Let’s Encrypt aren’t just supplying SSLs that work for free, their second party trick is building a fairly innocuous but genius client to automate the whole process. Really. If you have a vanilla setup (say NGINX or Apache serving your sites) and no Dokku shenanigans getting in the way, then follow their guide. Clone down the client. Run the client. Answer two questions. That’s it.


Now to Put it All Together With Dokku

So… HTTP/2 plus Let’s Encrypt plus Dokku. Let me preface this by saying I'm standing on the shoulders of giants, here; and I've linked my sources where appropriate.

Assumptions

Assume we have the following:

  • 1x domain that runs under both www and non-www. Call it mydomain.com.
  • A functioning and running Dokku application called myappname that is currently being served at mydomain.com.
  • mydomain.com is currently live and running under an insecure http:// protocol.
  • You will likely have to preface all the following commands with sudo or run as root. You’re going to be messing with your /etc/ directory.

That’s exactly where I was with mxbry.com before I started this process.

Step 1: Securing our Site

Download the Let’s Encrypt application. Here I've cloned it in to a /etc/letsencrpyt/ directory purely to keep everything neat. Clone the repo anywhere you don’t mind storing it permanently.

git clone https://github.com/letsencrypt/letsencrypt /etc/letsencrypt/

hakobaito.co.uk sets up the next few steps quite well, and the only complicated step worth understanding is why NGINX requires stopping. Dokku is hogging ports 80 and 443. Let’s Encrypt requires these ports to authenticate the certificate it is generating. Both happening at the same time? No dice right now. So NGINX needs to be stopped, and restarted when the process is over. It shouldn’t be more than 2 minutes downtime for your site. Let’s stop NGINX and then generate our certificate using the Let’s Encrypt CLI:

service nginx stop
/etc/letsencrypt/letsencrypt-auto certonly -d mydomain.com -d www.mydomain.com -m mycontact@email.com auth

Few things to cover here:

  • -d allows you to specify which domain(s) this certificate will be valid for. I added both the non-www and www versions of my site. I don’t know if you need to. Better safe than sorry.
  • -m allows you to specify a contact email for recovering the SSL secret, in case it all goes wrong. If you don’t add this option in as a command line option, you will be prompted for it in the form of a wizard.
  • certonly is because we don’t need all the self-installing wizardry that Let’s Encrypt will offer. We’re going to integrate this with NGINX ourselves.

The certificate parts have been generated. You need to combine them in a .tar and feed them to our NGINX configuration using the Dokku certs plugin that ships with Dokku as standard. I don’t want to bite the work of hakobaito.co.uk too much, but their next steps explain themselves:

cat /etc/letsencrypt/live/mydomain.com/fullchain.pem > /tmp/server.crt
cat /etc/letsencrypt/live/mydomain.com/privkey.pem > /tmp/server.key
tar cvf /tmp/certs.tar /tmp/server.crt /tmp/server.key
dokku certs:add myappname < /tmp/certs.tar

Here we’re stuffing the chain and key in the tmp directory (delete these once you’re done) for convenience.

Now just start NGINX and you should be good.

service restart nginx

www.mydomain.com and mydomain.com should both forward to HTTPS, where they will be running happily and securely.

Step 2: Making our Secure Site Fast

Let’s make our secure site modern by rolling in our new friend HTTP/2. Alas, problems:

  1. Dokku by default ships with NGINX 1.4.2. HTTP/2 support was added to NGINX in 1.9.5. That’s going to be trouble.
  2. The standard Dokku NGINX SSL conf template allows us to run a site under SPDY, but not HTTP/2. So we’re going to have to make an amend to that SSL NGINX conf template.

Two things to do: 1) upgrade NGINX, 2) edit the NGINX template.

Let’s upgrade NGINX. Digital Ocean provide a great guide on how to do this, but we need to make a small amend. Where they have set the PPA target as…

sudo add-apt-repository ppa:nginx/stable

… we will need to change this to…

sudo add-apt-repository ppa:nginx/development

We’re using the development branch, because currently 1.9.x is on NGINX’s “mainline” branch whilst stable is capped at 1.8.x. I’m not going to make any assurances about what you’re gaining and losing by jumping from 1.4.2 to 1.9.5, but it all seems fine to me so far.

With that amend in place, follow the rest of Digital Ocean’s guide.

Now we can support HTTP/2, let’s make sure our site uses it. To do this we need to change the template Dokku uses to generate out the NGINX conf file. Luckily Dokku makes adapting this template easy, without having to dig in to Dokku’s internal files. Dokku’s documentation on this subject outlines two different ways of doing this: either placing our new NGINX template in the root of our Dokku project, or by changing the template on the server.

I prefer the former; it’s cleaner. So in the root of our project’s repository let’s recreate the existing Dokku NGINX template line-for-line, except for a small little edit (in bold):

server {
listen [::]:$NGINX_PORT;
listen $NGINX_PORT;
server_name $NOSSL_SERVER_NAME;
access_log /var/log/nginx/${APP}-access.log;
error_log /var/log/nginx/${APP}-error.log;
return 301 https://\$host:$NGINX_SSL_PORT\$request_uri;
}
server {
listen [::]:$NGINX_SSL_PORT ssl http2;
listen $NGINX_SSL_PORT ssl http2;
server_name $SSL_SERVER_NAME;
access_log /var/log/nginx/${APP}-access.log;
error_log /var/log/nginx/${APP}-error.log;
$SSL_DIRECTIVES
...

Note how in the original Dokku template had spdy as a “listen” protocol. Well now we’ve replaced it with http2 , in two places within the file. If you want to see the full template, either copy the original from Dokku’s repo and make the changes yourself or see what I have committed to my mxbry.com repo.

Commit that file back to your repo, push that repo back up to your server (the ol’ git push dokku master), and you’re done.

mydomain.com running under an SSL on HTTP/2. Easy.

Epilogue

Two things to do now:

  1. Note that the Let’s Encrypt certs expire after 90 days. So 90 days from the day you generate the certs you will have to run all of those Let’s Encrypt and .tar balling commands again, otherwise visitors to your site will be met with a nasty insecure message. Alternatively, you can set up a CRON to do all this for you and never think about SSLs again.
Avoid this every 90 days by setting up a CRON to run the day before your cert expires.

2. You can test your SSL and shiny HTTP/2 status at the following sites:

  • SSL Labs — Test your SSL connection and be notified of any vulnerabilities on your server. Like POODLE for instance.
  • keycdn.com — Just to confirm you’re running on sweet, sweet, HTTP/2.
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.