Let's Encrypt, but simple

Posted on 2018-03-18 16:20 in misc

I don't like big monolithic things like Caddy or docker-oriented heavy web servers.
I don't like Certbot managing my web server; it's busy and functionnal already.
I don't like changing DNS records for TLS; most servers don't have any control over their domains, and that's good.

What I like, is having one default HTTP vhost (should be the only one present) that handles Let's Encrypt challenges and redirect the rest to the proper HTTPS vhost; and behind that, a Certbot that will only interact through a shared directory.

Nginx is great and can do that very easily, without heavy additional tools:

  server {
      listen 80 default_server;
      access_log off;
      location ^~ /.well-known/acme-challenge/ {
          root /var/www/challenges/;
      location / {
          rewrite / https://$host$uri;

It's also a good place to use stub_status if you need any, between the two location blocks:

      location /nginx_status {
          stub_status on;
          deny all;

Before reloading nginx, create that shared directory:

mkdir -p /var/www/challenges/
systemctl reload nginx

You of course need Certbot, which has a very nice package for Debian 9 (current stable) and Ubuntu: apt install certbot
Otherwise, https://certbot.eff.org/ has good installation instructions.

Often forgotten step but very important, reload nginx after certificates are changed. crontab -e as root and add that at the end:
0 0 * * * service nginx reload > /dev/null
(You could edit /etc/cron.d/certbot like this but I prefer not to edit files already managed by dpkg.)

Add a vhost

Now the wholesome simple part that hopefully should just work once your DNS is correct:

certbot certonly --webroot -w /var/www/challenges/ -d example.com -d sub.example.com
# feel free to remove the "-d sub.example.com" or add more names

You can add multiple subdomains or alternative domains to the same certificate, but they will have to be renewed at the same time.
It's a bit easier to maintain mutliple certs, especially when it's nicely automated like that. I recommend grouping certs by domain or usage/service. (see Let's Encrypt's rate limits, also keep in mind names will be publicly associated in the certificate)

And once that's done, you should be able to just add or enable the related vhost, with functional TLS.

server {
    listen 443 ssl;
    listen [::]:443 ssl; 
    server_name example.com sub.example.com;
    root /var/www/example/;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

The example.com in all /etc/letsencrypt/ paths is the first -d option passed to certbot, used to identify the certificate. If you added multiple -d options, they will share that certificate.

And with the previous default HTTP vhost, http://example.com and http://sub.example.com will redirect to their https:// version, unless it's about ACME challenges.