Introduction Link to heading
This is just my guide on obtaining a TLS certificate via acme.sh on a FreeBSD system.
The write up is using linode to let us perform a DNS challenge (a DNS is required if you want a wildcard certificate) and using nginx as the webserver.
The guide assumes that you have set up DNS records, obtained API keys (if required), and have set up your web server of choice.
We won’t go into how to do any of these things because the steps will differ depending on your set up.
This guide will only focus on installing acme.sh
onto FreeBSD, obtaining a certificate, setting up automatic renewal, and letting acme reload the nginx webserver whenever the certificate has been renewed.
Software Link to heading
FreeBSD 14.0-RELEASE-p6 using the latest
packages:
- acme.sh 3.0.7_1
- sudo 1.9.15p5_4
Installing acme.sh Link to heading
Obtaining a certificate as the acme user Link to heading
Running acme.sh
as the root user will lead to some strange errors.
For example, acme.sh was not able to find the required files to let me do a DNS challenge:
[root@jail ~]# acme.sh --issue --test --dns dns_linode_v4 -d brendans-bits.com -d *.brendans-bits.com --dnssleep 900
<snip>
[Fri Jul 5 21:54:26 AEST 2024] Can not find dns api hook for: dns_linode_v4
<snip>
The solution was to switch to the installed acme user:
[root@jail ~]# su - acme
$ whoami
acme
$ pwd
/var/db/acme
Running these commands as the acme user was successful.
$ acme.sh --set-default-ca --server letsencrypt
[Sat Jul 6 16:15:04 AEST 2024] Changed default CA to: https://acme-v02.api.letsencrypt.org/directory
$ acme.sh --register-account --server letsencrypt -m <your@email.com>>
[Sat Jul 6 16:16:02 AEST 2024] Create account key ok.
[Sat Jul 6 16:16:02 AEST 2024] Registering account: https://acme-v02.api.letsencrypt.org/directory
[Sat Jul 6 16:16:03 AEST 2024] Registered
<snip>
$ acme.sh --issue --dns dns_linode_v4 -d brendans-bits.com -d *.brendans-bits.com --dnssleep 900
<snip>
[Sat Jul 6 16:41:53 AEST 2024] Cert success.
<snip>
[Sat Jul 6 16:41:53 AEST 2024] Your cert is in: /var/db/acme/certs/brendans-bits.com_ecc/brendans-bits.com.cer
[Sat Jul 6 16:41:53 AEST 2024] Your cert key is in: /var/db/acme/certs/brendans-bits.com_ecc/brendans-bits.com.key
[Sat Jul 6 16:41:53 AEST 2024] The intermediate CA cert is in: /var/db/acme/certs/brendans-bits.com_ecc/ca.cer
[Sat Jul 6 16:41:53 AEST 2024] And the full chain certs is there: /var/db/acme/certs/brendans-bits.com_ecc/fullchain.cer
Running acme.sh --list
shows that we have successfully retrieved a certificate.
$ acme.sh --list
Main_Domain KeyLength SAN_Domains CA Created Renew
brendans-bits.com "ec-256" *.brendans-bits.com LetsEncrypt.org 2024-07-06T06:41:53Z 2024-09-03T06:41:53Z
We can also see that acme.sh
has also saved these details into the certificate configuration file:
$ cat certs/brendans-bits.com_ecc/brendans-bits.com.conf
Le_Domain='brendans-bits.com'
Le_Alt='*.brendans-bits.com'
Le_Webroot='dns_linode_v4'
<snip>
Le_DNSSleep='900'
<snip>
Le_CertCreateTime='1720248113'
Le_CertCreateTimeStr='2024-07-06T06:41:53Z'
Le_NextRenewTimeStr='2024-09-03T06:41:53Z'
Le_NextRenewTime='1725345713'
<snip>
Install the certificates Link to heading
We need to create a folder to hold the installed certificates:
[root@jail ~]# mkdir -p /usr/local/etc/ssl/brendans-bits.com
[root@jail ~]# chown acme:acme /usr/local/etc/ssl/brendans-bits.com
[root@jail ~]# su - acme
$ acme.sh --install-cert -d brendans-bits.com --key-file /usr/local/etc/ssl/brendans-bits.com/key.pem --fullchain-file /usr/local/etc/ssl/brendans-bits.com/cert.pem
[Sat Jul 6 16:43:38 AEST 2024] The domain 'brendans-bits.com' seems to have a ECC cert already, lets use ecc cert.
[Sat Jul 6 16:43:38 AEST 2024] Installing key to: /usr/local/etc/ssl/brendans-bits.com/key.pem
[Sat Jul 6 16:43:38 AEST 2024] Installing full chain to: /usr/local/etc/ssl/brendans-bits.com/cert.pem
(I won’t go into detail about how you load the certificates onto your web server, as this process differs for each server.)
Allow acme.sh to reload the webserver Link to heading
We’re going to allow the acme user to use sudo
to reload the web server whenever the certificate is renewed.
To do this, we add acme ALL = NOPASSWD: /usr/sbin/service nginx reload
to the sudoers file.
(Change this command to suit which ever web server you are using)
[root@jail ~]# visudo
# Add the following line into the sudoers file
acme ALL = NOPASSWD: /usr/sbin/service nginx reload
This line allows the acme user to run /usr/sbin/service nginx reload
without needing a password.
Then, we switch to the acme user and check that this works:
[root@nextcloud ~]# su - acme
$ whoami
acme
$ sudo service nginx reload
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
If this works, we can re-run the acme.sh --install-cert
with reloadcmd "sudo service nginx reload"
.
This will then add the reload command to the certificate configuration file.
$ cat certs/brendans-bits.com_ecc/brendans-bits.com.conf
<snip>
Le_ReloadCmd=''
<snip>
$ acme.sh --install-cert -d brendans-bits.com --key-file /usr/local/etc/ssl/brendans-bits.com/key.pem --fullchain-file /usr/local/etc/ssl/brendans-bits.com/cert.pem --reloadcmd "sudo service nginx reload"
[Sat Jul 6 16:44:40 AEST 2024] The domain 'brendans-bits.com' seems to have a ECC cert already, lets use ecc cert.
[Sat Jul 6 16:44:40 AEST 2024] Installing key to: /usr/local/etc/ssl/brendans-bits.com/key.pem
[Sat Jul 6 16:44:40 AEST 2024] Installing full chain to: /usr/local/etc/ssl/brendans-bits.com/cert.pem
[Sat Jul 6 16:44:40 AEST 2024] Run reload cmd: sudo service nginx reload
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
[Sat Jul 6 16:44:40 AEST 2024] Reload success
$ cat certs/brendans-bits.com_ecc/brendans-bits.com.conf
<snip>
Le_ReloadCmd='__ACME_BASE64__START_c3VkbyBzZXJ2aWNlIG5naW54IHJlbG9hZA==__ACME_BASE64__END_'
<snip>
You may be wondering what the '__ACME_BASE64_...
stuff is all about.
The reload command is saved as a base64 encoded string to deal with special characters.
Decoding the base64 string reveals the reload command:
$ echo c3VkbyBzZXJ2aWNlIG5naW54IHJlbG9hZA== | base64 --decode
sudo service nginx reload
Install a cronjob to renew the certificate Link to heading
We then let acme.sh
create a cronjob to let acme.sh
run every day and check whether the certificate needs to be renewed.
$ acme.sh --install-cronjob
$ crontab -l
26 4 * * * /usr/local/sbin/acme.sh --cron --home "/var/db/acme/.acme.sh" > /dev/null
Putting it all together Link to heading
So far, we’ve:
- successfully obtained a certificate,
- installed the certificate,
- allowed acme to reload the webserver, and
- set up a daily cronjob to check whether the certificate needs to be renewed.
Now it’s time to put it all together and see if all of the steps work:
$ acme.sh --cron --home "/var/db/acme/.acme.sh" --force
[Sat Jul 6 16:54:59 AEST 2024] ===Starting cron===
[Sat Jul 6 16:54:59 AEST 2024] Renew: 'brendans-bits.com'
<snip>
[Sat Jul 6 16:55:06 AEST 2024] Cert success.
<snip>
[Sat Jul 6 16:55:06 AEST 2024] Your cert is in: /var/db/acme/certs/brendans-bits.com_ecc/brendans-bits.com.cer
[Sat Jul 6 16:55:06 AEST 2024] Your cert key is in: /var/db/acme/certs/brendans-bits.com_ecc/brendans-bits.com.key
[Sat Jul 6 16:55:06 AEST 2024] The intermediate CA cert is in: /var/db/acme/certs/brendans-bits.com_ecc/ca.cer
[Sat Jul 6 16:55:06 AEST 2024] And the full chain certs is there: /var/db/acme/certs/brendans-bits.com_ecc/fullchain.cer
[Sat Jul 6 16:55:06 AEST 2024] Installing key to: /usr/local/etc/ssl/brendans-bits.com/key.pem
[Sat Jul 6 16:55:06 AEST 2024] Installing full chain to: /usr/local/etc/ssl/brendans-bits.com/cert.pem
[Sat Jul 6 16:55:06 AEST 2024] Run reload cmd: sudo service nginx reload
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
[Sat Jul 6 16:55:06 AEST 2024] Reload success
[Sat Jul 6 16:55:06 AEST 2024] ===End cron===
And we can check whether the web server is using the new certificates:
(my linux machine)$ openssl s_client -servername nextcloud.brendans-bits.com -connect nextcloud.brendans-bits.com:443 | openssl x509 -inform pem -text | grep 'Timestamp'
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = E5
verify return:1
depth=0 CN = brendans-bits.com
verify return:1
Signed Certificate Timestamp:
Timestamp : Jul 6 06:55:15.811 2024 GMT
Signed Certificate Timestamp:
Timestamp : Jul 6 06:55:15.818 2024 GMT
Since 06:55 GMT matches 16:55 AEST, we can confirm that forcibly running acme.sh --cron
:
- renewed the certificate,
- installed the certificate, and
- reloaded the web server.