TrueNAS ACME using deSEC.io

Posted on July 5, 2024

TrueNAS can secure connections to its WebUI using ACME-provisioned TLS certificates. Because I don't want to expose TrueNAS to the internet, the best possible way to get a certificate is to use ACME DNS-01. Unfortunately, TrueNAS only supports Cloudflare and AWS Route 53 out of the box. For other DNS providers, a shell script can be used. I am using deSEC.io and so I needed to write a shell script. I struggled quite a bit with this because there's not really any documentation out there about how to do it.

Assuming TrueNAS tries to get a certificate for my.dns.domain it will call the shell script with the following parameters. To set the challenge, it calls

myscript.sh set my.dns.domain _acme-challenge.my.dns.domain <challenge>

Once the challenge is either solved or failed it removes the challenge from DNS by calling

myscript.sh unset my.dns.domain _acme-challenge.my.dns.domain <challenge>

It doesn't attempt to find the zone for you instead it gives you the full domain name (e.g. let's say the zone in this example is dns.domain, then TrueNAS will provide my.dns.domain instead of dns.domain). That's annoying because for deSEC I need the zone. I decided to hard code the zone into the script to avoid anything more complicated than calling curl.

truenas_acme.sh
1 #!/bin/bash
2
3 set -euo pipefail
4
5 TOKEN="<deSEC.io token>"
6 ZONE="dns.domain"
7 URL="https://desec.io/api/v1/domains/${ZONE}/rrsets"
8
9 # https://desec.readthedocs.io/en/latest/dns/rrsets.html#creating-an-rrset
10 add_record() {
11 name=${1%%".${ZONE}"} # bash for "trim suffix"
12 txtvalue=$2
13 curl -s -X POST "${URL}/" \
14 --header "Authorization: Token ${TOKEN}" \
15 --header "Content-Type: application/json" --data @- <<EOB
16 {
17 "subname": "${name}",
18 "type": "TXT",
19 "ttl": 3600,
20 "records": ["\"${txtvalue}\""]
21 }
22 EOB
23 }
24
25 # https://desec.readthedocs.io/en/latest/dns/rrsets.html#creating-an-rrset
26 del_record() {
27 name=${1%%".${ZONE}"}
28 curl -s -X DELETE "${URL}/${name}/TXT/" \
29 --header "Authorization: Token ${TOKEN}"
30 }
31
32 if [ "$#" -ne 4 ]; then
33 echo "invalid number of parameters"
34 exit 1
35 fi
36
37 case "${1}" in
38 "set")
39 add_record "${3}" "${4}"
40 ;;
41 "unset")
42 del_record "${3}"
43 ;;
44 *)
45 echo "unexpected commandline: ${@}"
46 exit 1
47 ;;
48 esac

It's not the most robust piece of software, but it does its job.