Implementing DANE with certbot using Let's Encrypt certificates


Despite being around for a while, DANE has been only been slowly catching on in the last few years. But, that’s finally starting to change as encrypted DNS gets more popular and people have started to realize the advantages DANE offers. Want to implement it using free certificates from Let’s Encrypt? You should! Let’s do it…

If you already know all about DANE and just want to skip to the implementation, click here.

The problem: trust and man-in-the-middle

Modern certificates provide pretty great security and have become fairly simple to implement compared to how it used to be. In addition, Let’s Encrypt has changed the game by providing free, quality, trusted certificates for everyone and Google has been steadily on a crusade to make sure everyone types that ’s’ at the end of ‘http’. However, there are a few lingering issues with how certificates are implemented. In particular there are two issues that we can now pretty easily solve with DANE: trust issues and man-in-the-middle attacks.

The way certificates are used currently, it’s necessary to follow the certificate chain up to a level where you already trust the ‘issuing authority’. For example, I present my certificate to you but you don’t know if I’m trustworthy. Makes sense, we don’t really know each other. You see that my certificate is ‘signed’ (issued) by intermediate-guy. But, you don’t know him either so you also don’t know whether he’s trustworthy. However, you notice that his certificate is issued by super-honest-girl and you DO know her and, not just because of her name, you trust her. So by extension, you can now reasonably trust anything she’s signed and anything signed by someone she’s signed for and eventually we come full-circle to you implicitly trusting my certificate. That’s the basis of the ‘chain of trust’.

The second issue is man-in-the-middle attacks. This is where you trust the certificate being used, but someone intercepts that communication, changes it, then re-encodes it using another certificate. This relies on them being there to intercept the initial certificate handshake and they can impersonate both ends of the negotiation. Let’s use the classic example to clear this up:

Because of this possibility, we have to return to a trusted ‘issuing’ Certificate Authority. If all certificates in the conversation are signed by one or more trusted authorities, then we can be reasonably sure that they are from who they claim to be from and have not been tampered with. So, you can see that these issues are linked.

DANE: an elegant and overdue solution

DNS-based Authentication of Named Entities (DANE) seeks to tackle the above two problems without having to make changes on server systems. The only changes involved are DNS records and client behaviour. The full specification does a good job of using decently clear plain-English in explaining what DANE is and how it works. In a nutshell:

DANE allows administrators to specify, via securely signed DNS records (DNSSEC), the exact thumbprint/hash of the certificate the client should be expecting to receive from the system identified in DNS.

How does this address the two problems we identified earlier? First of all, it eliminates the possibility of a man-in-the-middle attack by clearly specifying what certificate the client should be expecting to receive. If someone were to try intercepting your communication and substituting a certificate, the client would immediately notice the hashed public key does not match what’s specified in the DNS record and terminate communication. Following this line of reasoning, there is also no need for a trusted Certificate Authority (CA) anymore either. Since we know what certificate to expect we don’t need anyone we ‘trust’ to tell us the certificate is ok.


DANE falls apart entirely if the DNS records received from the DNS server are not signed. That’s why DNSSEC is required for DANE and why adoption has been slow. People have taken their sweet time implementing DNSSEC but, things have changed and most DNS providers now offer this as a standard (read: free) option. If the received records are signed and all signatures match, it means that the DNS record has not been tampered with and, therefore, the hashed public key can be trusted for verifying the certificate provided by the server in question. That was a mouthful…

TLSA records

Since this is all basically implemented on the DNS server, a new type of record had to be created. Introducing the TLSA record! The record identification is made of up three (3) parts:

  1. The port number on which the TLS server is listening;
  2. The protocol to be used (tcp, udp, sctp, user-defined); and
  3. Server host name.

The data portion consists of:

If you’re interested in what all these options mean, I suggest you read the specification. But, in this post and in nearly all cases you’re going to encounter, the 3 1 1 settings we’ll be using are the officially recommended settings.

Get certificates

The problem with regular Let’s Encrypt certificates

As you know, Let’s Encrypt certificates renew every 90 days. This is not a problem in itself, however, the standard procedure is to generate a new private key with each renewal and, thus, an entirely new public key also. This will necessarily change the hashed key value that would appear in our TLSA record meaning that we would have to remember to update our DNS records on each renewal otherwise the wrong key will be specified and no one will trust our site/server/application! We need to tell Let’s Encrypt to re-use our private key so that the public key hash value does NOT change – that way our DNS record remains valid across certificate renewals.

Make our certificates ‘DANE-ready’

As mentioned in the title of this post, we’ll be using certbot to generate free, trusted certificates signed by Let’s Encrypt. I’ve already written three articles covering the most common homelab/SOHO setups, so if you need help setting up certbot and requesting your certificates, choose an article that best matches your use-case. Each link will open in a new window so you can come back here when you’re done.

Before you get your certificates, regardless of the article you choose to follow, you will need to append the following two parameters to the commands in those articles:

