Cannot validate challenge for a wildcard certificate using dns-01

My domain is:

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

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

  • ansible 2.18.4
  • community.crypto.acme_certificate 2.26.0

The error message is "No TXT record found at _acme-challenge.sdxlive.com", except that there is a TXT record matching exactly the expected <resource_value> of that record on the authoritative DNS server:

# named-checkzone -j -D -f raw -o - sdxlive.com "/etc/bind/db.sdxlive.com.signed"|grep _acme-challenge.sdxlive.com
OK
*.sdxlive.com.				      3600 IN NSEC	_acme-challenge.sdxlive.com. A AAAA RRSIG NSEC
_acme-challenge.sdxlive.com.		      60 IN TXT		"<resource_value>"
...

I have not changed my workflow since the last time I successfully validated a wildcard certificate 2 months ago for the same domain using the same authoritative DNS server: what is going on?

It's best to use https://unboundtest.com to ensure the txt records are publicly visible - that platform closely replicates the ISRG setup.

Issues like you are experiencing* are often due to ephemeral networking/routing conditions. I suggest trying again in a few minutes.

*There was a valid TXT record returned, and you seem to be running your own dns servers, so a cache issue is unlikely.

1 Like

Not sure if this is actually related to your problem, but it looks like something is off about your IPv6 DNS server glue records:

  • com to sdxlive.com: The glue address(es) for ns1.sdxlive.com (2a01:e0a:81c:99c0:558c:7a:5b5c:7b5d) differed from its authoritative address(es) (2a01:e0a:81c:99c0:558c:7a:5b5c:7b5d, 2a01:e0a:81c:99c0:ab21:dfd4:b245:7ef). See RFC 1034, Sec. 4.2.2.
  • com to sdxlive.com: The glue address(es) for ns2.sdxlive.com (2a01:e0a:81c:99c0:558c:7a:5b5c:7b5d) differed from its authoritative address(es) (2a01:e0a:81c:99c0:558c:7a:5b5c:7b5d, 2a01:e0a:81c:99c0:ab21:dfd4:b245:7ef). See RFC 1034, Sec. 4.2.2.

It also looks like ns1.sdxlive.com and ns2.sdxlive.com are at the same IPs, which shouldn't inherently break anything but isn't a sign of a robust DNS infrastructure.

4 Likes

I've been trying for almost an hour, and the result is always the same.
unboundtest.com returns the correct TXT records, but LE cannot validate the certificate. Something is wrong here.

I will fix this slight discrepancy if I can change my glue records on my registrar without disabling first DNSSEC. I'm expecting an answer from them very soon.
However, this should not be the source of the current issue.

I wouldn't be so sure. Unboundtest does not always reproduce what LE servers do.

LE walks the authoritative tree. Misconfigurations in delegations or glue records can cause odd results. We have seen such things often.

1 Like

Except that after fixing up the slight discrepancy with my registrar,...

the issue is still there:

    "msg": "Failed to validate challenge for dns:*.sdxlive.com: Status is \"invalid\" and not \"valid\". Challenge dns-01: Error urn:ietf:params:acme:error:unauthorized: \"No TXT record found at _acme-challenge.sdxlive.com\".",

Using unboundtest.com to verify the TXT records:

I ask again: what is going on?
Is the fact that there are 2 TXT records (one for *.sdxlive.com and another one for sdxlive.com) disturbing LE?

If I remove the domain TXT record and leave only the wildcard domain TXT record:

... the issue is still there:

    "msg": "Failed to validate challenge for dns:*.sdxlive.com: Status is \"invalid\" and not \"valid\". Challenge dns-01: Error urn:ietf:params:acme:error:unauthorized: \"No TXT record found at _acme-challenge.sdxlive.com\".",
  • Is it possible to have more details of the failure on LE side so you or I can fix it?
  • Any trace in the logs?
  • Also, is it possible to know what type of delegation tests are run by LE if that's what is blocking the validation?

Glad you got those glue records fixed.

Internal logs for LE servers are not available to the community volunteers. Staff routinely monitor this forum and we can also escalate once we work through the more likely causes.

Do you have more descriptive logs of the ACME Client activity?

What often happens for "no TXT record" is that the ACME Client places the TXT record and then immediately tells LE the challenge is ready. But, DNS Servers usually need some amount of time to sync those changes and LE's query gets a "No TXT" result as a consequence if the sync was not complete.

To deal with this ACME Clients either pre-check the TXT record (which because of things like anycast may not be conclusive) or they just wait a period of time. If the wait time is not long enough the same "No TXT" can happen.

