Automating Certificate Renewal with PowerDNS & Step CA

I’ve been looking for a way to get an Internal CA Certificate onto my TrueNAS WebUI. I also had the idea that this would allow me to setup and use the inbuilt S3 service from TrueNAS however I have now learned that this is being depricated. Proxmox makes this quite easy and integrates seamlessly with Step CA using ACME. Unfortunaately the builtin ACME client on TrueNAS is a bit limited and using the default http-01 challenge for ACME is not possible. ACME.sh provides quite a few alternatives now, including DNS alias challenge handling (which I may still look into) but for now, I have landed on the following solition/workaround/hack:

  • Switch to accessing the TrueNAS WebUI on subdomain of my internal DNS that is authoritative on PowerDNS. eg TrueNAS hostname is truenas.mgmt.example.com, WebUI now hosted on truenas.int.example.com.
  • Create a CNAME record in PowerDNS truenas.int.example.com IN CNAME truenas.mgmt.example.com.
  • Use ACME.sh on my automation server to issue / renew the certificate and then deploy it to TrueNAS via the TrueNAS API.

Like an idiot, I didn’t look properly into whether or not ACME.sh had a pre-built DNS-01 challenge plugin for PowerDNS and went a little way down the path of developing my own. It does - “dns_pdns”. Code is here for reference

Putting it all together, you need only to set the following environment variables and issue the ACME.sh command and then you’re away. Some notes;

  • --insecure is used to allow the deploy-hook to talk to TrueNAS API over HTTP. (I think)
  • --dns dns_pdns tells ACME.sh to use the PowerDNS DNS-01 challenge authenticator.
  • --server ... tells ACME.sh to use my Internal Step CA, not the default LetsEncrypt servers.
  • --dnssleep 120 is IMPORTANT. By default, ACME.sh wants to use Cloudflares DNS over HTTP (DoH) client to verify the DNS record created by the PowerDNS plugin. Obviously, for this internal only DNS zones, this is not going to work. According to https://github.com/acmesh-official/ACME.sh/issues/2587, the only was to prevent this behaviour is to set the DNS Sleep to 120 at which point ACME.sh will fall back to regular DNS name resolution.
  • --days 1 added to align with the certificate lifetime configured on Step CA for ACME certificates see update below for info.
export DEPLOY_TRUENAS_HOSTNAME=truenas.mgmt.example.com # this the actual hostname of the TrueNAS appliance onto which you are reploying the new CA.
export DEPLOY_TRUENAS_APIKEY="your secret API key generated on TrueNAS WebUI"
export PDNS_ServerId="localhost"
export PDNS_Token="your secret PowerDNS API Token"
export PDNS_Ttl=60
export PDNS_Url="http://pdns.mgmt.etse.me:8081"

acme.sh --insecure --issue -d truenas.int.example.com --dns dns_pdns --deploy-hook truenas --server https://step-ca.mgmt.etse.me/acme/acme/directory --dnssleep 120

Don’t forget to import your Internal CA’s Root and Intermediate Certificate into TrueNAS manually.

Update +24 hours

Renewal didn’t work :( Here is the snippet from the acme.sh.log.

[Thu Sep 12 12:06:02 AWST 2024] di='/home/jacko/.acme.sh/freenas2.int.etse.me_ecc/'
[Thu Sep 12 12:06:02 AWST 2024] d='freenas2.int.etse.me_ecc'
[Thu Sep 12 12:06:02 AWST 2024] _renewServer
[Thu Sep 12 12:06:02 AWST 2024] Using config home:/home/jacko/.acme.sh
[Thu Sep 12 12:06:02 AWST 2024] ACME_DIRECTORY='https://acme.zerossl.com/v2/DV90'
[Thu Sep 12 12:06:02 AWST 2024] _ACME_SERVER_HOST='acme.zerossl.com'
[Thu Sep 12 12:06:02 AWST 2024] _ACME_SERVER_PATH='v2/DV90'
[Thu Sep 12 12:06:02 AWST 2024] DOMAIN_PATH='/home/jacko/.acme.sh/freenas2.int.etse.me_ecc'
[Thu Sep 12 12:06:02 AWST 2024] Renew: 'freenas2.int.etse.me'
[Thu Sep 12 12:06:02 AWST 2024] Le_API='https://step-ca.mgmt.etse.me/acme/acme/directory'
[Thu Sep 12 12:06:02 AWST 2024] Renew to Le_API=https://step-ca.mgmt.etse.me/acme/acme/directory
[Thu Sep 12 12:06:02 AWST 2024] initpath again.
[Thu Sep 12 12:06:02 AWST 2024] Using config home:/home/jacko/.acme.sh
[Thu Sep 12 12:06:02 AWST 2024] ACME_DIRECTORY='https://step-ca.mgmt.etse.me/acme/acme/directory'
[Thu Sep 12 12:06:02 AWST 2024] _ACME_SERVER_HOST='step-ca.mgmt.etse.me'
[Thu Sep 12 12:06:02 AWST 2024] _ACME_SERVER_PATH='acme/acme/directory'
[Thu Sep 12 12:06:02 AWST 2024] Skip, Next renewal time is: 2024-11-08T11:41:19Z
[Thu Sep 12 12:06:02 AWST 2024] Add '--force' to force to renew.
[Thu Sep 12 12:06:02 AWST 2024] Return code: 2
[Thu Sep 12 12:06:02 AWST 2024] Skipped freenas2.int.etse.me_ecc

Looks to me like Skip, Next renewal time is: 2024-11-08T11:41:19Z is indicating that ACME.sh thinks the cerfificate doesn’t need to be renewed… for ages.

Let’s have a look at the config: cat .acme.sh/freenas2.int.etse.me_ecc/freenas2.int.etse.me.conf

Le_CertCreateTime='1725968479'
Le_CertCreateTimeStr='2024-09-10T11:41:19Z'
Le_NextRenewTimeStr='2024-11-08T11:41:19Z'
Le_NextRenewTime='1731066079'

Looking at: acme.sh --help show that there is a cli switch --days:

  --days <ndays>                    Specifies the days to renew the cert when using '--issue' command. The default value is 60 days.

Time to re-issue our certificate, this time adding --days 1.


© 2021. All rights reserved.

Powered by Hydejack v9.1.6