Using the official image from dockerhub, have tried both the latest stable and the nightly build with the same result. Created a token via Cloudflare, tested and verified as working both via the provided curl command and using other applications.
Errors out every single time with:
Error determining zone_id: 6003 Invalid request headers. Please confirm that you have supplied valid Cloudflare API credentials. (Did you copy your entire API token/key? To use Cloudflare tokens, you'll need the python package cloudflare>=2.3.1. This certbot is running cloudflare 2.11.7)
Command being run inside the container is:
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /cloudflare.ini \
--agree-tos \
--no-eff-email \
-n \
-m $CLOUDFLARE_EMAIL \
-d $CLOUDFLARE_DOMAIN_LIST
cloudflare.ini:
dns_cloudflare_api_token = $CLOUDFLARE_TOKEN
All environment variables are being correctly set and things work just fine when I switch to a global API key instead of a token.
MANUAL TESTS - Performed via root shell of the container.
Domain: test.snkdev.au
Token (this token was deleted prior to posting): lK9yHZ0wbru_hOcL-tWuWVHbertx6cV_aW4Nfk_z
Verified with:
curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer lK9yHZ0wbru_hOcL-tWuWVHbertx6cV_aW4Nfk_z" \
-H "Content-Type:application/json"
{"result":{"id":"c9e3b094ac6dce9e2df9dd69d0be6226","status":"active"},"success":true,"errors":[],"messages":[{"code":10000,"message":"This API Token is valid and active","type":null}]}
Also tested with Terraform, able to edit DNS entries with that token no problem.
Test cloudflare.ini:
dns_cloudflare_api_token = lK9yHZ0wbru_hOcL-tWuWVHbertx6cV_aW4Nfk_z
Test command:
certbot certonly -v \
--dns-cloudflare \
--dns-cloudflare-credentials /cloudflare.ini \
--agree-tos \
--no-eff-email \
-n \
-m <removed> \
-d test.snkdev.au
Output:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-cloudflare, Installer None
Requesting a certificate for test.snkdev.au
Performing the following challenges:
dns-01 challenge for test.snkdev.au
Cleaning up challenges
Error determining zone_id: 6003 Invalid request headers. Please confirm that you have supplied valid Cloudflare API credentials. (Did you copy your entire API token/key? To use Cloudflare tokens, you'll need the python package cloudflare>=2.3.1. This certbot is running cloudflare 2.11.7)
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
Log file:
2023-09-03 23:43:20,485:DEBUG:certbot._internal.main:certbot version: 2.7.0.dev0
2023-09-03 23:43:20,486:DEBUG:certbot._internal.main:Location of certbot entry point: /usr/local/bin/certbot
2023-09-03 23:43:20,486:DEBUG:certbot._internal.main:Arguments: ['-v', '--dns-cloudflare', '--dns-cloudflare-credentials', '/cloudflare.ini', '--agree-tos', '--no-eff-email', '-n', '-m', '<removed>', '-d', 'test.snkdev.au']
2023-09-03 23:43:20,486:DEBUG:certbot._internal.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#dns-cloudflare,PluginEntryPoint#manual,PluginEntryPoint#null,PluginEntryPoint#standalone,PluginEntryPoint#webroot)
2023-09-03 23:43:20,494:DEBUG:certbot._internal.log:Root logging level set at 20
2023-09-03 23:43:20,494:DEBUG:certbot._internal.plugins.selection:Requested authenticator dns-cloudflare and installer None
2023-09-03 23:43:20,496:DEBUG:certbot._internal.plugins.selection:Single candidate plugin: * dns-cloudflare
Description: Obtain certificates using a DNS TXT record (if you are using Cloudflare for DNS).
Interfaces: Authenticator, Plugin
Entry point: dns-cloudflare = certbot_dns_cloudflare._internal.dns_cloudflare:Authenticator
Initialized: <certbot_dns_cloudflare._internal.dns_cloudflare.Authenticator object at 0x7fd14a6db0d0>
Prep: True
2023-09-03 23:43:20,496:DEBUG:certbot._internal.plugins.selection:Selected authenticator <certbot_dns_cloudflare._internal.dns_cloudflare.Authenticator object at 0x7fd14a6db0d0> and installer None
2023-09-03 23:43:20,496:INFO:certbot._internal.plugins.selection:Plugins selected: Authenticator dns-cloudflare, Installer None
2023-09-03 23:43:20,538:DEBUG:certbot._internal.main:Picked account: <Account(RegistrationResource(body=Registration(key=None, contact=(), agreement=None, status=None, terms_of_service_agreed=None, only_return_existing=None, external_account_binding=None), uri='https://acme-v02.api.letsencrypt.org/acme/acct/1287892266', new_authzr_uri=None, terms_of_service=None), 5c8a8cb1a3c4ffeb4c4d27c24f0e4282, Meta(creation_dt=datetime.datetime(2023, 9, 2, 9, 15, 7, tzinfo=<UTC>), creation_host='d9cec661cda0', register_to_eff=None))>
2023-09-03 23:43:20,539:DEBUG:acme.client:Sending GET request to https://acme-v02.api.letsencrypt.org/directory.
2023-09-03 23:43:20,540:DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org:443
2023-09-03 23:43:21,240:DEBUG:urllib3.connectionpool:https://acme-v02.api.letsencrypt.org:443 "GET /directory HTTP/1.1" 200 752
2023-09-03 23:43:21,241:DEBUG:acme.client:Received response:
HTTP 200
Server: nginx
Date: Sun, 03 Sep 2023 23:43:21 GMT
Content-Type: application/json
Content-Length: 752
Connection: keep-alive
Cache-Control: public, max-age=0, no-cache
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800
{
"keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change",
"meta": {
"caaIdentities": [
"letsencrypt.org"
],
"termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf",
"website": "https://letsencrypt.org"
},
"newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct",
"newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce",
"newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order",
"renewalInfo": "https://acme-v02.api.letsencrypt.org/draft-ietf-acme-ari-01/renewalInfo/",
"revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert",
"zHZqzlugegA": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417"
}
2023-09-03 23:43:21,244:DEBUG:certbot._internal.cert_manager:Renewal conf file /etc/letsencrypt/renewal/snkdev.ad.snk.net.au.conf is broken. Skipping.
2023-09-03 23:43:21,244:DEBUG:certbot._internal.cert_manager:Traceback was:
Traceback (most recent call last):
File "/opt/certbot/src/certbot/certbot/_internal/cert_manager.py", line 437, in _search_lineages
candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
File "/opt/certbot/src/certbot/certbot/_internal/storage.py", line 510, in __init__
self._check_symlinks()
File "/opt/certbot/src/certbot/certbot/_internal/storage.py", line 589, in _check_symlinks
raise errors.CertStorageError(
certbot.errors.CertStorageError: expected /etc/letsencrypt/live/snkdev.ad.snk.net.au/cert.pem to be a symlink
2023-09-03 23:43:21,245:DEBUG:certbot._internal.display.obj:Notifying user: Requesting a certificate for test.snkdev.au
2023-09-03 23:43:21,246:DEBUG:acme.client:Requesting fresh nonce
2023-09-03 23:43:21,246:DEBUG:acme.client:Sending HEAD request to https://acme-v02.api.letsencrypt.org/acme/new-nonce.
2023-09-03 23:43:21,480:DEBUG:urllib3.connectionpool:https://acme-v02.api.letsencrypt.org:443 "HEAD /acme/new-nonce HTTP/1.1" 200 0
2023-09-03 23:43:21,480:DEBUG:acme.client:Received response:
HTTP 200
Server: nginx
Date: Sun, 03 Sep 2023 23:43:21 GMT
Connection: keep-alive
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: 2zuXfv7ZYip0g5KCzgfoGwGfgfLUrs31brMpNuLgUJNEDcHKI-c
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800
2023-09-03 23:43:21,480:DEBUG:acme.client:Storing nonce: 2zuXfv7ZYip0g5KCzgfoGwGfgfLUrs31brMpNuLgUJNEDcHKI-c
2023-09-03 23:43:21,480:DEBUG:acme.client:JWS payload:
b'{\n "identifiers": [\n {\n "type": "dns",\n "value": "test.snkdev.au"\n }\n ]\n}'
2023-09-03 23:43:21,482:DEBUG:acme.client:Sending POST request to https://acme-v02.api.letsencrypt.org/acme/new-order:
{
"protected": "eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2FjY3QvMTI4Nzg5MjI2NiIsICJub25jZSI6ICIyenVYZnY3WllpcDBnNUtDemdmb0d3R2ZnZkxVcnMzMWJyTXBOdUxnVUpORURjSEtJLWMiLCAidXJsIjogImh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL25ldy1vcmRlciJ9",
"signature": "bqryZgUXI0slmBcORG-S8IXsosHA4K5vrDfmjbUZgzNXwT7btrjg2xb8iL72nU37T8YmRfW7NY51hTPsaQveXMs2QLn8VJg2eRK74XsK0YFXOdsyNmnn8qdZ7HL6eTdvoIMLcmS3PDgOgs3r4tQOwfLxdasWhaX6_SC06mZ5goeP4mdD1VyjRJuYYFlWXek1pqYmSMiMDuyL9uWrmZymo_2Ijh8oAw4sRkNt-9KnJt0AQQ6-czeK_1YQqAKqsz7LuOtqWcMpgCYQdUwh-Ib-MOXXGSHHUwu9O9OqLl-wo1si1hKbLjUGrKiGngW0A18FnXPftBo-50bu0xWWNlo5tg",
"payload": "ewogICJpZGVudGlmaWVycyI6IFsKICAgIHsKICAgICAgInR5cGUiOiAiZG5zIiwKICAgICAgInZhbHVlIjogInRlc3Quc25rZGV2LmF1IgogICAgfQogIF0KfQ"
}
2023-09-03 23:43:21,746:DEBUG:urllib3.connectionpool:https://acme-v02.api.letsencrypt.org:443 "POST /acme/new-order HTTP/1.1" 201 340
2023-09-03 23:43:21,746:DEBUG:acme.client:Received response:
HTTP 201
Server: nginx
Date: Sun, 03 Sep 2023 23:43:21 GMT
Content-Type: application/json
Content-Length: 340
Connection: keep-alive
Boulder-Requester: 1287892266
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-v02.api.letsencrypt.org/directory>;rel="index"
Location: https://acme-v02.api.letsencrypt.org/acme/order/1287892266/206011419756
Replay-Nonce: 2zuXfv7ZMvEmXRnHe0BM7FsMZVFwQkg9bWrpqA7QB037dytwJcs
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800
{
"status": "pending",
"expires": "2023-09-10T23:41:01Z",
"identifiers": [
{
"type": "dns",
"value": "test.snkdev.au"
}
],
"authorizations": [
"https://acme-v02.api.letsencrypt.org/acme/authz-v3/261125688126"
],
"finalize": "https://acme-v02.api.letsencrypt.org/acme/finalize/1287892266/206011419756"
}
2023-09-03 23:43:21,747:DEBUG:acme.client:Storing nonce: 2zuXfv7ZMvEmXRnHe0BM7FsMZVFwQkg9bWrpqA7QB037dytwJcs
2023-09-03 23:43:21,747:DEBUG:acme.client:JWS payload:
b''
2023-09-03 23:43:21,748:DEBUG:acme.client:Sending POST request to https://acme-v02.api.letsencrypt.org/acme/authz-v3/261125688126:
{
"protected": "eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2FjY3QvMTI4Nzg5MjI2NiIsICJub25jZSI6ICIyenVYZnY3Wk12RW1YUm5IZTBCTTdGc01aVkZ3UWtnOWJXcnBxQTdRQjAzN2R5dHdKY3MiLCAidXJsIjogImh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2F1dGh6LXYzLzI2MTEyNTY4ODEyNiJ9",
"signature": "IuTJSJb3l7D3VgoW-JoFZ1l9klYV3BNbo0Sznq7Q5keuDj4Rad6l2wSlymus5reshfuU5hQ8fj3IZmOUJOEJBTjNzFTQYN48suoyI1_CTZbsV8BTNNA3tQTNjbe4oo6sReqwGcWekp8442akRNcToN3lDf677vRUL8ZQgjERDcJA1wtGvZLi337dhKl2fMUNSZ_S0Ha_g9sdYcLCdskm1Or02wWuZypfIC49_-F1e6RnyNerIad-FSAMgbiv-1f-vk5cd-_iH8gIhsNOE4jtNP1Y5-NX7kCo881ZoHFWK7DVV4Wlp1p9zh3qogRprzTZlkP7bnEHBv3bXcD-5KdcsA",
"payload": ""
}
2023-09-03 23:43:21,996:DEBUG:urllib3.connectionpool:https://acme-v02.api.letsencrypt.org:443 "POST /acme/authz-v3/261125688126 HTTP/1.1" 200 798
2023-09-03 23:43:21,996:DEBUG:acme.client:Received response:
HTTP 200
Server: nginx
Date: Sun, 03 Sep 2023 23:43:21 GMT
Content-Type: application/json
Content-Length: 798
Connection: keep-alive
Boulder-Requester: 1287892266
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: 2zuXfv7ZzM-5x5QOpD5KZSGgu_I6mLZwYYKT3VPogC7pE5NwyXI
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800
{
"identifier": {
"type": "dns",
"value": "test.snkdev.au"
},
"status": "pending",
"expires": "2023-09-10T23:41:01Z",
"challenges": [
{
"type": "http-01",
"status": "pending",
"url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/261125688126/7pq6hA",
"token": "doVA3B7TmMIGrfmKeRMjyPn73GoljVNBUebnau5qcPQ"
},
{
"type": "dns-01",
"status": "pending",
"url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/261125688126/IIDPMQ",
"token": "doVA3B7TmMIGrfmKeRMjyPn73GoljVNBUebnau5qcPQ"
},
{
"type": "tls-alpn-01",
"status": "pending",
"url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/261125688126/EnKPDQ",
"token": "doVA3B7TmMIGrfmKeRMjyPn73GoljVNBUebnau5qcPQ"
}
]
}
2023-09-03 23:43:21,996:DEBUG:acme.client:Storing nonce: 2zuXfv7ZzM-5x5QOpD5KZSGgu_I6mLZwYYKT3VPogC7pE5NwyXI
2023-09-03 23:43:21,996:INFO:certbot._internal.auth_handler:Performing the following challenges:
2023-09-03 23:43:21,996:INFO:certbot._internal.auth_handler:dns-01 challenge for test.snkdev.au
2023-09-03 23:43:22,000:DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.cloudflare.com:443
2023-09-03 23:43:22,290:DEBUG:urllib3.connectionpool:https://api.cloudflare.com:443 "GET /client/v4/zones?name=test.snkdev.au&per_page=1 HTTP/1.1" 400 None
2023-09-03 23:43:22,291:DEBUG:certbot._internal.error_handler:Encountered exception:
Traceback (most recent call last):
File "/opt/certbot/src/plugin/certbot_dns_cloudflare/_internal/dns_cloudflare.py", line 198, in _find_zone_id
zones = self.cf.zones.get(params=params) # zones | pylint: disable=no-member
File "/usr/local/lib/python3.10/site-packages/CloudFlare/cloudflare.py", line 675, in get
return self._base.do_auth('GET', self._parts, [identifier1, identifier2, identifier3, identifier4], params, data)
File "/usr/local/lib/python3.10/site-packages/CloudFlare/cloudflare.py", line 129, in do_auth
return self._call(method, headers, parts, identifiers, params, data, files)
File "/usr/local/lib/python3.10/site-packages/CloudFlare/cloudflare.py", line 506, in _call
raise CloudFlareAPIError(code, message, error_chain)
CloudFlare.exceptions.CloudFlareAPIError: Invalid request headers
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/opt/certbot/src/certbot/certbot/_internal/auth_handler.py", line 88, in handle_authorizations
resps = self.auth.perform(achalls)
File "/opt/certbot/src/certbot/certbot/plugins/dns_common.py", line 76, in perform
self._perform(domain, validation_domain_name, validation)
File "/opt/certbot/src/plugin/certbot_dns_cloudflare/_internal/dns_cloudflare.py", line 76, in _perform
self._get_cloudflare_client().add_txt_record(domain, validation_name, validation, self.ttl)
File "/opt/certbot/src/plugin/certbot_dns_cloudflare/_internal/dns_cloudflare.py", line 121, in add_txt_record
zone_id = self._find_zone_id(domain)
File "/opt/certbot/src/plugin/certbot_dns_cloudflare/_internal/dns_cloudflare.py", line 215, in _find_zone_id
raise errors.PluginError('Error determining zone_id: {0} {1}. Please confirm '
certbot.errors.PluginError: Error determining zone_id: 6003 Invalid request headers. Please confirm that you have supplied valid Cloudflare API credentials. (Did you copy your entire API token/key? To use Cloudflare tokens, you'll need the python package cloudflare>=2.3.1. This certbot is running cloudflare 2.11.7)
2023-09-03 23:43:22,291:DEBUG:certbot._internal.error_handler:Calling registered functions
2023-09-03 23:43:22,291:INFO:certbot._internal.auth_handler:Cleaning up challenges
2023-09-03 23:43:22,294:DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.cloudflare.com:443
2023-09-03 23:43:22,559:DEBUG:urllib3.connectionpool:https://api.cloudflare.com:443 "GET /client/v4/zones?name=test.snkdev.au&per_page=1 HTTP/1.1" 400 None
2023-09-03 23:43:22,560:DEBUG:certbot_dns_cloudflare._internal.dns_cloudflare:Encountered error finding zone_id during deletion: Error determining zone_id: 6003 Invalid request headers. Please confirm that you have supplied valid Cloudflare API credentials. (Did you copy your entire API token/key? To use Cloudflare tokens, you'll need the python package cloudflare>=2.3.1. This certbot is running cloudflare 2.11.7)
2023-09-03 23:43:22,561:DEBUG:certbot._internal.log:Exiting abnormally:
Traceback (most recent call last):
File "/opt/certbot/src/plugin/certbot_dns_cloudflare/_internal/dns_cloudflare.py", line 198, in _find_zone_id
zones = self.cf.zones.get(params=params) # zones | pylint: disable=no-member
File "/usr/local/lib/python3.10/site-packages/CloudFlare/cloudflare.py", line 675, in get
return self._base.do_auth('GET', self._parts, [identifier1, identifier2, identifier3, identifier4], params, data)
File "/usr/local/lib/python3.10/site-packages/CloudFlare/cloudflare.py", line 129, in do_auth
return self._call(method, headers, parts, identifiers, params, data, files)
File "/usr/local/lib/python3.10/site-packages/CloudFlare/cloudflare.py", line 506, in _call
raise CloudFlareAPIError(code, message, error_chain)
CloudFlare.exceptions.CloudFlareAPIError: Invalid request headers
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/bin/certbot", line 33, in <module>
sys.exit(load_entry_point('certbot', 'console_scripts', 'certbot')())
File "/opt/certbot/src/certbot/certbot/main.py", line 19, in main
return internal_main.main(cli_args)
File "/opt/certbot/src/certbot/certbot/_internal/main.py", line 1873, in main
return config.func(config, plugins)
File "/opt/certbot/src/certbot/certbot/_internal/main.py", line 1600, in certonly
lineage = _get_and_save_cert(le_client, config, domains, certname, lineage)
File "/opt/certbot/src/certbot/certbot/_internal/main.py", line 143, in _get_and_save_cert
lineage = le_client.obtain_and_enroll_certificate(domains, certname)
File "/opt/certbot/src/certbot/certbot/_internal/client.py", line 517, in obtain_and_enroll_certificate
cert, chain, key, _ = self.obtain_certificate(domains)
File "/opt/certbot/src/certbot/certbot/_internal/client.py", line 428, in obtain_certificate
orderr = self._get_order_and_authorizations(csr.data, self.config.allow_subset_of_names)
File "/opt/certbot/src/certbot/certbot/_internal/client.py", line 496, in _get_order_and_authorizations
authzr = self.auth_handler.handle_authorizations(orderr, self.config, best_effort)
File "/opt/certbot/src/certbot/certbot/_internal/auth_handler.py", line 88, in handle_authorizations
resps = self.auth.perform(achalls)
File "/opt/certbot/src/certbot/certbot/plugins/dns_common.py", line 76, in perform
self._perform(domain, validation_domain_name, validation)
File "/opt/certbot/src/plugin/certbot_dns_cloudflare/_internal/dns_cloudflare.py", line 76, in _perform
self._get_cloudflare_client().add_txt_record(domain, validation_name, validation, self.ttl)
File "/opt/certbot/src/plugin/certbot_dns_cloudflare/_internal/dns_cloudflare.py", line 121, in add_txt_record
zone_id = self._find_zone_id(domain)
File "/opt/certbot/src/plugin/certbot_dns_cloudflare/_internal/dns_cloudflare.py", line 215, in _find_zone_id
raise errors.PluginError('Error determining zone_id: {0} {1}. Please confirm '
certbot.errors.PluginError: Error determining zone_id: 6003 Invalid request headers. Please confirm that you have supplied valid Cloudflare API credentials. (Did you copy your entire API token/key? To use Cloudflare tokens, you'll need the python package cloudflare>=2.3.1. This certbot is running cloudflare 2.11.7)
2023-09-03 23:43:22,562:ERROR:certbot._internal.log:Error determining zone_id: 6003 Invalid request headers. Please confirm that you have supplied valid Cloudflare API credentials. (Did you copy your entire API token/key? To use Cloudflare tokens, you'll need the python package cloudflare>=2.3.1. This certbot is running cloudflare 2.11.7)
Any suggestions welcome, I'd much prefer not to need to use a global API key for obvious reasons.