I have already described how I use acme.sh to obtain SSL certificates from Let’s Encrypt. Today, I’m going to show you how I use anvil to copy those certificates from the original location to another directory, which is then used for rsync by another jail.
Throughout this blog post, it is assumed that the cert-shifter will be run as the anvil user. Please adjust to suit your choices.
Why shift certificates?
As part of my certificate distribution solution, I want to copy the certificates to a webserver. I do not want to push them, I want the webserver to pull them. However, allowing the webserver to have access to the acme.sh jail is not a wise decision. Nobody connects to that jail. Ever. It has the keys.
Instead, I copy the certificates, which by definition are public, and not the keys, to another directory. This directory will then be nullfs mounted read-only into another jail (I call that the rsync jail). The copy process will connect to the rsync jail. Should that connection ever be exploited, all they have is public certificates.
Installation
I installed anvil from a FreeBSD package:
pkg install anvil
The anvil package installs the anvil user:
$ grep anvil /etc/passwd anvil:*:217:217:anvil certificate dropper:/var/empty:/usr/sbin/nologin
The cert-shifter script supplied by the anvil package will run as that user.
Configuration
The cert-shifter configuration file is /usr/local/etc/anvil/cert-shifter.conf. Here are the default values. They are closely connected to the default values for the acme.sh port.
CERT_SRC="/var/db/acme/certs" CERT_DST_ROOT="/var/db/certs-for-rsync" CERT_DST_CERTS="${CERT_DST_ROOT}/certs" TMP="${CERT_DST_ROOT}/tmp"
Let’s look at each one of those values.
- CERT_SRC – the location of the certificates. The anvil user must be able to read everything in that directory. More precisely, the user which runs the cert-shifter command must be able to read that directory. I configured that via:
sudo chgrp -R anvil /var/db/acme/certs
It looks like this on my box:
$ sudo ls -l /var/db/acme/certs total 561 drwxr-xr-x 2 acme anvil 9 Jul 4 20:38 certs.unixathome.org drwxr-xr-x 2 acme anvil 9 Jul 13 22:24 fruity-ext.int.unixathome.org drwxr-xr-x 2 acme anvil 9 Jul 13 22:21 fruity-int.int.unixathome.org drwxr-xr-x 2 acme anvil 9 Jul 14 14:03 lists.bsdcan.org drwxr-xr-x 2 acme anvil 9 Jul 14 14:04 lists.freebsddiary.org drwxr-xr-x 2 acme anvil 9 Jul 14 14:05 lists.freshports.org drwxr-xr-x 2 acme anvil 9 Jul 14 14:06 lists.freshsource.org drwxr-xr-x 2 acme anvil 9 Jul 14 14:14 lists.langille.org drwxr-xr-x 2 acme anvil 9 Jul 14 14:15 lists.pgcon.org drwxr-x--- 2 acme anvil 9 Jul 15 13:13 lists.unixathome.org drwxr-xr-x 2 acme anvil 9 Jul 11 22:32 services.unixathome.org drwxr-xr-x 2 acme anvil 9 Jul 13 22:18 svn.int.unixathome.org drwxr-xr-x 2 acme anvil 9 Jun 30 21:10 www.unixathome.org
Let’s see what’s inside one of those directories:
[dan@certs:~] $ sudo ls -l /var/db/acme/certs/certs.unixathome.org total 119 -rw-r--r-- 1 acme anvil 1647 Jul 4 20:38 ca.cer -rw-r--r-- 1 acme anvil 1809 Jul 4 20:38 certs.unixathome.org.cer -rw-r--r-- 1 acme anvil 510 Jul 4 20:38 certs.unixathome.org.conf -rw-r--r-- 1 acme anvil 948 Jul 4 20:38 certs.unixathome.org.csr -rw-r--r-- 1 acme anvil 175 Jul 4 20:38 certs.unixathome.org.csr.conf -rw-r--r-- 1 acme anvil 1679 Jul 4 20:38 certs.unixathome.org.key -rw-r--r-- 1 acme anvil 3456 Jul 4 20:38 fullchain.cer
The above layout is how acme.sh produces certificates, and by design, that’s what anvil works with.
- CERT_DST_ROOT – This is the top level directory where cert-shifter will copy the certificates. It must be writable by the anvil user.
- CERT_DST_CERTS – This is the subdirectory of the above and it is where the certificates will be copied.
- TMP – a temp directory used by cert-shifter when copying the certificates. It should be on the same filesystem as CERT_DST_CERTS because mv will be used during the copy process.
The default values should just work if you are using the acme.sh port. All you need to do is the chown described above.
The crontab
Here is the crontab I set for the anvil user so the certificates are collected.
$ sudo crontab -u anvil -l # mail any output to `dan', no matter whose crontab this is MAILTO=dan@example.org 19 20 * * * /usr/local/bin/cert-shifter
The script does a little bit of logging. Here is what it looked like when it last ran here:
Jul 15 20:19:00 certs cert-shifter: starting /usr/local/bin/cert-shifter Jul 15 20:19:00 certs cert-shifter: collecting from /var/db/acme/certs/lists.unixathome.org Jul 15 20:19:00 certs cert-shifter: stopping /usr/local/bin/cert-shifter
As you can see, only one certificate was copied over. What’s in that directory? This:
-rw-r--r-- 1 anvil anvil 1647 Jul 15 13:13 ca.cer -rw-r--r-- 1 anvil anvil 1809 Jul 15 13:13 lists.unixathome.org.cer -rw-r--r-- 1 anvil anvil 3456 Jul 15 13:13 lists.unixathome.org.fullchain.cer
NOTE: this differs from what acme.sh creates. The cert-shifter script renames all fullchain.cer files to be prefixed by the domain name. I so this because I store all my certificates in /usr/local/etc/ssl.
I run this script once a day, the same frequency as my acme.sh script, but not at the same time.