Summary
OpenBSD's acme-client
acme-client
is the default Automatic Certificate Management Environment (ACME) client on OpenBSD, installed at the same time when the OS is.
To configure acme-client.conf is easy and enables us to get certificates with Let's Encrypt or another.
The basic usage is:
# # configure # nvim /etc/acme-client.conf # # get or update (for renewal) certificate # acme-client <domain> # # revoke certificate # acme-client -r <domain>
How about CertBot
Besides, we know there is another option. Yes, CertBot by EFF (Electronic Frontier Foundation), a very popular client. Although we can get it via pkg_add certbot
, there was sometimes a problem around permissions on OpenBSD when renewing the certificate.
Environment
- OS: OpenBSD 7.0
- ACME client: OpenBSD acme-client
- Certificate Authority: Let's Encrypt
- Web server: OpenBSD httpd
How to use
Configure acme-client.conf
Well, let's see the conf file which is officially provided by OpenBSD.
$ cat /etc/examples/acme-client.conf
There are clear examples:
# # $OpenBSD: acme-client.conf,v 1.4 2020/09/17 09:13:06 florian Exp $ # authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" } authority letsencrypt-staging { api url "https://acme-staging-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-staging-privkey.pem" } authority buypass { api url "https://api.buypass.com/acme/directory" account key "/etc/acme/buypass-privkey.pem" contact "mailto:me@example.com" } authority buypass-test { api url "https://api.test4.buypass.no/acme/directory" account key "/etc/acme/buypass-test-privkey.pem" contact "mailto:me@example.com" } 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" sign with letsencrypt }
Let's copy it:
$ # well, you can see there is none by default $ ls /etc/acme-client* $ doas cp -p /etc/examples/acme-client.conf /etc/
Then edit:
$ doas nvim /etc/acme-client.conf
like below:
- 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" - sign with letsencrypt - } + domain your.domain { + #alternative names { secure.example.com } + domain key "/etc/ssl/private/your.domain.key" + domain full chain certificate "/etc/ssl/your.domain.fullchain.pem" + sign with letsencrypt + }
OK. your.domain
is now ready for "sign with letsencrypt".
Start OpenBSD httpd as web server
Be sure to httpd
listens on egress:
server "your.domain" { listen on egress port 80 # ... }
Well, you may also find the example of httpd.conf
in /etc/examples/
.
Of course, it has to be started:
$ # case disabled: $ doas rcctl start -f httpd $ # case enabled: $ doas rcctl restart httpd
Request certificate
Just run the single command below, and you will get the sweet certificate:
$ doas acme-client -v your.domain
Well, -v
is for verbose and optional.
There are other methods. How to update it:
$ # the same command above $ doas acme-client your.domain
Moreover, how to revoke it:
$ doas acme-client -r your.domain
Notes
Periodical update is required
Remember Let's Encrypt certificate lasts only 90 days. You have to update it periodically.
One way to do so is to use cron
.
Let's Encrypt has their Rate Limits
Be careful about that Let's Encrypt has several rate limits.
For example,
- "Certificates per Registered Domain". It is 50 per week.
Also,
- "Duplicate Certificate". You can renew the same certificate up to 4 times (totally 5 times) per week.
Here is their documentation on it.
Side story: The history and the changelog
acme-client first appeared in 6.1.
In 6.5 or below (to 6.1), you had to use more options to get certificate:
$ doas acme-client -ADv your.domain
In 6.6, acme-client became to able to communicate with the v02 Let's Encrypt API. Therefore, -A
and -D
have not been necessary any more. Nice improvement.
Troubleshooting: When the challenge failed
You might sometimes see such errors and, sadly, fail:
acme-client: /etc/ssl/private/your.domain: generated RSA domain key acme-client: https://acme-v02.api.letsencrypt.org/directory: directories acme-client: acme-v02.api.letsencrypt.org: DNS: xxx.xxx.xxx.xxx acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz-v3/84303399960 acme-client: challenge, token: (...), uri: https://acme-v02.api.letsencrypt.org/acme/chall-v3/84303399960/MQGXJQ, status: 0 acme-client: /var/www/acme/(...): created acme-client: https://acme-v02.api.letsencrypt.org/acme/chall-v3/84303399960/MQGXJQ: challenge acme-client: order.status 0 acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz-v3/84303399960 acme-client: challenge, token: (...), uri: https://acme-v02.api.letsencrypt.org/acme/chall-v3/84303399960/MQGXJQ, status: -1 acme-client: order.status -1 acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz-v3/84303399960 acme-client: Invalid response from http://your.domain/.well-known/acme-challenge/(...) [2606:4700:3031::6815:3ae6]: \\"\\u003c!DOCTYPE html\\u003e\\\\n\\u003chtml\\u003e\\\\n\\u003chead\\u003e\\\\n\\u003cmeta charset=\\\\\\"utf-8\\\\\\"\\u003e\\\\n\\u003ctitle\\u003e404 Not Found\\u003c/title\\u003e\\\\n\\u003cstyle type=\\\\\\"text/css\\\\\\"\\u003e\\u003c!--\\\\nbody { background-\\" acme-client: bad exit: netproc(87417): 1
It is 404 aka page-is-missing error. In other cases, it may 403 aka access-is-denied.
They are because your web server returned "bad response". Let's mitigate it by properly mapping response challenges:
server "your.domain" { listen on egress port 80 + # acme-client + location "/.well-known/acme-challenge/*" { + root "/acme" + request strip 2 + } # ... }
Then restart httpd:
$ # case disabled: $ doas rcctl -f restart httpd $ # case enabled: $ doas rcctl restart httpd
It must be fixed.
# acme-client -v your.domain
I hope you meet success like this:
acme-client: https://acme-v02.api.letsencrypt.org/directory: directories acme-client: acme-v02.api.letsencrypt.org: DNS: xxx.xxx.xxx.xxx acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz-v3/84304202350 acme-client: challenge, token: (...), uri: https://acme-v02.api.letsencrypt.org/acme/chall-v3/84304202350/NVmlRA, status: 0 acme-client: /var/www/acme/(...): created acme-client: https://acme-v02.api.letsencrypt.org/acme/chall-v3/84304202350/NVmlRA: challenge acme-client: order.status 0 acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz-v3/84304202350 acme-client: challenge, token: (...), uri: https://acme-v02.api.letsencrypt.org/acme/chall-v3/84304202350/NVmlRA, status: 2 acme-client: order.status 1 acme-client: https://acme-v02.api.letsencrypt.org/acme/finalize/58118741/68735636600: certificate acme-client: order.status 3 acme-client: https://acme-v02.api.letsencrypt.org/acme/cert/(...): certificate acme-client: /etc/ssl/your.domain: created
Conclusion
You have got the Let's Encrypt certificate! All what to do is add it to httpd.conf
.
server "your.domain" { listen on * port 80 # acme-client location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } + block return 301 "https://$SERVER_NAME$REQUEST_URI" } + server "your.domain" { + listen on * tls port 443 + tls { + certificate "/etc/ssl/your.domain.fullchain.pem" + key "/etc/ssl/private/your.domain.key" + } + # acme-client + location "/.well-known/acme-challenge/*" { + root "/acme" + request strip 2 + } + # ... + }
Here, I replaced egress
with *
to apply also to lo
. It is optional.
Your TLS connection based on encryption aka HTTPS is completed.
Happy connecting securelyπ
Top comments (0)