TLSA record changes with every renewal process which breaks DANE

My web server is (include version): Not relevant

The operating system my web server runs on is (include version): Ubuntu 20.04

My hosting provider, if applicable, is: not relevant VPS with full root access

I can login to a root shell on my machine (yes or no, or I don't know): Yes

I'm using a control panel to manage my site (no, or provide the name and version of the control panel): No, not for Cerbot, there is Webmin but not used for certbot

The version of my client is (e.g. output of certbot --version or certbot-auto --version if you're using Certbot): certbot 0.40.0

Hi, I tried to switch over from dehydrated script (which sometimes causes problems with bind and I would like to have a second option) for cert renewal to certbot. I'm running into problems with changing TLSA records with certbot.

With the dehydrated script and the option PRIVATE_KEY_RENEW="no" the cert files after renewal are not identical but if I create the TLSA records this record stays the same. Which means TLSA record does not have to be renewed every time there is a new ceritficate created (which means around every 2 months). Changing TLSA every time a certificate is renewed is a bad process in my opinion because there are at least hours until until a full day until DNS records are renewed and in this time TLSA / DANE is not valid).

I tried to to the same with certbot but I noticed that with every renewal process the generated TLSA
record based on certificate changes. I thought that maybe the option --reuse-keys would help. But it does not seem to help. I'm still getting new TLSA records every time based on the generated certificates.

Am I doing something wrong or is this by design with certbot? If this is by design it would be nice if this can be updated automatically via options because for validation DNS is already used.

Renew job running on root user hourly (auto generated except the --reuse-key part:
test -x /usr/bin/certbot -a ! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew --reuse-key

Options for domain:
renew_before_expiry = 30 days
version = 0.40.0
archive_dir = /etc/letsencrypt/archive/
cert = /etc/letsencrypt/live//cert.pem
privkey = /etc/letsencrypt/live//privkey.pem
chain = /etc/letsencrypt/live//chain.pem
fullchain = /etc/letsencrypt/live//fullchain.pem

Options used in the renewal process

[renewalparams]
account =
rsa_key_size = 4096
server = https://acme-v02.api.letsencrypt.org/directory
authenticator = dns-rfc2136
dns_rfc2136_credentials = /etc/letsencrypt/renewal/rfc2136.ini
renew_hook = systemctl reload nginx apache2 webmin dovecot postfix

You might be better off adding this to /etc/letsencrypt/cli.ini:

reuse-key = true

On Ubuntu 20.04, the systemd timer is used, not the cronjob. So if the only place you made this change was the crontab entry, it would have had no effect.

You haven't mentioned what kind of TLSA record you are using.

If it's e.g. 3 1 1, then reuse-key should work fine.

3 Likes

Perhaps worth the read:

Thank you. I found this.

Sorry it's German but basically it says that the switchover problem is no problem is the X3 - or whatever is valid at the time - from letsencrypt is placed in the dns TLSA records additionally.

1 Like

Thanks, I was not sure which options could be used in the ini due to that I placed it in command line. Seems to be nearly everything which is working in command line is working in the ini? This part of the documentation is very short. I will try that, thanks.

I'm using 3.1 2 - so this should work.

I have tried this via dry run but still getting new TLSA? Should it be possible to simulate that via dry run or not?

1 Like

A dry-run discards the certificate, so I don't see how you would be getting a different TLSA record.

Here is me with reuse-key = true in cli.ini:

Generating the initial certificate

$ sudo -E certbot certonly -d example.com --webroot -w /tmp
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator webroot, Installer None
Requesting a certificate for example.com
Performing the following challenges:
http-01 challenge for example.com
Using the webroot path /tmp for all unmatched domains.
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
  /etc/letsencrypt/live/example.com/fullchain.pem
  Your key file has been saved at:
  /etc/letsencrypt/live/example.com/privkey.pem
  Your certificate will expire on 2026-01-31. To obtain a new or
  tweaked version of this certificate in the future, simply run
  certbot again. To non-interactively renew *all* of your
  certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by:

  Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
  Donating to EFF:                    https://eff.org/donate-le

Generating the initial TLSA record

$ cat <<EOF
_443._tcp.example.com. IN TLSA 3 1 1 $(openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -pubkey |
        openssl pkey -pubin -outform DER |
        openssl dgst -sha256 -binary |
        hexdump -ve '/1 "%02x"')
EOF
_443._tcp.example.com. IN TLSA 3 1 1 be801bf6e8b7c08b202c807620e23dc29ff4b3581b3a11ad6dba53815490456e

Forcing a renewal

$ sudo -E certbot renew --force-renewal
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Plugins selected: Authenticator webroot, Installer None
Renewing an existing certificate for example.com
Reusing existing private key from /etc/letsencrypt/live/example.com/privkey.pem.
Performing the following challenges:
http-01 challenge for example.com
Using the webroot path /tmp for all unmatched domains.
Waiting for verification...
Cleaning up challenges

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/example.com/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Checking that the TLSA record has not changed

$ cat <<EOF
_443._tcp.example.com. IN TLSA 3 1 1 $(openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -pubkey |
        openssl pkey -pubin -outform DER |
        openssl dgst -sha256 -binary |
        hexdump -ve '/1 "%02x"')
EOF
_443._tcp.example.com. IN TLSA 3 1 1 be801bf6e8b7c08b202c807620e23dc29ff4b3581b3a11ad6dba53815490456e
1 Like

Just be aware that intermediate CAs aren't forever. You still need to check whether the new certificate chain from the CA has the expected issuer, before deploying it. Unless you pay close attention to LE pre-announcements of upcoming changes, eventually a static pin of the intermediate CA will become stale.

So you're actually much better off with "3 1 1" records managed properly, than with "2 1 1" which give you an illusion of stability. The only reliable way to manage "2 1 1" records is to inject a layer of indirection between the certificate files LE renews and the ones used by application, with some code to conditionally propagate the changes only if the right preconditions hold (the new chain matches the published TLSA RRs).

I'll soon have a wrapper script for "certbot" (to be announced here and on the dane-users list, my @vdukhovni twitter feed, ...) that will make "3 1 1" rekeying painless and reliable (rekey automatically only after TLSA publication, otherwise keep using the same keys).

Once that's available, "2 1 1" records are best avoided, since you end up with a needlessly less secure configuration that way, "3 1 1" is easier to manage and monitor, less likely to be implemented incorrectly by verifiers, ...

3 Likes

Thank you.

I viewed the log entries in the dry run and renew option. But maybe this is not working with dry run like I imagined.

I did two dry runs and compared the full chain entries which are written into the log and compared with the current TLSA record. That resulted in three different TLSA.

So maybe I have to try this with real conditions. I will wait until next renewal and check it.

Can you setup a "test" subdomain?
If so, you could begin practicing with it in the in the staging environment immediately.
With something simple like:
certbot -d test.my.domain --staging
certbot delete -d test.my.domain
certbot -d test.my.domain --staging
certbot -d test.my.domain --staging --force-renewal

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.