# Configuring Acme-client To enable TLS, you will want a certificate signed by a trusted certificate authority (CA). In this guide, we'll use OpenBSD's [acme-client](https://man.openbsd.org/acme-client) with Let's Encrypt. ## Overview TLS (Transport Layer Security) aka SSL (Secure Sockets Layer) is the encryption security measure that enables browsers to recognize a website as "secure". In modern browsers the SSL information can be accessed by clicking the padlock icon in the address bar. SSL certificates are obtained from CAs (Certificate Authorities). Currently, the only free CAs are [Lets Encrypt](https://letsencrypt.org/getting-started/), [Buypass](https://www.buypass.com/ssl/products/acme) and [ZeroSSL](https://zerossl.com/). You can request an SSL cert for your web domain using an Automatic Certificate Management Environment (ACME) client such as OpenBSD's Acme-client, which we will configure in this article. ### Before You Begin You will first need to properly configure and start [openhttpd](/openhttpd/configure). You will also need properly functioning [DNS records](/dns/overview) for your hostname (`username.example.com`). If you are using a training vps, it is likely that DNS records for your hostname have already been set up for you. You can and should test the two configurations using [host](/host/usage) and [netcat](/netcat/http). Note: You must have a server block in [/etc/httpd.conf](https://man.openbsd.org/httpd.conf) listening on port 80. Do not delete this block or else acme-client will not work. ## Configuration First, copy the [acme-client.conf](https://man.openbsd.org/acme-client.conf) template: $ doas cp /etc/examples/acme-client.conf /etc/acme-client.conf We'll open up ##STARTCODEBLOCK## /etc/acme-client.conf ##ENDCODEBLOCK## and analyze the meaning of each block: ### Authority blocks authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" } This defines the Certificate Authority [letsencrypt](https://letsencrypt.org/). It provides the API URL and the location of the account key. **Note**: Let's Encrypt [rate-limits](https://letsencrypt.org/docs/rate-limits/) the number of SSL certs you can request. If you encounter an error and are unable to request an SSL cert, please fix all errors before requesting again. If you request too many certs in a short time, your domain will get blacklisted for a few hours or days. To avoid issues, use letsencrypt-staging first and make sure you get success with that before using letsencrypt. Although we are using Let's Encrypt for this tutorial, it is important to note that having the majority of servers depend upon a single provider is dangerous. For this reason, it would be beneficial to someday have the community run its own Certificate Authority to avoid censorship of domains or other security issues. authority letsencrypt-staging { api url "https://acme-staging-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-staging-privkey.pem" } letsencrypt-staging is a staging server which you can use to practice requesting fake certificates. The rate limits for the staging server are less strict, so you should practice first with this CA. To both of these blocks, we will want to add our contact email, so we add ##STARTCODEBLOCK## contact "mailto:me@example.com" ##ENDCODEBLOCK## inside both blocks. (make sure to have the `mailto:`): authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" contact "mailto:me@example.com" } authority letsencrypt-staging { api url "https://acme-staging-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-staging-privkey.pem" contact "mailto:me@example.com" } Next, the default [acme-client.conf](https://man.openbsd.org/acme-client.conf) defines two more authorities: 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" } These two blocks are the same as for letsencrypt, but with the alternative provider [buypass](https://buypass.com/). Make sure to replace the contact email with your own email. ### Domain Block domain example.com { alternative names { secure.example.com } domain key "/etc/ssl/private/example.com.key" domain full chain certificate "/etc/ssl/example.com.crt" sign with letsencrypt } First, replace every appearance of `example.com` with your own domain. Each SSL cert is valid only for a **common name** and a set of **alternative names** that are provided on the certificate. For example, an SSL certificate might have the common name `example.com` and the alternative names `www.example.com` and `mail.example.com`. You can safely skip this by commenting out this line (see warning below) If you use too many alternative names, an acme-client certificate request has a higher chance of failure. So, we recommend keeping the number of alternative names to under 5. **Warning**: Having the `alternative names` directive with nothing inside will cause errors. For example, the below will cause errors: alternative names { } If you don't need any alternative names, you should comment this line out by putting a # at the beginning of the line, like so: # alternative names { } **Note**: If you add an alternative name to the conf file, but the cert already exists, you must remove the old public cert first before requesting a new one. Otherwise, you will get `unknown SAN error` -- acme-client will complain there is an unknown **Subject Alternative Name**. Next, the `domain key` and `domain full chain certificate` tell acme-client where to put the private key and certificate: domain key "/etc/ssl/private/example.com.key" domain full chain certificate "/etc/ssl/example.com.crt" The public key goes inside the folder `/etc/ssl` and the private key goes inside `/etc/ssl/private`. The line `sign with letsencrypt` line tells Acme-client which Certificate Authority (which you defined in the Authority Blocks) to use. For testing purposes, you may want to change it to `letsencrypt-staging`. You can also consider using `buypass` or `buypass-test`. **Note**: staging or testing certificates are not recognized by most browsers and will be rejected as an invalid certificate. After you finish testing with a staging certificate, change this line back to an official authority (such as `sign with letsencrypt`). ## Requesting Certificates After you have finished configuring the conf file, we can request certificates: $ doas acme-client -Fv example.com If there are no errors, you should see something similar to the following output: $ doas acme-client -Fv example.com acme-client: /etc/acme/letsencrypt-privkey.pem: generated RSA account key acme-client: /etc/ssl/private/example.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: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz-v3/11133258838 acme-client: challenge, token: uWHZmqhx6NEpcv25LEvodMAeymB1guTFVtyktVzkJgs, uri: https://acme-v02.api.letsencrypt.org/acme/chall-v3/11133258838/_UI3-A, status: 0 acme-client: /var/www/acme/uWHZmqhx6NEpcv25LEvodMAeymB1guTFVtyktVzkJgs: created acme-client: https://acme-v02.api.letsencrypt.org/acme/chall-v3/11133258838/_UI3-A: challenge acme-client: order.status 0 acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz-v3/11133258838 acme-client: challenge, token: uWHZmqhx6NEpcv25LEvodMAeymB1guTFVtyktVzkJgs, uri: https://acme-v02.api.letsencrypt.org/acme/chall-v3/11133258838/_UI3-A, status: 2 acme-client: order.status 1 acme-client: https://acme-v02.api.letsencrypt.org/acme/finalize/113861127/8112730231: certificate acme-client: order.status 3 acme-client: https://acme-v02.api.letsencrypt.org/acme/cert/03f7fd846802cb0689c2bbd7b6f5e89eb66b: certificate acme-client: /etc/ssl/example.com.crt: created The last line says that the public certificate was generated. If you see that, it's a success! You now have two certificates, the public key inside `/etc/ssl/example.com.crt`, and the private key inside `/etc/ssl/private/example.com.key`: $ doas ls -l /etc/ssl/example.com.crt /etc/ssl/private/example.com.key -r--r--r-- 1 root wheel 4797 Feb 25 02:11 /etc/ssl/example.com.crt -r-------- 1 root wheel 3272 Feb 25 02:10 /etc/ssl/private/example.com.key ## Automation Let's Encrypt TLS certs expire after 90 days, while Buypass certs expire after 180. For both, you must remember to request the TLS cert or TLS will stop working. To avoid forgetting, we can automate the request process using [crontab](/crontab/edit). $ doas crontab -e Add this line at the bottom: ~ ~ * * * acme-client example.com >> /var/log/acme-client.log 2>&1 This cronjob will check the certificate once each day, at a random time of day, to see if it needs to be renewed. If it does, it will renew the cert. ## Troubleshooting If acme-client fails, there are several possible causes: ### Domain Not Listed If you add a new alternative name inside your domain block in [/etc/acme-client.conf](https://man.openbsd.org/acme-client.conf), you will see this error: acme-client: /etc/ssl/example.com.crt: domain not listed: new.example.com Here, `new.example.com` was a new alternative name you added. The solution is to move your old public cert and private key to a new location (to back it up). Then, request the cert again. $ doas mv /etc/ssl/example.com.crt /etc/ssl/example.com.crt.bak $ doas mv /etc/ssl/private/example.com.key /etc/ssl/private/example.com.key.bak Then request the cert again: $ doas acme-client -Fv example.com ### Missing Domain Records It's possible that your domain records are missing. Run this command, replacing `example.com` with your real hostname: $ host example.com You should see one or two records like the following: example.com has address 93.184.216.34 example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946 If you have missing records, you will see this response: Host example.com not found: 3(NXDOMAIN) You will either need to speak with your DNS provider or troubleshoot [nsd](/nsd/troubleshoot). If DNS records are missing or incorrect, fix these first. The IPv4 and IPv6 address must exactly match the IPs that [OpenHTTPd](/openhttpd/configure) is listening on. If they do not match, you must fix this. **Note**: You **cannot** request a domain you don't own# The domain must point to an IP you own. Lastly, if your DNS record includes an IPv6 address, make sure your web server is listening on IPv6. If the DNS record contains an IPv4 address, make sure your web server is listening on IPv4. ### OpenHTTPd Misconfigured If [openhttpd](/openhttpd/configure) is not configured and running properly, acme-client won't work. (acme-client uses the "http-01" challenge. A file is created with a special message in `/var/www/acme/`, and the certificate authority requests that file using the URL `http://example.com/.well-known/acme-challenge/*`. ) To test if your web server is running properly, use [telnet](/telnet/http) (replacing `example.com` with your domain) on another computer. $ telnet example.com 80 GET /index.html HTTP/1.1 Host: example.com If you do not get the correct response, double check your [openhttpd configuration](/openhttpd/configure). **Note**: Although a web browser can also be used for testing, many web browsers automatically forward all port 80 requests to port 443. As a result, your web browser will only see what is listening on port 443, whereas the certificate authority tests port 80 only. ### Incorrect File Permissions Double check `/var/www` and `/var/www/acme` for correct file permissions: $ ls -ld /var/www /var/www/acme drwxr-xr-x 10 root daemon 512 Oct 5 07:47 /var/www drwxr-xr-x 2 root daemon 512 Oct 5 07:47 /var/www/acme See Also: [Configure OpenHTTPd](/openhttpd/configure) [Telnet HTTP](/telnet/http) [OpenSSL HTTP](/openssl/http)