OpenBSD httpd and Let's Encrypt

Published on 2021-04-03.

Prepare the zone

Before we do anything with httpd the DNS settings should already be done. There is no standard way of doing this, it all depends on the server administrator, who is running the DNS servers and what the end result should be. I have decided to use these settings:

pwd.re.         A       46.23.92.90
pwd.re.         AAAA    2a03:6000:6f72:615::90
pwd.re.         CAA     0 issue "letsencrypt.org"
www.pwd.re.     A       46.23.92.90
www.pwd.re.     AAAA    2a03:6000:6f72:615::90

The CAA entry is specifying that only Let's Encrypt is authorized to issue certificates for the pwd.re domain. It's not a necessary record to add, but I can do it so why not? :)

Configure httpd, part one

We will do this in a few steps. The first step is to get httpd up and running with a very basic configuration. I recommend taking a quick look at /etc/examples/httpd.conf and reading through the manual pages for httpd and httpd.conf.

Add the following into httpd.conf:

server "pwd.re" {
    listen on * port 80

    location "/.well-known/acme-challenge/*" {
        root "/acme"
        request strip 2
    }
}

Verify that the configuration is good, and if so enable and start httpd:

# httpd -n
configuration OK
# rcctl enable httpd
# rcctl start httpd
httpd(ok)

That's all we have to in httpd.conf for now.

Configure acme-client

The ACME protocol is the magic that in the end enables us to get the certificates we want. There is a client included in OpenBSD base so no extra software needs to be installed.

# cp /etc/examples/acme-client.conf /etc
# mkdir -p -m 700 /etc/ssl/private
# mkdir -p -m 755 /var/www/acme

Add the following into acme-client.conf:

domain pwd.re {
    alternative names { www.pwd.re }
    domain key "/etc/ssl/private/pwd.re.key"
    domain full chain certificate "/etc/ssl/pwd.re.fullchain.pem"
    sign with letsencrypt
}

Let's try and get those certificates we've been waiting for so long:

# acme-client -v pwd.re
[...]
acme-client: /etc/ssl/pwd.re.fullchain.pem: created

Common issues I ran into was DNS issues (A and AAAA record not updated) and forgetting to either open up port 80 in the firewall or forgetting to start httpd.

Configure httpd, part two

Now we can do the final configuration in httpd.conf and enable our website with TLS! Create the directory /var/www/htdocs/pwd.re and modify the httpd.conf file to contain the following:

server "pwd.re" {
    alias www.pwd.re
    listen on * port 80
    block return 301 "https://pwd.re$REQUEST_URI"
}

server "pwd.re" {
    listen on * tls port 443
    # Not a typo, we're chroot'ed to /var/www
    root "/htdocs/pwd.re"

    tls {
        certificate "/etc/ssl/pwd.re.fullchain.pem"
        key "/etc/ssl/private/pwd.re.key"
    }

    location "/.well-known/acme-challenge/*" {
        root "/acme"
        request strip 2
    }
}

server "www.pwd.re" {
    listen on * tls port 443
    tls {
        certificate "/etc/ssl/pwd.re.fullchain.pem"
        key "/etc/ssl/private/pwd.re.key"
    }
    block return 301 "https://pwd.re$REQUEST_URI"
}

This config will enable TLS on https://pwd.re and redirect non-secure HTTP connections and https://www.pwd.re to the non-www variant.

Let's see if it works:

$ curl -s --head http://www.pwd.re | grep Location
Location: https://pwd.re/
$ curl -s --head http://pwd.re | grep Location
Location: https://pwd.re/
$ curl -s --head https://www.pwd.re | grep Location
Location: https://pwd.re/
$ curl -s --head https://pwd.re/ | grep Location
(no output)

Success! We receive the Location header in return, redirecting us to the correct URL.

Final touch

All that's left is to run crontab -e and add the following to the bottom:

# Let's Encrypt
~   *   *   *   *   acme-client pwd.re && rcctl reload httpd

The ~ means "a random value (within the legal range)" (src), which I didn't know about! The crontab entry can be trimmed to not run as often but acme-client will check the certificate and abort if it's not up for renewal, so no extra load is put on the Let's Encrypt servers.

If everything works as it should you should now have a httpd server up and running, with certificates from Let's Encrypt automatically being renewed when necessary. And oh, if you got some extra cash, have a think about donating to Let's Encrypt for the service they're providing