letsencrypt cert renewal

lighttp, https and cert renewal

david marsh

2023-05-26

Here’s how I worked around an error with automatically renewing my letsencrypt certificate.

tldr

Add this to /etc/lighttpd/mod_ssl.conf:

    url.redirect = (
      "^/\.well-known/acme-challenge/.*" => "",
      "" => "https://${url.authority}${url.path}${qsa}"
    )

intro

This website currently uses lighttpd to serve it’s web pages.

I also use letsencrypt for the TLS certificate.

I enable https and force all http traffic to https, which I achived by following this guide:

How to redirect HTTP requests to HTTPS

I also wanted the certificate to renew automatically without intervention on my part. This is where things fell apart and prompted this post.

cert renewal

As letsencrypt certs expire every 90 days, I didn’t want to manually log on and renew them.

There are two ways to have letsencrypt authenticate the server that I could use:

stand alone

Stand alone opens a temporary webserver on port 80 so the letsencrypt servers can check a temporary file created by certbot and confirm the website is who it says it is. The only problem with this is it means the webserver needs to be stopped to free up port 80. I wasn’t keen on this, not because I have a high traffic website that can’t handle an outage, but I’d prefer not to stop a service that I didn’t need to and there’s always a risk it wont be able to start without intervention on my part.

Something like this would have worked in the crontab, even if it’s not elegant:

rc-service lighttpd stop ; \
/usr/bin/certbot renew --quiet ; \
rc-service lighttpd start

webroot

The other way I wanted to try to have letsencrypt authenticate my server was using webroot. Webroot places a temporary file in the /.well-known/acme-challenge/ directory of the webserver and provides the file via the http server that’s already running.

This means the webserver doesn’t need to be stopped to free up port 80 for the temporary file check.

This allows me to have just the revew entry in the crontab:

/usr/bin/certbot renew --quiet

final config

In the end I went with the webroot solution, as I didn’t want to stop and start the webserver and risk it not starting and me not noticing.

There are three lines that need some explaining:

"^/\.well-known/acme-challenge/.*" => "",

The above line takes precedence, and means that when letsencrypt tries to get the files from the website, it won’t be redirected to the SSL site. This makes sense as there’s no point using a certificate as it’s trying to generate one.

"" => "https://${url.authority}${url.path}${qsa}"

The above line then takes any request that aren’t for the acme-challange files and redirect them to https.

url.redirect-code = 308

The above line uses the newer [308 redirect code].

Here’s how the full /etc/lighttpd/mod_ssl.conf config file looks like:

server.modules += ("mod_openssl")

$SERVER["socket"] == ":443" {
  ssl.engine  = "enable"
  ssl.pemfile = "/etc/letsencrypt/live/rdm.sh/fullchain.pem"
  ssl.privkey = "/etc/letsencrypt/live/rdm.sh/privkey.pem"
}

$HTTP["host"] == "www.rdm.sh" {
  ssl.pemfile = "/etc/letsencrypt/live/www.rdm.sh/fullchain.pem"
  ssl.privkey = "/etc/letsencrypt/live/www.rdm.sh/privkey.pem"
}

$HTTP["scheme"] == "http" {
  $HTTP["host"] =~ ".*" {
    url.redirect = (
      "^/\.well-known/acme-challenge/.*" => "",
      "" => "https://${url.authority}${url.path}${qsa}"
    )
    url.redirect-code = 308
  }
}

testing

Running certbot renew --dry-run now gives me:

rdm:~# certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/rdm.sh.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Simulating renewal of an existing certificate for rdm.sh

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/www.rdm.sh.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Simulating renewal of an existing certificate for www.rdm.sh

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded: 
  /etc/letsencrypt/live/rdm.sh/fullchain.pem (success)
  /etc/letsencrypt/live/www.rdm.sh/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

crontab

Here’s the full crontab:

17  10/23   *   *   *   /usr/bin/certbot renew --quiet