Some DNS Providers have specific API calls to verify if the sync after a change is complete.

So, can you describe more about your ACME Clients flow? A more detailed log with timestamps would probably be enough to see what is happening.

Also, we see TXT values now. So, does your ACME Client remove them prior to the next cert request?

2 Likes

I never had any such issue before and I don't see why that would change on my side.

  • The records are available as soon as there are created on the DNS server
  • even if I retry the validation process again and again minutes or hours after they are created, the issue still persists.

For instance, here is a copy of the authorization metadata for the last successful wildcard certificate I received:

authorizations:
  '*.sdxlive.com':
    challenges:
    - status: valid
      token: <token>
      type: dns-01
      url: https://acme-v02.api.letsencrypt.org/acme/chall/1980828197/469487915865/EdrYBA
      validated: '2025-02-01T15:08:54Z'
      validationRecord:
      - hostname: sdxlive.com
    expires: '2025-03-03T15:08:55Z'
    identifier:
      type: dns
      value: sdxlive.com
    status: valid
    uri: https://acme-v02.api.letsencrypt.org/acme/authz/1980828197/469487915865
    wildcard: true
  sdxlive.com:
    challenges:
    - status: valid
      token: <token>
      type: dns-01
      url: https://acme-v02.api.letsencrypt.org/acme/chall/1980828197/469487915875/WpMN3g
      validated: '2025-02-01T15:08:55Z'
      validationRecord:
      - hostname: sdxlive.com
    expires: '2025-03-03T15:08:56Z'
    identifier:
      type: dns
      value: sdxlive.com
    status: valid
    uri: https://acme-v02.api.letsencrypt.org/acme/authz/1980828197/469487915875
cert_days: 89

Since then, nothing has changed on my side. However, I remember that LE has recently introduced some "profiles" which may have destabilized somehow the system in some cases. These new profiles have been advertised just after my last successful certificate validation.
My logs would not be helpful because I don't think that the timestamps have anything to do with the issue.

I have just been able to create/validate a SAN certificate with the same workflow using the same DNS server for a host located on the same domain. Here is a copy of the authorization metadata I've just received:

authorizations:
  sdx1-kvm.sdxlive.com:
    challenges:
    - status: valid
      token: <token>
      type: dns-01
      url: <url>
      validated: '2025-04-03T16:31:44Z'
      validationRecord:
      - hostname: sdx1-kvm.sdxlive.com
    expires: '2025-05-03T16:31:45Z'
    identifier:
      type: dns
      value: sdx1-kvm.sdxlive.com
    status: valid
    uri: <uri>
cert_days: 89

So the problem is specific to wildcard certificates.
Maybe the CAA record has something to do with that issue; I remember I've changed it on 20/01/2025 from issuewild to issue following an LE article I read.
If only there was a way to know the exact cause of the error in LE logs, that would narrow it down and allow a quick fix.

As indicated in my first post, I use ansible with their acme_certificate module.
I run my own DNS server and I create/update/delete all the required records using ansible with their nsupdate module.
In all previous certificate validation, I used the exact same workflow, and I never had any trouble of any kind with the creation/validation of the wildcard certificate.

I have tried many things since yesterday, including removing the TXT records and recreating them because I thought LE could be confused by the fact that I create one TXT record for *.sdxlive.com and another one for sdxlive.com. So I tested multiple scenari (only one TXT record for *.sdxlive.com, then only one TXT record for sdxlive.com) but they all failed.

Also, when the validation succeeds, all TXT records for _acme-challenge.sdxlive.com are removed before the next validation a few months later.

Do you mean you check the prior authz and/or chall URLs? Or that you issue a new cert request? Because a new cert request would use new challenge tokens.

If true, yours would be the first report of that work affecting DNS Challenges in general. Given that rolled out months ago it is highly unlikely to be related. So unlikely to not be worth pursuing. LE issues typically over 6 million certs per day and many are authenticated using DNS Challenge.

No, if the CAA were a problem the error message would be very clear that issuance of the cert is prevented by CAA. It would not interfere with the challenge itself.

Having two TXT record values one for the apex and one for the first level subdomain is very common. If LE had a problem with that it would be affecting a vast number of people and it is not.

I had asked whether your client deletes all the TXT records before starting a new cert request.

I asked because you have said the error message was "No TXT records" found. But, if there was leftover values from a prior run and LE could not find the current one the error would clearly say "Incorrect TXT record" found rather than "No TXT record"

