I hate spending money on things I don’t absolutely have to. Maybe that’s why I like opensource. I also like encryption. Therefore, I really like acme-client on OpenBSD. They have such an easy setup for generating your own SSL certs for use by a web server. In the article, I will walk through not only the basic configs that I’ve used. I am also writing a follow-up article that will illustrate how to create a quick ansible playbook that will do all the heavy lifting for you.

There are plenty of articles out there about configuring acme-client for OpenBSD. I barely had to change anything to get the config working for me. One thing to note though, if you decide to use a staging authority, and if you get a weird 403 error when you try to pull your certs, you may have to remove /etc/acme/letsencrypt-privkey.pem. I had that issue myself while I was testing things out.

/etc/acme-client.conf
authority letsencrypt {
  api url "https://acme-v01.api.letsencrypt.org/directory"
  account key "/etc/acme/letsencrypt-privkey.pem"
}
domain www.example.com {
    alternative names { example.com }
    domain key "/etc/ssl/private/www.example.com.key"
    domain certificate "/etc/ssl/www.example.com.crt"
    domain full chain certificate "/etc/ssl/www.example.com.pem"
    sign with letsencrypt
}

Next up is the httpd.conf. Again, not much to the configuration. OpenBSD does a pretty good job about making sure that people have to work hard to configure a service insecurily. Running this config through ssllabs yielded an A+ rating, and I consider myself an amature web admin.

/etc/httpd.conf
chroot "/var/www"
ext_addr="*"

prefork 2

server "www.example.com" {
    listen on $ext_addr tls port 443
    alias "example.com"
    root "/htdocs/public"
    tls {
        certificate "/etc/ssl/www.example.com.pem"
        key "/etc/ssl/private/www.example.com.key"
        ticket lifetime default
        ciphers "secure"
    }

    hsts max-age 16000000
    hsts preload
    hsts subdomains

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

}

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

}

The last step is to initialize the certs by running the command below:

acme-client -ADv www.example.com


The deep dive

Just to keep things a little more secure, I did enable hsts with these lines:

hsts max-age 16000000
hsts preload
hsts subdomains

Ciphers are limited to just the most secure available with:

ciphers "secure"

These little blocks are used by acme to verify that you actually own the domain you are requesting certs for

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

And that’s pretty much it. Read about how to do this whole setup using ansible here.

Has been tested on OpenBSD 6.4