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.
| 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.