Are you certain that error message is coming from the Let's Encrypt Server? Because many ACME Clients do a "pre-check" of the TXT value before submitting the request to LE.

Is it possible your Client's pre-check is failing and it is the one showing that message?

2 Likes

I mean validating using the prior cert request of course with acme_certificate module:

______________________________________________________
/ TASK [task_name : Validating a TLS Wildcard domain server \
\ Certificate from Let’s Encrypt CA - Phase II]            /
 ----------------------------------------------------------
The full traceback is:
  File "/tmp/ansible_acme_certificate_payload_3oamgigl/ansible_acme_certificate_payload.zip/ansible_collections/community/crypto/plugins/modules/acme_certificate.py", line 976, in main
    client.get_certificate()
  File "/tmp/ansible_acme_certificate_payload_3oamgigl/ansible_acme_certificate_payload.zip/ansible_collections/community/crypto/plugins/modules/acme_certificate.py", line 858, in get_certificate
    authz.raise_error('Status is "{status}" and not "valid"'.format(status=authz.status), module=self.module)
  File "/tmp/ansible_acme_certificate_payload_3oamgigl/ansible_acme_certificate_payload.zip/ansible_collections/community/crypto/plugins/module_utils/acme/challenges.py", line 249, in raise_error
    raise ACMEProtocolException(
fatal: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "account_email": "<user>@sdxlive.com",
            "account_key_content": null,
            "account_key_passphrase": null,
            "account_key_src": "lets_encrypt_rsakey.pem",
            "account_uri": null,
            "acme_directory": "https://acme-v02.api.letsencrypt.org/directory",
            "acme_version": 2,
            "agreement": null,
            "chain_dest": "sdxlive.com.intermediate.crt",
            "challenge": "dns-01",
            "csr": "sdxlive.com.csr",
            "csr_content": null,
            "data": {
                "account_uri": "https://acme-v02.api.letsencrypt.org/acme/acct/<account_value>",
                "authorizations": {
                    "*.sdxlive.com": {
                        "challenges": [
                            {
                                "status": "pending",
                                "token": "<token>",
                                "type": "dns-01",
                                "url": "https://acme-v02.api.letsencrypt.org/acme/chall/<account_value>/<value1>/<value2>"
                            }
                        ],
                        "expires": "2025-04-08T13:17:05Z",
                        "identifier": {
                            "type": "dns",
                            "value": "sdxlive.com"
                        },
                        "status": "pending",
                        "uri": "https://acme-v02.api.letsencrypt.org/acme/authz/<account_value>/<value1>",
                        "wildcard": true
                    },
                    "sdxlive.com": {
                        "challenges": [
                            {
                                "status": "pending",
                                "token": "<token>",
                                "type": "dns-01",
                                "url": "https://acme-v02.api.letsencrypt.org/acme/chall/<account_value>/<value3>/<value4>"
                            },
                            {
                                "status": "pending",
                                "token": "<token>",
                                "type": "http-01",
                                "url": "https://acme-v02.api.letsencrypt.org/acme/chall/<account_value>/<value3>/<value5>"
                            },
                            {
                                "status": "pending",
                                "token": "<token>",
                                "type": "tls-alpn-01",
                                "url": "https://acme-v02.api.letsencrypt.org/acme/chall/<account_value>/<value3>/7TgUhg"
                            }
                        ],
                        "expires": "2025-04-08T13:17:05Z",
                        "identifier": {
                            "type": "dns",
                            "value": "sdxlive.com"
                        },
                        "status": "pending",
                        "uri": "https://acme-v02.api.letsencrypt.org/acme/authz/<account_value>/<value3>"
                    }
                },
                "cert_days": 31,
                "challenge_data": {
                    "*.sdxlive.com": {
                        "dns-01": {
                            "record": "_acme-challenge.sdxlive.com",
                            "resource": "_acme-challenge",
                            "resource_value": "CdBzTL-h79q107qbJ_FkpRCt5kOvhsbyOlKR4E5NIF0"
                        }
                    },
                    "sdxlive.com": {
                        "dns-01": {
                            "record": "_acme-challenge.sdxlive.com",
                            "resource": "_acme-challenge",
                            "resource_value": "<resource_value>-I"
                        },
                        "http-01": {
                            "resource": ".well-known/acme-challenge/<token>",
                            "resource_value": "<token>.<token2>"
                        },
                        "tls-alpn-01": {
                            "resource": "sdxlive.com",
                            "resource_original": "dns:sdxlive.com",
                            "resource_value": "<resource_value>+I="
                        }
                    }
                },
                "challenge_data_dns": {
                    "_acme-challenge.sdxlive.com": [
                        "CdBzTL-h79q107qbJ_FkpRCt5kOvhsbyOlKR4E5NIF0",
                        "<resource_value>-I"
                    ]
                },
                "changed": true,
                "failed": false,
                "finalize_uri": "https://acme-v02.api.letsencrypt.org/acme/finalize/<account_value>/<account_value2>",
                "order_uri": "https://acme-v02.api.letsencrypt.org/acme/order/<account_value>/<account_value2>"
            },
            "deactivate_authzs": false,
            "dest": "sdxlive.com.crt",
            "force": true,
            "fullchain_dest": "sdxlive.com.fullchain.crt",
            "include_renewal_cert_id": "never",
            "modify_account": true,
            "order_creation_error_strategy": "auto",
            "order_creation_max_retries": 3,
            "profile": null,
            "remaining_days": 91,
            "request_timeout": 10,
            "retrieve_all_alternates": false,
            "select_chain": null,
            "select_crypto_backend": "cryptography",
            "terms_agreed": true,
            "validate_certs": true
        }
    },
    "msg": "Failed to validate challenge for dns:*.sdxlive.com: Status is \"invalid\" and not \"valid\". Challenge dns-01: Error urn:ietf:params:acme:error:unauthorized: \"No TXT record found at _acme-challenge.sdxlive.com\".",
    "other": {
        "authorization": {
            "challenges": [
                {
                    "error": {
                        "detail": "No TXT record found at _acme-challenge.sdxlive.com",
                        "status": 403,
                        "type": "urn:ietf:params:acme:error:unauthorized"
                    },
                    "status": "invalid",
                    "token": "<token>",
                    "type": "dns-01",
                    "url": "https://acme-v02.api.letsencrypt.org/acme/chall/<account_value>/<value1>/<value2>",
                    "validated": "2025-04-01T13:21:42Z"
                }
            ],
            "expires": "2025-04-08T13:17:05Z",
            "identifier": {
                "type": "dns",
                "value": "sdxlive.com"
            },
            "status": "invalid",
            "uri": "https://acme-v02.api.letsencrypt.org/acme/authz/<account_value>/<value1>",
            "wildcard": true
        },
        "identifier": "dns:*.sdxlive.com"
    }
}

