Dns-rfc2136 "Received response from server: REFUSED" due to unexpected TXT record

Long time Let's Encrypt user (http-01 challenge with an acme client python script), first time certbot user with dns-01 challenge via dns-rfc2136 plugin.

I'm attempting to switch over to using certbot and the dns-rfc2136 plugin to issue/renew my certificates. I have a BIND DNS server configured to allow updates like so:

zone "calenhad.com" {
	type master;
	[snip]
	update-policy {
		grant "certbot" name _acme-challenge.calenhad.com. txt;
	};
};

And I have verified that I can dynamically update this record using nsupdate on a different machine:

named[7720]: client @0x7ff6744cf080 [redacted]#33304/key certbot: updating zone 'calenhad.com/IN': adding an RR at '_acme-challenge.calenhad.com' TXT "test ddns update"

However, attempting to use certbot fails:

gibmat@manwe:~$ sudo certbot --dry-run certonly -d manwe.calenhad.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-rfc2136, Installer None
Account registered.
Simulating a certificate request for manwe.calenhad.com
Performing the following challenges:
dns-01 challenge for manwe.calenhad.com
Cleaning up challenges
Encountered exception during recovery: certbot.errors.PluginError: Received response from server: REFUSED
Received response from server: REFUSED

Looking at more verbose output:

Performing the following challenges:
dns-01 challenge for manwe.calenhad.com
No authoritative SOA record found for _acme-challenge.manwe.calenhad.com
No authoritative SOA record found for manwe.calenhad.com
Received authoritative SOA response for calenhad.com
Encountered exception:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/_internal/auth_handler.py", line 70, in handle_authorizations
    resps = self.auth.perform(achalls)
  File "/usr/lib/python3/dist-packages/certbot/plugins/dns_common.py", line 57, in perform
    self._perform(domain, validation_domain_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/_internal/dns_rfc2136.py", line 87, in _perform
    self._get_rfc2136_client().add_txt_record(validation_name, validation, self.ttl)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/_internal/dns_rfc2136.py", line 148, in add_txt_record
    .format(dns.rcode.to_text(rcode)))
certbot.errors.PluginError: Received response from server: REFUSED

Poking at the code where the exception is raised, it appears that certbot is attempting to construct a TXT record for _acme-challenge.manwe.calenhad.com, not _acme-challenge.calenhad.com as I would expect from the docs for the rfc2136 plugin and numerous blog posts about setting up certbot for dns-01 challenges.

BIND correctly refuses this update. But is this correct certbot behavior? Will I have to add a ton of additional grants in my BIND zone for each and every host that I want to issue a specific certificate for? While a wildcard cert can work in several instances, I have some servers that need a specific, single-host certificate.

certbot version: 1.10.1
OS: Debian 10
certbot config files:

gibmat@manwe:~$ cat /etc/letsencrypt/cli.ini 
# Because we are using logrotate for greater flexibility, disable the
# internal certbot logrotation.
max-log-backups = 0

key-type = rsa
rsa-key-size = 4096

email = [redacted]

authenticator = dns-rfc2136
dns-rfc2136-credentials = /etc/letsencrypt/rfc2136.ini
dns-rfc2136-propagation-seconds = 10

gibmat@manwe:~$ sudo cat /etc/letsencrypt/rfc2136.ini 
# Target DNS server
dns_rfc2136_server = [redacted]
# Target DNS port
dns_rfc2136_port = 53
# TSIG key name
dns_rfc2136_name = certbot
# TSIG key secret
dns_rfc2136_secret = [redacted]
# TSIG key algorithm
dns_rfc2136_algorithm = HMAC-SHA512
1 Like

Hi @gibmat

if you use that command

manwe.calenhad.com is your domain name, so a TXT entry _acme-challenge.manwe.calenhad.com is required.

So I don't see a problem.

1 Like

It's the CA's choice to decide which Authorization Domain Name to use.

Let's Encrypt uses the requested domain as the ADN, with the exception of a wildcard domain, in which case it will trim the wildcard label and use that as the ADN.

If you really don't want to create further grants, you could create a wildcard certificate, which will only use _acme-challenge.example.com for the ADN.

1 Like

Your BIND configuration seems to be just for the hostname calenhad.com while you're requesting a certificate for the subdomain manwe.calenhad.com.

Edit: Dang, everybody is quick this evening (CET timezone :stuck_out_tongue:) :smiley:

@gibmat Which documentation made you believe that the _acme-challenge.calenhad.com would also validate subdomains such as manwe.calenhad.com?

1 Like

Thanks for the quick replies, all! I'll work on a pull request to clarify the documentation, although it may not be today.

@Osiris It's the documentation available here: https://certbot-dns-rfc2136.readthedocs.io/en/stable/ Go down to the bottom and look at the second example:

certbot certonly \
  --dns-rfc2136 \
  --dns-rfc2136-credentials ~/.secrets/certbot/rfc2136.ini \
  -d example.com \
  -d www.example.com

While I haven't tried that specific command, based on the replies and what I've seen today, I would expect the validation of "www.example.com" to fail in the same way -- but from reading the doc, you're lead to believe the single grant in the BIND configuration is sufficient. :wink:

1 Like

I'm afraid the sample BIND configuration isn't compatible with all three examples. Perhaps the note (…)

This configuration limits the scope of the TSIG key to just be able to add and remove TXT records for one specific host for the purpose of completing the dns-01 challenge.

(…) isn't clear enough about the restriction of the sample BIND configuration.

1 Like

A simple way to address this could be to add a second grant to _acme-challenge.www.example.com. in the sample BIND configuration.

1 Like

Along with another note explicitly calling out the need for additional BIND grants if you won't be using only a wildcard certificate. I'll work on that in my pull request, unless someone else beats me to it!

And I can confirm that adding an additional grant fixed my issue. :partying_face:

1 Like

You could also substitute the "name" ruletype for "subdomain", "zonesub" or "wildcard" with the corresponding changes to the overall update-policy contents, depending on which ruletype you choose.

The certbot-dns-rfc2136 documentation also links to the BIND documentation:

I think we should note here that the examples in the documentation are just that: samples. Nobody said it's a good idea to copy/paste those examples and expect it to work in your specific situation. Although perhaps the note about the sample BIND configuration to be specific for a single hostname might be a little bit more explicit.

1 Like