Migrating OpenBSD httpd from HTTP to HTTPS

LetsEncrypt offer free TLS certs and have a nice ACME clients to handle auto-renewal. Here’s how to implement on OpenBSD, assuming you have your httpd server up and running on port 80 already.

For example you might have some basic /etc/httpd:

types { include "/usr/share/misc/mime.types" }

server "ndonaghy.com" { 
	listen on * port 80 
	root "/htdocs" 
	location "/stuff/*" { 
		directory { 
			auto index
		} 
	} 
}

Step 1: Add acme-challenge location#

Add this location directive to /etc/httpd.conf:

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

Restart httpd with rcctl restart httpd.

Step 2: Configure acme-client - /etc/acme-client.conf#

Copy the example .conf and replace example.com with your domain and save the file:

cp /etc/examples/acme-client.conf /etc/acme-client.conf

# nano /etc/acme-client.conf
domain example.com {
	alternative names { secure.example.com }
	domain key "/etc/ssl/private/example.com.key"
	domain full chain certificate "/etc/ssl/example.com.fullchain.pem"

Step 3: Run acme-client and fetch key/cert#

Issue this:

acme-client -v yourdomain.com

You should see your key and cert were written; now httpd can use them. Example output:

# acme-client -v ndonaghy.com 
acme-client: /etc/ssl/private/ndonaghy.com.key: generated RSA domain key
acme-client: https://acme-v02.api.letsencrypt.org/directory: directories
acme-client: acme-v02.api.letsencrypt.org: DNS: 172.65.32.248
acme-client: account key: https://acme-v02.api.letsencrypt.org/acme/acct/1234******
acme-client: https://acme-v02.api.letsencrypt.org/acme/finalize/1234******/987*********: certificate
acme-client: order.status 3
acme-client: https://acme-v02.api.letsencrypt.org/acme/cert/0123456789abcdef: certificate
acme-client: /etc/ssl/ndonaghy.com.fullchain.pem: created

Step 4: Update firewall - /etc/pf.conf#

Where you are accepting port 80, now add 443:

pass in proto tcp to port { 80 443 } keep state

Step 5: Update httpd - /etc/httpd.conf#

Add HTTPS section to bottom of the file:

server "ndonaghy.com" {
        listen on * tls port 443
        tls {
                certificate "/etc/ssl/ndonaghy.com.fullchain.pem"
                key "/etc/ssl/private/ndonaghy.com.key"
        }
        root "/htdocs"
}

Step 6: Restart httpd and pf#

Like so:

rcctl restart httpd
pf -f /etc/pf.conf

Step 7: Test!#

Can you navigate to https://yourdomain.com?

If not, retrace steps and check acme-client -v output for any errors. Ensure that acme-client.conf, httpd.conf, and pf.conf are configured correctly, no typos, files exist, etc.

Step 8: Enable auto-renewal#

Without autorenewal, you will have a bad time. Here we run once a day and reload httpd if the certificate renewed; && means execute if acme-client return status was 0; if the certificate did not need renewal or failed to renew for some reason, it won’t reload httpd.

# crontab -e
~	~	*	*	*	acme-client yourdomain.com && rcctl reload httpd

Step 9: Tidy up httpd - /etc/httpd.conf#

Two things to do in HTTP section:

  • Add a redirect so all HTTP requests are redirected to HTTPS:
	location * {
		block return 301 "https://$HTTP_HOST$REQUEST_URI"
	}
  • Remove root /htdocs line; we won’t be serving the site from HTTP anymore.

When done, your full /etc/httpd.conf should look something like:

types { include "/usr/share/misc/mime.types" }

server "ndonaghy.com" {
        listen on * port 80
	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
	location * {
		block return 301 "https://$HTTP_HOST$REQUEST_URI"
	}

}

server "ndonaghy.com" {
	listen on * tls port 443
	tls {
		certificate "/etc/ssl/ndonaghy.com.fullchain.pem"
		key "/etc/ssl/private/ndonaghy.com.key"
	}
	root "/htdocs"
}

Step 10: Accept HTTP#

In case you were wondering:

  • The other verification option is DNS TXT record based, but acme-client on OpenBSD does not support this.
  • In my case, my DNS provider does not support this; but yours might.
  • If you do use DNS TXT verification you need to handle the challenge/response -> DNS update -> DNS propagation aspects, and not with OpenBSD acme-client.