Securing your self-hosted website with Let’s Encrypt, part 3: using Let's Encrypt

In part 2, we looked at how HTTPS works in practice, what role certificate authorities play and where Let's Encrypt can make things easier.

Now we will finally look at how to use Let's Encrypt to make your life easier, safer and more private!

Installing the Let's Encrypt client

As mentioned in part 2, Let's Encrypt uses an open protocol to connect clients with their certificate-generating servers. This protocol is called ACME, and the beauty of using an open protocol is that anyone can develop a client they like better than the official one provided by Let's Encrypt.

But since we want to keep it simple, we will just install theirs.

There are two ways:

1) using your distribution's package manager

For example:


apt-get install letsencrypt

or 2) installing from source


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

Obtaining a certificate

Perfect! We have the official Let's Encrypt client installed in our system. How do we obtain a certificate?

Like everything open source, there's a million ways of shooting yourself in the feet there are various ways of obtaining a certificate with Let's Encrypt, depending on how you want to prove to the Let's Encrypt server that you own/control the domain for which you want to get a certificate.

These methods are implemented as plugins, and there are a few official built-in plug-ins.

Personally, I like simple methods that I can understand, so I'll just talk about the simplest one that worked for my configuration, using nginx: the webroot method.

Suppose I run and control example.com and I want to generate a certificate for it.

I would run this in my web server (all in one line, sans the )


./letsencrypt-auto certonly --webroot \
-w /var/www/example.com/public_html/ \
-d example.com

Translation:

"I want a certificate for example.com, using the webroot method. The webroot directory for this domain is at /var/www/example.com/public_html/. Do magic"
The client will request a certificate from the L-E server, and after some cryptographic magic dance is performed, the files for our certificate will show up in our server, exactly in /etc/letsencrypt/live/example.com/, where we can find cert.pem, privkey.pem and fullchain.pem---exactly what we need to configure our web server to use https!

Before we continue, a series of notes:

  • this command is valid if you installed letsencrypt from source
  • it also will attempt to auto update itself each time you call it--if you're not happy with that, you should use the non-auto version. Refer to the docs for more info!
  • the first time you run the client it will ask you for your email and to accept the terms of service
  • Also you will probably need to use sudo for this and most of the following commands unless you run this as root, but I'll let you find out

The magic dance, explained

So what did just happen here and why did we need to use that 'webroot' plugin?

Let's look at this again, but in more detail:

  • The client contacts the Let's Encrypt server and requests a certificate for domain example.com
  • The server presents a "challenge" to the client, and says something like: "OK, fine, to prove you control example.com, place these special files in the special location you and me know about"
  • The client takes the files' contents, and places them in the .well-known/acme-challenge web directory --which internally is mapped to /var/www/example.com/public_html/.well-known/acme-challenge using the value of the -w parameter we provided in the command line
  • The Let's Encrypt client contacts example.com and ensures that the files it presented in the challenge are there and also are correct. This could fail if for example you don't actually control the server, or if you changed servers recently and the DNS hasn't propagated yet
  • If the Let's Encrypt server is happy, it will issue a certificate for example.com
  • The files will be placed in /etc/letsencrypt/live/example.com/
  • And all we need to do is to use the keys in our server
  • ✅ done
If you are really intrigued by what's going on, you can look at the log at /var/log/letsencrypt for a very detailed log.

Using a certificate (with nginx)

Now you need to tell nginx that you want to use https in this domain, and point to the certificate keys that Let's Encrypt placed in /etc/letsencrypt/live/example.com/.

Assuming that you have a configuration for example.com in /etc/nginx/sites-available/example.com, this is how the basic https configuration bit would roughly look like:


server {
listen 443;
ssl on;
ssl_certificate /etc/letsencrypt/live/example.com/cert.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
# ...
}

You could go all enthusiastic and restart the server immediately, but I suggest that you test your configuration beforehand:


nginx -t

If all's fine you should get a confirmation message from nginx saying the configuration is correct. If you missed some semicolon or some other little thing, you would get an error message and you can fix it before you stop and then find out that you cannot start the server again because the configuration is broken:


service nginx restart

Yay! the server is now using 🔒https and serving encrypted data, using our newly minted free Let's Encrypt certificate. Woohooo!

It's strongly advised to keep listening to port 80 and install a redirection to send requests from 80 to 443 (i.e. from http to https):


server {
listen 80;
server_name example.com;
rewrite ^ https://example.com$request_uri? permanent;
}

