cert-shifter: copying certificates from acme.sh to a fresh directory

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.


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.


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.


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

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.

Website Pin Facebook Twitter Myspace Friendfeed Technorati del.icio.us Digg Google StumbleUpon Premium Responsive

Leave a Comment

Scroll to Top