Let's Encrypt and OpenBSD's httpd
June 2020 update: These instructions should work for OpenBSD 6.8. Do
not assume they work well, or even work for any other version. Here's
why: I wrote this post for OpenBSD 6.1. The instructions worked up to
6.4. Then there was a very slight syntax change for httpd.conf
. While
I caught this for my own httpd.conf
file, I forgot to update this blog
post for almost 18 months. The instructions were broken. It is only
thanks to reader L.R. that the post has been fixed and made to work
again on OpenBSD 6.7. There's a reason why the OpenBSD devs tell people
to use the man pages and official FAQ, not random webpages!
These instructions cover using Let's Encrypt, OpenBSD's httpd
, and the
acme-client
program to serve websites using SSL certificates.
A caveat: these are very basic instructions from somebody who has been
using httpd
for a while but has just set up SSL for the first time.
They may not make sense if you're trying to learn both httpd
and how
to use it with SSL.
I am indebted to Michael W. Lucas's book Relayd and Httpd Mastery for
walking me through the process. Between it, the files in OpenBSD's
/etc/examples/
directory, OpenBSD's manual pages, and of course the
developers' commitment to simplicity, setting up httpd
is painless.
If there are mistakes below, they are solely mine.
1. Getting httpd ready
We will get our SSL certificate from a certificate provider called Let's
Encrypt. We do this by running a program called acme-client
. One of
the things acme-client
does is write some files and make them
publically available on our site, so as to prove to Let's Encrypt that
we are the site's owner.
Configure httpd and your site to work with acme-client.
This is
really easy! Just configure your /etc/httpd.conf
file to look mostly
like this:
You just need to add
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
directory no auto index
}
to your /etc/httpd.conf
file. As such, mine looks like this:
server "www.increasinglyadequate.com" {
alias "increasinglyadequate.com"
listen on egress port 80
log style combined
root "/htdocs/increasinglyadequate"
# The next five lines are what you need for acme-client:
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
directory no auto index
}
}
Now run rcctl restart httpd
so your changes take effect.
2. Getting your certificate via acme-client
acme-client
is responsible for getting and renewing your certificates.
The file /etc/acme-client.conf
tells acme-client
how to operate.
Configuring this file is also very simple!
The file comes pre-loaded with the appropriate settings for Let's Encrypt. Leave these be. At the bottom of the file you will find a commented-out section containing an example configure for a domain. All you'll need to do is change the name "example.com" to whatever your domain name is. That's it.
By default, acme-client stores its files in a couple of directories. I
am not a fan of this because I am slow and like everything in one place.
Michael W. Lucas is smart and even he advocates a naming scheme other
than the default. His scheme uses subdirectories to indicate that the
files are managed through acme-client and that they pertain to a
particular domain (e.g., he recommends using a directory like
/etc/ssl/acme/increasinglyadequate.com/
). Since I have just a couple
of sites and am using just one SSL certificate provider, I'll stuff
everything into /etc/ssl/
. You do whatever you want.
Anyhow, my domain section for www.increasinglyadequate.com
looks like
this:
domain www.increasinglyadequate.com {
alternative names { increasinglyadequate.com }
domain key "/etc/ssl/private/www.increasinglyadequate.com.key"
domain full chain certificate "/etc/ssl/www.increasinglyadequate.com.fullchain.pem"
sign with letsencrypt
}
Note that you can have multiple domains listed in this file.
Assuming you've followed step 1 and already configured
/etc/httpd.conf/
with the appropriate lines, you are ready to get your
certificate.
# acme-client -v www.increasinglyadequate.com
Because of the -v
flags for verbosity, you'll get a lot of output.
But at the end you should see something like this:
...
acme-client: /etc/ssl/increasinglyadequate.fullchain.pem: created
Verify:
# ls -l /etc/ssl/www*
-r--r--r-- 1 root wheel 3859 Jul 8 16:52 www.increasinglyadequate.com.fullchain.pem
# ls -l /etc/ssl/private/
-r-------- 1 root wheel 3272 Jul 8 16:47 www.increasinglyadequate.com.key
If you get a message indicating failure, it's most likely because you
haven't configured /etc/httpd.conf
as in step 1 or because of some
typo in /etc/acme-client.conf
.
3. Setting up httpd to serve your site using SSL
This configuration file should only be used if you have completed the
previous steps. In particular, if you redirect http traffic to https,
but https doesn't work yet, acme-client
won't be able to communicate
with Let's Encrypt.
At any rate, provided you've got your SSL certificate, we can now tell
httpd
to serve your site using it, and we can tell httpd
to redirect
all http traffic to https. Just edit /etc/httpd.conf
to look something
like this:
# This block redirects port 80 traffic to port 443; all the actual configuration
# options can go underneath the block containing tls details.
server "www.increasinglyadequate.com" {
alias "increasinglyadequate.com"
listen on egress port 80
block return 301 "https://$SERVER_NAME$REQUEST_URI"
}
server "www.increasinglyadequate.com" {
alias "increasinglyadequate.com"
listen on egress tls port 443
hsts
gzip-static
tls certificate "/etc/ssl/increasinglyadequate.fullchain.pem"
tls key "/etc/ssl/increasinglyadequate.key"
log style combined
root "/htdocs/increasinglyadequate"
# The next five lines are for acme-client:
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
directory no auto index
}
}
Once again, restart httpd
with rcctl restart httpd.
Additionally,
make sure that your firewall and router allow traffic on to reach port
443 on the machine running httpd
.
P.S. The hsts
line should force browsers to automatically switch from
http to https. As such, you may be able to omit the entire section that
exists solely for redirecting http traffic, but I read that it may be
best to keep both since some clients (including webcrawlers) don't use
this HSTS feature.
Make sure your certificates get renewed:
The manual page for acme-client
has this simple advice: "A daily
cron(8) job can renew the certificates: acme-client example.com &&
rcctl reload httpd
".
That's fine, but since this command is going to be done just once a day,
I might as well let OpenBSD's daily
system maintenance script take
care of it. That way I can read about it with the usual daily system
maintenance mail. Plus this saves me from having to fuck around with
cron's syntax and from getting a separate email just about this
command.
crontab calls /etc/daily
at 01:30 every day. One of /etc/daily
's
commands is to run the script /etc/daily.local
if it exists. So I
created an /etc/daily.local
script that looks a bit like this:
#!/bin/sh
acme-client -v www.increasinglyadequate.com
rcctl reload httpd
Conclusion
Please bear in mind that I have little idea of what I'm doing, that I haven't thoroughly tested these instructions, and that even if they work now, they may not work next week. If you actually want to know how all of this works, get yourself a copy of Lucas's book.