--keep --reuse-key
keepRetain the current certificates unless they need to be renewed.
reuse-keyUse the same private key when renewing certificates. This prevents the public key from changing, therefore we don’t have to update our TLSA DNS record each time our certificate renews.

If you know what options you’ll be using in certbot, then go ahead and generate your certificates now and then come back and keep reading. If you need a little help, here are the articles I wrote about certbot:

Remember to append the above two parameters to your certbot command!!!

Make sure you go through the testing/verifying sections of those articles to confirm things are setup properly. When checking your [renewalparams], make sure the following line is present:

reuse_key = True

If it’s not present, add it before doing the --dry-run test!

Hashing and testing

Ok, I’m assuming you got your certificate issued without any problems and you’re ready to DANE it up :-)

The official recommendation is to use a SHA-256 hash (matching-type: 1) of our certificate’s public key (selector: SPKI) in our TLSA DNS records. In order to do that, we need to generate the hash. Then we need to renew our certificate and make sure that the hash stays the same to confirm our setup. Let’s start with getting the initial hash. We’re working with the public key so you do NOT have to be root to do the following:

openssl x509 -noout -pubkey -in /etc/letsencrypt/live/domain.tld/cert.pem | openssl rsa -pubin -outform DER 2>/dev/null | sha256sum > initial.certhash

The output, without the trailing ‘-’, is our hashed public key. Now, let’s force-renew our certificate and make sure this hash does NOT change. You MUST run the following in a root shell or SUDO:

# list certificates being handled by certbot
certbot certificates
# use the appropriate certificate name
certbot renew --cert-name certname --force-renewal

Now, let’s hash the new public certificate just like we did before and compare the output:

# hash the renewed public certificate
openssl x509 -noout -pubkey -in /etc/letsencrypt/live/domain.tld/cert.pem | openssl rsa -pubin -outform DER 2>/dev/null | sha256sum > renewed.certhash
# compare our initial and renewed certificate hash
diff initial.certhash renewed.certhash && echo ok || echo error

If the files match, the only output you should see is ‘ok’ and we’re good to go onto the next step. If they are not the same, you will see some output and then ‘error’ as the last line. In that case, you need to go back and review the commands you used and make sure you added the --keep --reuse-key options.

Generate TLSA records

Almost there! We just have to generate our TLSA records. This part will really depend on your DNS server/provider. If you’re using Cloudflare, GoDaddy, Namecheap or something like that, they all have GUI forms for adding records. Select TLSA and fill out the fields as required. Remember, you are generating a 3 1 1 record which means:

Usage field(3) DANE-EE: Domain Issued Certificate
Selector field(1) SPKI: Use subject public key
Matching-type field(1) SHA-256: SHA256 hash

The other fields should be pretty easy for you to figure out – the port your service is running on, the protocol and domain name.

If you’re directly updating a zonefile for something like BIND9, an amazing guy named Shumon Huque made an online TLSA generator that will generate exactly what you need to type in your zonefile. You’ll need to copy/paste your public certificate since his page generates the (same) hash we already did but you can’t just supply that.


After you’ve set up your TLSA records, installed your certificates on your server/application/website and given things time to propagate, you can test your records. Again, Shumon Huque has a great site for TLSA testing. Assuming you pass the test, you’re all done!

The magic of ‘DANE-EE’ (optional reading)

This section is really just for the curious and those that want to know a little more why Usage 3: DANE-EE is the recommended DANE implementation method in most cases. It really comes down to flexibility as defined in the standards. DANE-EE has the following key attributes:

These specifications have a few very useful consequences:

First, access to your site will not be blocked/forbidden simply because your certificate is expired. You’ll get warnings from your browser and might get other problems, but DANE will still work properly and everything will remain encrypted.

Second, and very cool for lab-use, you can use self-signed certificates! Obviously you’ll get browser warnings, but DANE verification will still function properly. Why? Because verifying things up to a trusted CA is no longer necessary, remember?

Finally, and absolutely most useful of all, the certificate name does NOT have to match the server you are accessing! This is so useful if you run one machine to host several sites and don’t want to setup SNI or anything like that. Unlike traditional certificate implementations, you do NOT have to include every target as a SAN name on your certificate. This can save you hundreds or even possibly thousands of dollars! All that matters is that the public key hash matches what is specified in your TLSA records – the host name doesn’t matter anymore. If you’ve ever administered webhosts in the past, you can fully appreciate how useful this is :-)

Final thoughts

Now you have the latest and greatest method of securing your server/app/website using a free and trusted auto-renewing TLS certificate that is hardened against man-in-the-middle attacks and is very flexible in it’s implementation. Not to mention, you’re complying with the cutting-edge recommended TLS implementation – aren’t you impressive!

Thanks for reading my techie-thoughts on this issue. Have any comments or suggestions? Want to add your tips? Things you want me to cover in a future article? Comment below!