Hello community,
here is the log from the commit of package python-certbot-dns-cloudflare for
openSUSE:Leap:15.2 checked in at 2020-02-27 06:41:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.2/python-certbot-dns-cloudflare (Old)
and /work/SRC/openSUSE:Leap:15.2/.python-certbot-dns-cloudflare.new.26092
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-certbot-dns-cloudflare"
Thu Feb 27 06:41:49 2020 rev:6 rq:779447 version:1.2.0
Changes:
--------
---
/work/SRC/openSUSE:Leap:15.2/python-certbot-dns-cloudflare/python-certbot-dns-cloudflare.changes
2020-02-19 18:48:30.223017941 +0100
+++
/work/SRC/openSUSE:Leap:15.2/.python-certbot-dns-cloudflare.new.26092/python-certbot-dns-cloudflare.changes
2020-02-27 06:41:50.961652849 +0100
@@ -1,0 +2,6 @@
+Fri Feb 21 15:32:45 UTC 2020 - Marketa Calabkova <[email protected]>
+
+- update to version 1.2.0
+ * Added support for Cloudflare's limited-scope API Tokens
+
+-------------------------------------------------------------------
Old:
----
certbot-dns-cloudflare-1.1.0.tar.gz
New:
----
certbot-dns-cloudflare-1.2.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-certbot-dns-cloudflare.spec ++++++
--- /var/tmp/diff_new_pack.AAPEQA/_old 2020-02-27 06:41:51.241653432 +0100
+++ /var/tmp/diff_new_pack.AAPEQA/_new 2020-02-27 06:41:51.241653432 +0100
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-certbot-dns-cloudflare
-Version: 1.1.0
+Version: 1.2.0
Release: 0
Summary: Cloudflare Authenticator plugin for Certbot
License: Apache-2.0
++++++ certbot-dns-cloudflare-1.1.0.tar.gz ->
certbot-dns-cloudflare-1.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-cloudflare-1.1.0/PKG-INFO
new/certbot-dns-cloudflare-1.2.0/PKG-INFO
--- old/certbot-dns-cloudflare-1.1.0/PKG-INFO 2020-01-14 19:41:51.000000000
+0100
+++ new/certbot-dns-cloudflare-1.2.0/PKG-INFO 2020-02-04 22:47:10.000000000
+0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: certbot-dns-cloudflare
-Version: 1.1.0
+Version: 1.2.0
Summary: Cloudflare DNS Authenticator plugin for Certbot
Home-page: https://github.com/certbot/certbot
Author: Certbot Project
@@ -17,7 +17,6 @@
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
@@ -28,5 +27,5 @@
Classifier: Topic :: System :: Networking
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Utilities
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
Provides-Extra: docs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-1.1.0/certbot_dns_cloudflare/__init__.py
new/certbot-dns-cloudflare-1.2.0/certbot_dns_cloudflare/__init__.py
--- old/certbot-dns-cloudflare-1.1.0/certbot_dns_cloudflare/__init__.py
2020-01-14 19:41:31.000000000 +0100
+++ new/certbot-dns-cloudflare-1.2.0/certbot_dns_cloudflare/__init__.py
2020-02-04 22:46:57.000000000 +0100
@@ -22,17 +22,40 @@
Use of this plugin requires a configuration file containing Cloudflare API
credentials, obtained from your Cloudflare
-`account page <https://www.cloudflare.com/a/account/my-account>`_. This plugin
-does not currently support Cloudflare's "API Tokens", so please ensure you use
-the "Global API Key" for authentication.
+`account page <https://dash.cloudflare.com/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,
+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.
+
+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
+older than that, and you can't upgrade it on your system, you'll have to stick
to
+the Global key.
+
+.. code-block:: ini
+ :name: certbot_cloudflare_token.ini
+ :caption: Example credentials file using restricted API Token (recommended):
+
+ # Cloudflare API token used by Certbot
+ dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567
.. code-block:: ini
- :name: credentials.ini
- :caption: Example credentials file:
+ :name: certbot_cloudflare_key.ini
+ :caption: Example credentials file using Global API Key (not recommended):
# Cloudflare API credentials used by Certbot
dns_cloudflare_email = [email protected]
- dns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234567
+ dns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234
The path to this file can be provided interactively or using the
``--dns-cloudflare-credentials`` command-line argument. Certbot records the
path
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-1.1.0/certbot_dns_cloudflare/_internal/dns_cloudflare.py
new/certbot-dns-cloudflare-1.2.0/certbot_dns_cloudflare/_internal/dns_cloudflare.py
---
old/certbot-dns-cloudflare-1.1.0/certbot_dns_cloudflare/_internal/dns_cloudflare.py
2020-01-14 19:41:31.000000000 +0100
+++
new/certbot-dns-cloudflare-1.2.0/certbot_dns_cloudflare/_internal/dns_cloudflare.py
2020-02-04 22:46:57.000000000 +0100
@@ -4,6 +4,10 @@
import CloudFlare
import zope.interface
+from acme.magic_typing import Any
+from acme.magic_typing import Dict
+from acme.magic_typing import List
+
from certbot import errors
from certbot import interfaces
from certbot.plugins import dns_common
@@ -38,14 +42,35 @@
return 'This plugin configures a DNS TXT record to respond to a dns-01
challenge using ' + \
'the Cloudflare API.'
+ def _validate_credentials(self, credentials):
+ token = credentials.conf('api-token')
+ email = credentials.conf('email')
+ key = credentials.conf('api-key')
+ if token:
+ if email or key:
+ raise errors.PluginError('{}: dns_cloudflare_email and
dns_cloudflare_api_key are '
+ 'not needed when using an API Token'
+ .format(credentials.confobj.filename))
+ elif email or key:
+ if not email:
+ raise errors.PluginError('{}: dns_cloudflare_email is required
when using a Global '
+ 'API Key. (should be email address
associated with '
+ 'Cloudflare
account)'.format(credentials.confobj.filename))
+ if not key:
+ raise errors.PluginError('{}: dns_cloudflare_api_key is
required when using a '
+ 'Global API Key. (see {})'
+ .format(credentials.confobj.filename,
ACCOUNT_URL))
+ else:
+ raise errors.PluginError('{}: Either dns_cloudflare_api_token
(recommended), or '
+ 'dns_cloudflare_email and
dns_cloudflare_api_key are required.'
+ ' (see
{})'.format(credentials.confobj.filename, ACCOUNT_URL))
+
def _setup_credentials(self):
self.credentials = self._configure_credentials(
'credentials',
'Cloudflare credentials INI file',
- {
- 'email': 'email address associated with Cloudflare account',
- 'api-key': 'API key for Cloudflare account, obtained from
{0}'.format(ACCOUNT_URL)
- }
+ None,
+ self._validate_credentials
)
def _perform(self, domain, validation_name, validation):
@@ -55,6 +80,8 @@
self._get_cloudflare_client().del_txt_record(domain, validation_name,
validation)
def _get_cloudflare_client(self):
+ if self.credentials.conf('api-token'):
+ return _CloudflareClient(None, self.credentials.conf('api-token'))
return _CloudflareClient(self.credentials.conf('email'),
self.credentials.conf('api-key'))
@@ -88,8 +115,15 @@
logger.debug('Attempting to add record to zone %s: %s', zone_id,
data)
self.cf.zones.dns_records.post(zone_id, data=data) # zones |
pylint: disable=no-member
except CloudFlare.exceptions.CloudFlareAPIError as e:
+ code = int(e)
+ hint = None
+
+ if code == 9109:
+ hint = 'Does your API token have "Zone:DNS:Edit" permissions?'
+
logger.error('Encountered CloudFlareAPIError adding TXT record: %d
%s', e, e)
- raise errors.PluginError('Error communicating with the Cloudflare
API: {0}'.format(e))
+ raise errors.PluginError('Error communicating with the Cloudflare
API: {0}{1}'
+ .format(e, ' ({0})'.format(hint) if hint
else ''))
record_id = self._find_txt_record_id(zone_id, record_name,
record_content)
logger.debug('Successfully added TXT record with record_id: %s',
record_id)
@@ -139,6 +173,8 @@
"""
zone_name_guesses = dns_common.base_domain_name_guesses(domain)
+ zones = [] # type: List[Dict[str, Any]]
+ code = msg = None
for zone_name in zone_name_guesses:
params = {'name': zone_name,
@@ -148,16 +184,26 @@
zones = self.cf.zones.get(params=params) # zones | pylint:
disable=no-member
except CloudFlare.exceptions.CloudFlareAPIError as e:
code = int(e)
+ msg = str(e)
hint = None
if code == 6003:
- hint = 'Did you copy your entire API key?'
+ hint = ('Did you copy your entire API token/key? To use
Cloudflare tokens, '
+ 'you\'ll need the python package
cloudflare>=2.3.1.{}'
+ .format(' This certbot is running cloudflare ' +
str(CloudFlare.__version__)
+ if hasattr(CloudFlare, '__version__') else ''))
elif code == 9103:
- hint = 'Did you enter the correct email address?'
-
- raise errors.PluginError('Error determining zone_id: {0} {1}.
Please confirm that '
- 'you have supplied valid Cloudflare
API credentials.{2}'
- .format(code, e, '
({0})'.format(hint) if hint else ''))
+ hint = 'Did you enter the correct email address and Global
key?'
+ elif code == 9109:
+ hint = 'Did you enter a valid Cloudflare Token?'
+
+ if hint:
+ raise errors.PluginError('Error determining zone_id: {0}
{1}. Please confirm '
+ 'that you have supplied valid Cloudflare API
credentials. ({2})'
+
.format(code, msg, hint))
+ else:
+ logger.debug('Unrecognised CloudFlareAPIError while
finding zone_id: %d %s. '
+ 'Continuing with next zone guess...', e, e)
if zones:
zone_id = zones[0]['id']
@@ -165,9 +211,10 @@
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.'
- .format(domain, zone_name_guesses))
+ '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 ''))
def _find_txt_record_id(self, zone_id, record_name, record_content):
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-1.1.0/certbot_dns_cloudflare.egg-info/PKG-INFO
new/certbot-dns-cloudflare-1.2.0/certbot_dns_cloudflare.egg-info/PKG-INFO
--- old/certbot-dns-cloudflare-1.1.0/certbot_dns_cloudflare.egg-info/PKG-INFO
2020-01-14 19:41:51.000000000 +0100
+++ new/certbot-dns-cloudflare-1.2.0/certbot_dns_cloudflare.egg-info/PKG-INFO
2020-02-04 22:47:10.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: certbot-dns-cloudflare
-Version: 1.1.0
+Version: 1.2.0
Summary: Cloudflare DNS Authenticator plugin for Certbot
Home-page: https://github.com/certbot/certbot
Author: Certbot Project
@@ -17,7 +17,6 @@
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
@@ -28,5 +27,5 @@
Classifier: Topic :: System :: Networking
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Utilities
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
Provides-Extra: docs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-1.1.0/certbot_dns_cloudflare.egg-info/requires.txt
new/certbot-dns-cloudflare-1.2.0/certbot_dns_cloudflare.egg-info/requires.txt
---
old/certbot-dns-cloudflare-1.1.0/certbot_dns_cloudflare.egg-info/requires.txt
2020-01-14 19:41:51.000000000 +0100
+++
new/certbot-dns-cloudflare-1.2.0/certbot_dns_cloudflare.egg-info/requires.txt
2020-02-04 22:47:10.000000000 +0100
@@ -1,5 +1,5 @@
acme>=0.29.0
-certbot>=1.0.0
+certbot>=1.1.0
cloudflare>=1.5.1
mock
setuptools
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-cloudflare-1.1.0/setup.py
new/certbot-dns-cloudflare-1.2.0/setup.py
--- old/certbot-dns-cloudflare-1.1.0/setup.py 2020-01-14 19:41:33.000000000
+0100
+++ new/certbot-dns-cloudflare-1.2.0/setup.py 2020-02-04 22:46:58.000000000
+0100
@@ -4,13 +4,13 @@
from setuptools import setup
from setuptools.command.test import test as TestCommand
-version = '1.1.0'
+version = '1.2.0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
install_requires = [
'acme>=0.29.0',
- 'certbot>=1.0.0',
+ 'certbot>=1.1.0',
'cloudflare>=1.5.1',
'mock',
'setuptools',
@@ -44,7 +44,7 @@
author="Certbot Project",
author_email='[email protected]',
license='Apache License 2.0',
- python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
+ python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@@ -55,7 +55,6 @@
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-1.1.0/tests/dns_cloudflare_test.py
new/certbot-dns-cloudflare-1.2.0/tests/dns_cloudflare_test.py
--- old/certbot-dns-cloudflare-1.1.0/tests/dns_cloudflare_test.py
2020-01-14 19:41:31.000000000 +0100
+++ new/certbot-dns-cloudflare-1.2.0/tests/dns_cloudflare_test.py
2020-02-04 22:46:57.000000000 +0100
@@ -12,6 +12,9 @@
from certbot.tests import util as test_util
API_ERROR = CloudFlare.exceptions.CloudFlareAPIError(1000, '', '')
+
+API_TOKEN = 'an-api-token'
+
API_KEY = 'an-api-key'
EMAIL = '[email protected]'
@@ -49,6 +52,50 @@
expected = [mock.call.del_txt_record(DOMAIN,
'_acme-challenge.'+DOMAIN, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
+ def test_api_token(self):
+ dns_test_common.write({"cloudflare_api_token": API_TOKEN},
+ self.config.cloudflare_credentials)
+ self.auth.perform([self.achall])
+
+ expected = [mock.call.add_txt_record(DOMAIN,
'_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)]
+ self.assertEqual(expected, self.mock_client.mock_calls)
+
+ def test_no_creds(self):
+ dns_test_common.write({}, self.config.cloudflare_credentials)
+ self.assertRaises(errors.PluginError,
+ self.auth.perform,
+ [self.achall])
+
+ def test_missing_email_or_key(self):
+ dns_test_common.write({"cloudflare_api_key": API_KEY},
self.config.cloudflare_credentials)
+ self.assertRaises(errors.PluginError,
+ self.auth.perform,
+ [self.achall])
+
+ dns_test_common.write({"cloudflare_email": EMAIL},
self.config.cloudflare_credentials)
+ self.assertRaises(errors.PluginError,
+ self.auth.perform,
+ [self.achall])
+
+ def test_email_or_key_with_token(self):
+ dns_test_common.write({"cloudflare_api_token": API_TOKEN,
"cloudflare_email": EMAIL},
+ self.config.cloudflare_credentials)
+ self.assertRaises(errors.PluginError,
+ self.auth.perform,
+ [self.achall])
+
+ dns_test_common.write({"cloudflare_api_token": API_TOKEN,
"cloudflare_api_key": API_KEY},
+ self.config.cloudflare_credentials)
+ self.assertRaises(errors.PluginError,
+ self.auth.perform,
+ [self.achall])
+
+ dns_test_common.write({"cloudflare_api_token": API_TOKEN,
"cloudflare_email": EMAIL,
+ "cloudflare_api_key": API_KEY},
self.config.cloudflare_credentials)
+ self.assertRaises(errors.PluginError,
+ self.auth.perform,
+ [self.achall])
+
class CloudflareClientTest(unittest.TestCase):
record_name = "foo"
@@ -83,7 +130,7 @@
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 = API_ERROR
+ self.cf.zones.dns_records.post.side_effect =
CloudFlare.exceptions.CloudFlareAPIError(9109, '', '')
self.assertRaises(
errors.PluginError,
@@ -104,6 +151,25 @@
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
+ DOMAIN, self.record_name, self.record_content, self.record_ttl)
+
+ def test_add_txt_record_bad_creds(self):
+ self.cf.zones.get.side_effect =
CloudFlare.exceptions.CloudFlareAPIError(6003, '', '')
+ self.assertRaises(
+ errors.PluginError,
+ 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(9103, '', '')
+ self.assertRaises(
+ errors.PluginError,
+ 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(9109, '', '')
+ 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):