Using `Httpd` and `Acme-Client` on OpenBSD

Pub:
Tag: OpenBSD

When I started using OpenBSD for my web servers a couple months back I had some difficultly getting certificates from acme-client to be signed by Let’s Encrypt. This is a short tutorial on how to get everything running. We’ll work through this webpage’s configuration as an example.

CAA Records

First, you need to make sure you have CAA records for your domain. You’ll need to login to your domain registrar and add something like the following:

    maxbtroeger.com. 300 IN   CAA   0 issue "letsencrypt.org"
www.maxbtroeger.com. 300 IN   CAA   0 issue "letsencrypt.org"
Check your domain registrar’s documentation on how to add CAA records.

Initial httpd setup

With that out of the way we can move on to the server-side of things.

Note: all commands need to be run by root or a privileged user with doas.

In /etc/httpd.conf add the following:

server "www.maxbtroeger.com" {
  listen on * port 80
  root "/htdocs/www.maxbtroeger.com"
  location "/.well-known/acme-challenge/*" {
    root "/acme"
    request strip 2
  }
}

server "maxbtroeger.com" {
  listen on * port 80
  block return 301 "https://www.maxbtroeger.com$REQUEST_URI"
}

# Include additional MIME types
types {
        include "/usr/share/misc/mime.types"
}
this will give you an http page at www.maxbtroeger.com and all traffic from maxbtroeger.com will be redirected to it. Having your bare url redirect to a www. subdomain isn’t necessary, but it’s a good convention. These server blocks also tell Let’s Encrypt how to handle verification requests.

Give everything a test with httpd -n:

# httpd -n
configuration OK
and, if all is well, enable the httpd daemon
# rcctl enable httpd
# rcctl start httpd

Although our webserver is running, we need to tell acme-client how to generate certificates so that httpd can serve the webpage over https.

acme-client setup

First, we’ll make the relevant directories and set their permissions:

# mkdir -p -m 700 /etc/ssl/private
# mkdir -p -m 755 /var/www/acme

And in /etc/acme-client.conf we’ll add the following:

authority letsencrypt {
        api url "https://acme-v02.api.letsencrypt.org/directory"
        account key "/etc/acme/letsencrypt-privkey.pem"
}

domain www.maxbtroeger.com {
  alternative names { maxbtroeger.com }
  domain key "/etc/ssl/private/www.maxbtroeger.com.key"
  domain certificate "/etc/ssl/www.maxbtroeger.com.crt"
  domain full chain certificate "/etc/ssl/www.maxbtroeger.com.pem"
  sign with letsencrypt
}
This tells acme-client that we want a certificate for a primary domain www.maxbtroeger.com aliased to maxbtroeger.com, signed by Let’s Encrypt, and stored in /etc/ssl. If you’re not using a separate www. subdomain like I am, you can remove the alternative names line.

All we need to do now is generate our certificate:

# acme-client -v www.maxbtroeger.com
If all goes well we should see our certificates and keys stored in /etc/ssl
# ls /etc/ssl
...
www.maxbtroeger.com.crt
www.maxbtroeger.com.pem
private/www.maxbtroeger.com.key
...

Final httpd setup

To enable https we need to point incoming traffic to port 443 and tell httpd where our certificate is stored. Make the following changes to /etc/httpd.conf:

server "www.maxbtroeger.com" {
- listen on * port 80
+ listen on * tls port 443
  root "/htdocs/www.maxbtroeger.com"
  location "/.well-known/acme-challenge/*" {
    root "/acme"
    request strip 2
  }
+ tls {
+   certificate "/etc/ssl/www.maxbtroeger.com.pem"
+   key "/etc/ssl/private/www.maxbtroeger.com.key"
+ }
}

server "maxbtroeger.com" {
- listen on * port 80
+ listen on * tls port 443
+ tls {
+   certificate "/etc/ssl/www.maxbtroeger.com.pem"
+   key "/etc/ssl/private/www.maxbtroeger.com.key"
+ }
  block return 301 "https://www.maxbtroeger.com$REQUEST_URI"
}

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

# Include additional MIME types
types {
        include "/usr/share/misc/mime.types"
}
The new block at the end of /etc/httpd.conf redirects all unencrypted http traffic to https.

After checking that all is well with our configuration

# httpd -n
configuration OK
we can restart httpd and see if our web browser recognizes the certificate
# rcctl restart httpd
httpd(ok)
httpd(ok)
https works 👍

https works 👍

Automating renewal

With https working we’ll want to automate certificate renewal. One of the perks of using OpenBSD’s native acme-client over something outside of the base install like certbot is that we don’t need to stop httpd to renew our certificates.

As root add the following to your crontab (run # crontab -e):

0 0 * * * acme-client www.maxbtroeger.com && rcctl reload httpd

Conclusion

Hopefully everything should be running hands-free. If you encounter any difficulty, read the relevant manuals (man httpd.conf and man acme-client.conf) and consider checking out other tutorials: