Certbot/dns-cloudflare docker image not accepting token

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.

Please tell me that's not what I think it is.
If so, you need to change it immediately.

2 Likes

Please tell me that's not what I think it is.
If so, you need to change it immediately.

If you think it's a cloudflare token then yes that is what you think it is!

But as I noted in my original post it was deleted immediately after testing and prior to making the post :wink:

1 Like

OK, I missed that [TL;DR-it all]

What version of certbot are you using?

2 Likes

What version of certbot are you using?

I've tried with 2.6.0 (version in stable container) and 2.7.0.dev0 which is included in the nightly build. Same behaviour in both.

Edit: Cloudflare plugin is v2.11.1 for stable and v2.11.7 for the nightly

@certbot-devs, is there anyone supporting dns-cloudflare at the moment? Would you want bug reports on Issues · certbot/certbot · GitHub for dns-cloudflare issues?

(I think this is potentially at least a UI bug in dns-cloudflare if there's a more substantive underlying Cloudflare API problem, because the error message isn't very specific.)

3 Likes

Does it work with --staging?
Does it work with any other ACME client?

3 Likes

--staging fails with the same error.

Ran up a VM and installed certbot via snap using the same token, that works just fine.

2 Likes

Docker is probably messing the environment variables up.

1 Like

No I've jumped into the container and verified the variables were right. I've also run the commands manually with the values hardcoded, all the same error when using the token.

Plus I used the same env variables to the API key/email which worked no problems.

1 Like

Please explain more about where, and how, the variables are set and where they are used.
Can you test [like show/print/write to file] the variables to be sure they are as you expect [where you expect them]?

3 Likes

They're environment variables passed into the container which are then referenced by the entrypoint script, as per below:

#!/bin/sh

# Create the authentication .ini
echo "dns_cloudflare_api_token = $CLOUDFLARE_TOKEN" > /cloudflare.ini
chmod 600 /cloudflare.ini

# Run certbot
certbot certonly \
    --dns-cloudflare \
    --dns-cloudflare-credentials /cloudflare.ini \
    --dns-cloudflare-propagation-seconds 60 \
    --agree-tos \
    --no-eff-email \
    --staging \
    -n \
    -m $CLOUDFLARE_EMAIL \
    -d $CLOUDFLARE_DOMAIN_LIST

I can then open the cloudflare.ini file and check it is correct.

I've tested all of them by opening a root shell into the container and echoing out the variables, they are all correct. In testing I have also manually created the cloudflare.ini file and put the token in myself, then run the command with the values entered manually with the same result.

Also worth noting that the following script works just fine (with the API key passed in as $CLOUDFLARE_TOKEN):


# Create the authentication .ini
echo "dns_cloudflare_api_key = $CLOUDFLARE_TOKEN" > /cloudflare.ini
echo "dns_cloudflare_email = $CLOUDFLARE_EMAIL" >> /cloudflare.ini
chmod 600 /cloudflare.ini

# Run certbot
certbot certonly \
    --dns-cloudflare \
    --dns-cloudflare-credentials /cloudflare.ini \
    --dns-cloudflare-propagation-seconds 60 \
    --agree-tos \
    --no-eff-email \
    --staging \
    -n \
    -m $CLOUDFLARE_EMAIL \
    -d $CLOUDFLARE_DOMAIN_LIST

So it's definitely not the environment variables, how they're passed in or being read.

@snk-nick You seem very skilled but this looks suspiciously like an older cloudflare plug-in. Or, the wrong format for the cloudflare ini in the container.

Could you possibly have a pip or other version of certbot in that container? Or pointing to an older format ini file for the certbot command?

See this link or this one

I don't have any new ideas to debug the certbot issue. But, other ACME clients support limited scope tokens such as acme.sh (github)

UPDATE:
We cross-posted but I think your cloudlfare.ini format is wrong per one of the links above (this one)

Update2: You showed the correct format for the limited scope token in your first post. The one above was from your post #13

4 Likes

But the difference between your VM+snap experiment, which worked, and the non-working situation is Docker here. Snap uses 2.6.0 and you're currently using the nightly builds, which effectively makes the Cloudflare plugin identical, as there has not been any change to the plugin since those versions.

What's your Dockerfile?

1 Like

Don't be so quick to rule out "timing issues".
You pass a variable, then write a file, then read that file.
I'd suggest that you test pausing between the writing and reading of that file OR remove the need to use the disk at all.

2 Likes

@Osiris @rg305 Please review the two updates to my post #14

It almost certainly is the format of their cloudflare.ini file. A limited scope token requires a different format than they showed in their post #13

4 Likes

I concur.
But I'm just not a fan of:

So, I had to point that out - LOL

3 Likes

The one you're referring to there was a test I used to confirm the issue was the token, I swapped the ini file format out for the one required for the global API key, passed that in via the $CLOUDFLARE_TOKEN variable and it worked fine, confirming that the variables are being passed in without issue and that the .ini is being correctly written then read.

When trying to use the token the correct ini format has been used.

1 Like

? ? ? ?

2 Likes

I'm away from my development machine now, I'll post that tomorrow. But it's just a simple compose entry pulling the container, mounting the entry point script and passing in the variables. It works with the Global API key no problems.