Summary
When trying to validate a TLS wildcard certificate with Let’s Encrypt in phase II, I get this strange error despite the fact that the challenge is available in the DNS server.
Issue Type
Bug Report
Component Name
acme_certificate
Ansible Version
$ ansible --version
ansible [core 2.18.4]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /opt/ansible/venv/lib/python3.12/site-packages/ansible
ansible collection location = /opt
executable location = /opt/ansible/venv/bin/ansible
python version = 3.12.8 (main, Dec 4 2024, 12:15:27) [GCC 14.2.0] (/opt/ansible/venv/bin/python3.12)
jinja version = 3.1.6
libyaml = True
Community.general Version
$ ansible-galaxy collection list community.general
# /opt/ansible/venv/lib/python3.12/site-packages/ansible_collections
Collection Version
----------------- -------
community.general 10.5.0
# /opt/ansible_collections
Collection Version
----------------- -------
community.general 10.5.0
Configuration
$ ansible-config dump --only-changed
CACHE_PLUGIN(/etc/ansible/ansible.cfg) = redis
CACHE_PLUGIN_CONNECTION(/etc/ansible/ansible.cfg) = tls://localhost:<port>:0:<password>
CACHE_PLUGIN_TIMEOUT(/etc/ansible/ansible.cfg) = 259200
COLLECTIONS_PATHS(/etc/ansible/ansible.cfg) = ['/opt']
CONFIG_FILE() = /etc/ansible/ansible.cfg
DEFAULT_EXECUTABLE(/etc/ansible/ansible.cfg) = /bin/bash
DEFAULT_FORKS(/etc/ansible/ansible.cfg) = 1000
DEFAULT_GATHERING(/etc/ansible/ansible.cfg) = explicit
DEFAULT_HASH_BEHAVIOUR(/etc/ansible/ansible.cfg) = replace
DEFAULT_HOST_LIST(/etc/ansible/ansible.cfg) = ['/etc/ansible/hosts']
DEFAULT_LOAD_CALLBACK_PLUGINS(/etc/ansible/ansible.cfg) = True
DEFAULT_LOG_PATH(/etc/ansible/ansible.cfg) = /var/log/ansible.log
DEFAULT_PRIVATE_ROLE_VARS(/etc/ansible/ansible.cfg) = False
DEFAULT_ROLES_PATH(/etc/ansible/ansible.cfg) = ['/etc/ansible/roles'>
DEFAULT_TIMEOUT(/etc/ansible/ansible.cfg) = 240
DEFAULT_TRANSPORT(/etc/ansible/ansible.cfg) = ssh
EDITOR(env: EDITOR) = /usr/bin/gedit -s
ENABLE_TASK_DEBUGGER(/etc/ansible/ansible.cfg) = True
GALAXY_SERVER_LIST(/etc/ansible/ansible.cfg) = ['release_galaxy', 'test_galaxy', 'local_https', 'local_ssh', 'local_file', 'automation_hub']
HOST_KEY_CHECKING(/etc/ansible/ansible.cfg) = True
INJECT_FACTS_AS_VARS(/etc/ansible/ansible.cfg) = True
INTERPRETER_PYTHON(/etc/ansible/ansible.cfg) = /usr/bin/python3
INVENTORY_ENABLED(/etc/ansible/ansible.cfg) = ['ini', 'script', 'auto', 'yaml']
PERSISTENT_COMMAND_TIMEOUT(/etc/ansible/ansible.cfg) = 3599
PERSISTENT_CONNECT_RETRY_TIMEOUT(/etc/ansible/ansible.cfg) = 300
PERSISTENT_CONNECT_TIMEOUT(/etc/ansible/ansible.cfg) = 3600
RETRY_FILES_ENABLED(/etc/ansible/ansible.cfg) = False
SHOW_CUSTOM_STATS(/etc/ansible/ansible.cfg) = True
OS / Environment
Ubuntu 24.10
Python 3.12.8
Steps to Reproduce
- name: Validating a TLS Wildcard domain server Certificate from Let’s Encrypt CA - Phase II
acme_certificate:
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
Expected Results
Validated certificate from Let’s Encrypt
Actual Results
The full traceback is:
File "/tmp/ansible_acme_certificate_payload_pequ5udn/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_pequ5udn/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_pequ5udn/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": {
Cf. above
}
},
"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/<value1>/<value2><value3>",
"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/<value1>/<value2>",
"wildcard": true
},
"identifier": "dns:*.sdxlive.com"
}
}
Proof
Everyone can check that the challenge value is available at dns1.sdxlive.com using the public Unbound DNS checker which closely replicates the ISRG setup:
Query results for TXT _acme-challenge.sdxlive.com
Response:
;; opcode: QUERY, status: NOERROR, id: 929
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version 0; flags: do; udp: 1232
;; QUESTION SECTION:
;_acme-challenge.sdxlive.com. IN TXT
;; ANSWER SECTION:
_acme-challenge.sdxlive.com. 0 IN TXT "mUNgiFtYbqntKlRadroTILE5KW9iH98hmTEt1RSV2-I"
_acme-challenge.sdxlive.com. 0 IN TXT "CdBzTL-h79q107qbJ_FkpRCt5kOvhsbyOlKR4E5NIF0"
_acme-challenge.sdxlive.com. 0 IN RRSIG TXT 13 3 60 20250417083537 20250403121937 49706 sdxlive.com. YG5OKU7Kne5KJM6hKYxVB6/HsP2wWBY5v6IqCWXGCBNsdEWV92Sq2qqQBnH85Bzvyr+PPiBD3xsIhIVIy/GUkA==
Code of Conduct
- I agree to follow the Ansible Code of Conduct