dns-cloudflare: Update docs and error messages to reflect new API permissions (#8015)

* Tweaks for improved Cloudflare API

* Update docs for dns-cloudflare

* Update tests and changelog

* Fix bad merge

* Fix error code for record add

* Improve error message

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
This commit is contained in:
Cameron Steel 2020-06-13 04:38:13 +10:00 committed by GitHub
parent fb39de7d01
commit 961c573864
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 16 deletions

View file

@ -21,8 +21,8 @@ Credentials
-----------
Use of this plugin requires a configuration file containing Cloudflare API
credentials, obtained from your Cloudflare
`account page <https://dash.cloudflare.com/profile/api-tokens>`_.
credentials, obtained from your
`Cloudflare dashboard <https://dash.cloudflare.com/?to=/:account/profile/api-tokens>`_.
Previously, Cloudflare's "Global API Key" was used for authentication, however
this key can access the entire Cloudflare API for all domains in your account,
@ -31,11 +31,8 @@ meaning it could cause a lot of damage if leaked.
Cloudflare's newer API Tokens can be restricted to specific domains and
operations, and are therefore now the recommended authentication option.
However, due to some shortcomings in Cloudflare's implementation of Tokens,
Tokens created for Certbot currently require ``Zone:Zone:Read`` and ``Zone:DNS:Edit``
permissions for **all** zones in your account. While this is not ideal, your Token
will still have fewer permission than the Global key, so it's still worth doing.
Hopefully Cloudflare will improve this in the future.
The Token needed by Certbot requires ``Zone:DNS:Edit`` permissions for only the
zones you need certificates for.
Using Cloudflare Tokens also requires at least version 2.3.1 of the ``cloudflare``
python module. If the version that automatically installed with this plugin is

View file

@ -14,7 +14,7 @@ from certbot.plugins import dns_common
logger = logging.getLogger(__name__)
ACCOUNT_URL = 'https://dash.cloudflare.com/profile/api-tokens'
ACCOUNT_URL = 'https://dash.cloudflare.com/?to=/:account/profile/api-tokens'
@zope.interface.implementer(interfaces.IAuthenticator)
@ -118,7 +118,7 @@ class _CloudflareClient(object):
code = int(e)
hint = None
if code == 9109:
if code == 1009:
hint = 'Does your API token have "Zone:DNS:Edit" permissions?'
logger.error('Encountered CloudFlareAPIError adding TXT record: %d %s', e, e)
@ -210,11 +210,22 @@ class _CloudflareClient(object):
logger.debug('Found zone_id of %s for %s using name %s', zone_id, domain, zone_name)
return zone_id
raise errors.PluginError('Unable to determine zone_id for {0} using zone names: {1}. '
'Please confirm that the domain name has been entered correctly '
'and is already associated with the supplied Cloudflare account.{2}'
.format(domain, zone_name_guesses, ' The error from Cloudflare was:'
' {0} {1}'.format(code, msg) if code is not None else ''))
if msg is not None:
if 'com.cloudflare.api.account.zone.list' in msg:
raise errors.PluginError('Unable to determine zone_id for {0} using zone names: '
'{1}. Please confirm that the domain name has been '
'entered correctly and your Cloudflare Token has access '
'to the domain.'.format(domain, zone_name_guesses))
else:
raise errors.PluginError('Unable to determine zone_id for {0} using zone names: '
'{1}. The error from Cloudflare was: {2} {3}.'
.format(domain, zone_name_guesses, code, msg))
else:
raise errors.PluginError('Unable to determine zone_id for {0} using zone names: '
'{1}. Please confirm that the domain name has been '
'entered correctly and is already associated with the '
'supplied Cloudflare account.'
.format(domain, zone_name_guesses))
def _find_txt_record_id(self, zone_id, record_name, record_content):
"""

View file

@ -133,7 +133,7 @@ class CloudflareClientTest(unittest.TestCase):
def test_add_txt_record_error(self):
self.cf.zones.get.return_value = [{'id': self.zone_id}]
self.cf.zones.dns_records.post.side_effect = CloudFlare.exceptions.CloudFlareAPIError(9109, '', '')
self.cf.zones.dns_records.post.side_effect = CloudFlare.exceptions.CloudFlareAPIError(1009, '', '')
self.assertRaises(
errors.PluginError,
@ -175,6 +175,12 @@ class CloudflareClientTest(unittest.TestCase):
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.cf.zones.get.side_effect = CloudFlare.exceptions.CloudFlareAPIError(0, 'com.cloudflare.api.account.zone.list', '')
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_del_txt_record(self):
self.cf.zones.get.return_value = [{'id': self.zone_id}]
self.cf.zones.dns_records.get.return_value = [{'id': self.record_id}]

View file

@ -23,7 +23,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
### Fixed
*
* Cloudflare API Tokens may now be restricted to individual zones.
More details about these changes can be found on our GitHub repo.