To be honest, I don't know.
Since I'm not a python expert, I've uploaded a copy of ansible acme_certificate.py: maybe someone can decipher what is truly going on behind the curtains.
acme_certificate.py.txt (46.4 KB)

Also, you seem to have overlooked the fact that I can validate A SAN certificate but not a wildcard one using the same call to acme_certificate module using the same dns-01 challenge validated against the same DNS server, with of course different CSR, common name and subject alt name.

I have not overlooked it but would require much more detailed information from you to begin to compare and contrast what is happening with both.

The difference is interesting but is not sufficient to say anything definitive.

1 Like

I'd like to try a new run with a new CSR, but this time using the new "profile" feature as it is supported by ansible acme_certificate module.
Is it possible considering that the challenges for the old CSR expires on "2025-04-08T13:17:05Z"? Or do I have to wait until then before trying again with a new CSR?
Maybe I'll have more luck with the tlsserver profile...

I don't believe any profile other than "classic" is available to production. Perhaps it is in Staging.

That won't affect how the DNS Challenge works. You can see what this does at the LE profiles page docs which I assume you've seen. I've said this before.

I don't think my further involvement in this thread is helpful. You haven't provided key info I've asked for and repeatedly choose your own ideas of what is wrong. And, looking back at your previous threads in this forum that seems to be a pattern.

Perhaps a different volunteer will offer assistance. Or, perhaps try a forum for that ACME Client.

2 Likes

What more info do you need? I've already given a lot of details but I'm open to any suggestion.

OK,in case anyone stumbles upon the same issue, the solution is to use the parameter profile: 'classic' in both phases I (generating the challenge) & II (validating the challenge) when using the ansible module community.crypto. acme_certificate 2.26.0, despite the fact that the parameter is not marked as required in the official documentation so far.

What was tricky was the fact that phase I did not raise any error.
It is also very strange that there is no such issue with the SAN certificate despite using the same ansible module with the same parameters.

There was no issue with my IPv6 DNS server glue records on the registrar or any DNS propagation, just a slightly flawed ansible documentation.

1 Like