Handling TLS certs on Fedora IoT with acme.sh
If you're running Fedora IoT (or Fedora CoreOS outside of Kubenetes) then you'll need to run your ACME client within a container if you want free auto-updating TLS certificates.
One option for doing this is acme.sh. It wasn't obvious to me how this was expected to work, hence these notes. My primary resources were:
Note that I perform all of the following as the root user (see Shortcomings of Rootless Podman), because one of my uses for the certs is nginx bound to port 443.
Firstly we'll need to create a volume that acme.sh can write to and any certificate-consuming applications can read from:
podman volume create acme-certs
…then we can run acme.sh and point it at our volume (the :z
option when specifying the volume applies an SELinux label denoting shared content):
# Only needed for the first run:
podman run --rm -it -v acme-certs:/acme.sh:z \
docker.io/neilpang/acme.sh --register-account -m inbox@example.com
Once the initial registration is done you should be able to issue certs:
podman run --rm -it -v acme-certs:/acme.sh:z \
-e GANDI_LIVEDNS_KEY=api-key-gibberish \
docker.io/neilpang/acme.sh \
--issue --dns dns_gandi_livedns -d host.example.com
In this example I'm using Gandi LiveDNS for verification, the acme.sh DNS API supports many providers so probably yours is in there.
Assuming the certificate issue operation succeeds you will see output like this:
Your cert is in: /acme.sh/host.example.com/host.example.com.cer
Your cert key is in: /acme.sh/host.example.com/host.example.com.key
The intermediate CA cert is in: /acme.sh/host.example.com/ca.cer
And the full chain certs is there: /acme.sh/host.example.com/fullchain.cer
Take note of the names/paths as you'll need them when configuring whichever consuming applications you'll use.
Now all that's left is to automate certificate renewal. I've done this with a systemd timer because everything in Fedora IoT is unit files already, but it works just as well via cron.
cat <<EOF > /etc/systemd/system/acme-certs.service
[Unit]
Description=Renew TLS certificates via acme.sh
After=network.target
[Service]
Type=simple
ExecStart=podman run --rm -it -v acme-certs:/acme.sh:z \
-e GANDI_LIVEDNS_KEY=api-key-gibberish \
docker.io/neilpang/acme.sh --cron
EOF
cat <<EOF > /etc/systemd/system/acme-certs.timer
[Unit]
Description=Check and renew TLS certificates daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
# test manually
systemctl start acme-certs.service
journalctl --unit acme-certs.service
# enable the timer
systemctl enable acme-certs.timer
systemctl start acme-certs.timer
systemctl status acme-certs.timer acme-certs.service