The tools
Let's encrypt is the source of nearly all SSL/TLS certificates for HTTPS at the hobbyist level, offering automatic issuance and renewal of certificates, using challenges offered over HTTP or DNS.
In particular, a website must pass a DNS challenge to be issued a wildcard certificate for a domain of the form *.example.org
, by setting a TXT
record of the domain (or of the domain's CNAME
, which Letsencrypt respects) in question to a specific value.
Control of example.org.
and of *.example.org.
are both verified by checking the TXT
record at _acme-challenge.example.org.
.
Automatic renewal of Letsencrypt certificates is typically handled by Certbot.
DuckDNS, while not as universal, is a popular free dynamic DNS provider. Accounts may register domains of the form example.duckdns.org.
and update their A
/AAAA
records by means of a simple HTTP API.
Often, it is used alongside a conventional DNS provider, by pointing (say) www.example.org.
via CNAME
at example.duckdns.org.
.
Additionally, the same API lets users set or clear a TXT
record for their domain, specifically for interoperability with letsencrypt.
(All A
/AAAA
/TXT
records set for example.duckdns.org.
are mirrored to *.example.duckdns.org.
.)
The goal
The goal is to use a reasonably standard setup of Letsencrypt/Certbot to pass DNS challenges using the DuckDNS API.
In particular, we want a certificate for both example.org
and *.example.org
, where we control example.duckdns.org.
and the ordinary DNS provider serves *.example.org. CNAME example.duckdns.org.
.
If we have multiple front-end domains all pointed at the same back-end webserver, we may even want to handle renewals for all four of example.org
, *.example.org
, example.com
, *.example.com
.
The naive solution
To use the DuckDNS API per their spec, we can put this line in a simple shell script (after renaming the appropriate environment variables, and hardcoding the DuckDNS domain & auth-token)
V1='example.duckdns.org'
V2='our-duckdns-auth-token'
V3="$CERTBOT_VALIDATION"
curl -s -S "https://www.duckdns.org/update?domains=${V1}&token=${V2}&txt=${V3}"
and ask Certbot to run it using the following command
certbot certonly -v --manual --manual-auth-hook my-script.sh -d '*.example.org.'
after which renewal will be handled automatically.
The problem
DuckDNS only holds one TXT
record for a domain at a time, with any additional records set overwriting the old one.
At least using the default Certbot, Letsencrypt asks the client requesting the certificate to pass all challenges at once.
That is, while the naive solution above will work for *.example.org.
, if we extend it to
-d 'example.org' -d '*.example.org'
then Letsencrypt will run two challenges, one for each domain, and expect two different TXT
records to be simultaneously present at _acme-challenge.example.org.
.
Certbot will run the hooked script twice, with the second TXT
record added overwriting the first, leaving just one.
At least one challenge will therefore fail and the full certificate can't be issued.
In the four-domain extended version of the problem, there would be one success and three failures.
Since Letsencrypt is ubiquitous and DuckDNS is reasonably popular, there are scattered references [1] [2] [3] to this problem on the internet (including some more in the non-public DuckDNS google groups) but nothing that I can find on any Stack exchange sites.