You can register as many certificates as you want in the same server (barring some limits we'll discuss later). Run the command again, providing the new domain and webroot values, update the nginx configuration, and off you go!

Renewing certificates

But don't get too excited... yet! Let's Encrypt certificates are issued for 90 days only. Why? Because it's easier to issue short-lived certificates than invalidating already issued certificates. Anyway---renewal can be automated as well, so it's not as terrible as having to renew and update certificates the traditional way.

The renewal process is quite similar to issuing a certificate, but with one difference:


service nginx stop # ./letsencrypt-auto renew
service nginx start #

This will look at all the certificates that have been issued to this server, and only renew certificates that are going to expire in less than 30 days.

But you will rightfully be asking yourself: why are you stopping the server? why can't we use the webroot system again? Well, let's look at what's happening in detail, again:

  • we stop the web server, to free port 80 (remember we were listening in 80 to redirect http requests to htttps)
  • We ask the client to renew certificates
  • Only certs that expire in less than 30 days will be renewed
  • The client starts a temporary web server in port 80
  • Again, special files are served from this web server to complete the challenges from the Let's Encrypt server, just as when issuing
  • When done, new updated certificates are in place
  • Finally, the temporary server is stopped, port 80 is freed
  • And you can start your web server again

This looks complicated

Yes--specially compared to issuing, which doesn't require stopping and restarting the server.

There probably is a better way of doing this, but it works for me, and more importantly, I understand what is going on.

Of course, I'm still researching because ideally I'd like not to go around restarting servers, so if you have a succinct answer that doesn't involve a one hundred lines cryptic bash script, feel free to let me know :-)

Cron it!

You should not run this manually. You should not trust yourself to remember to update your certificates. I mean, the whole point of Let's Encrypt is automation. So let's put this into a cron file and spend our time doing better things.

Create this script and save it somewhere, setting the value of LE_BIN to the right path in your system:


#!/bin/bash
LE_BIN=/full/path/to/letsencrypt-auto
sudo service nginx stop
sudo $LE_BIN renew > /var/log/letsencrypt/renew.log 2>&1
sudo service nginx start

Make it executable:


chmod +x ./scripts/renew-letsencrypt.sh

And just to make sure it works, try it out before we put it in the Cronfile:


/home/username/scripts/renew-letsencrypt.sh

Did it work? Nice, then let's add it to the cron:


crontab -e

# m h dom mon dow
0 5 * * * /home/username/scripts/renew-letsencrypt.sh

This will attempt to renew certificates every day at 5 AM, which is a time of relatively low traffic for websites which are mostly visited by Europe and Americas. Adjust as you see fit!

Someone asked why running this every day. And to be honest, it could be changed to just weekly. Perhaps even monthly! Up to you.

Urgh, this is still complicated. Isn't there an easier setup?

Yes!

You can use the official Let's Encrypt Apache plugin, if you use Apache (which I don't).

If you use WordPress, someone is working on an (unfinished) plugin that should make this easier for you.

There are also web servers with built-in support for Let's Encrypt, for example Caddy, which has an impressive 28 seconds demo demonstrating the automatic HTTPS feature:

https://youtu.be/nk4EWHvvZtI

And other hosting companies have implemented automatic HTTPS hosting using Let's Encrypt for their users, such as WordPress.com or Dreamhost. But this series of posts is aimed at using Let's Encrypt in your self-hosted websites 💁🏻

Limits and caveats

Or: Let's Encrypt is not perfect 😢

  • There are rate limits in place, but to be honest they seem quite reasonable for individuals and most organisations: 500 registrations per 3 hours, 5 certificates per domain per week, etc... You can add many domains to one certificate though, so even if they don't issue wildcard certificates you should still find your use case covered
  • There are also infrastructure limits: the web server has to be reachable by the Let's Encrypt server, so 'localhost testing' before you go and set up things for real in your live server is impossible. You can ask the client to generate test certificates, which do not count against your daily limit, though.
  • And also the certificate types that can be officially issued is limited right now: only web certificates for now, no wildcard certificates, etc... although people seem to be finding workarounds
This is not to say that things are set in stone. But it's important to be aware of these limits!

Wrap up

So here we are, we now know how to get and auto renew free certificates that are trusted by browsers, using Let's Encrypt. Life is amazing, right?

Except for the fact that basic HTTPS setups can be vulnerable to attacks 😬😬😬

But we'll look at that in the next part: hardening default setups and avoiding known vulnerabilities