From 76895457c9f5956ec97ccadc3e0d7e894db17094 Mon Sep 17 00:00:00 2001 From: alexzorin Date: Wed, 10 Mar 2021 10:56:51 +1100 Subject: [PATCH] dns-digitalocean: use a 30 second TTL (#8693) Fixes an issue with long TTLs and caching behavior on DigitalOcean's DNS hosting service. --- .../_internal/dns_digitalocean.py | 13 +++++++++---- certbot-dns-digitalocean/setup.py | 2 +- .../tests/dns_digitalocean_test.py | 15 +++++++++------ certbot/CHANGELOG.md | 1 + tools/dev_constraints.txt | 3 ++- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/certbot-dns-digitalocean/certbot_dns_digitalocean/_internal/dns_digitalocean.py b/certbot-dns-digitalocean/certbot_dns_digitalocean/_internal/dns_digitalocean.py index 5893df9c2..cb5012fb7 100644 --- a/certbot-dns-digitalocean/certbot_dns_digitalocean/_internal/dns_digitalocean.py +++ b/certbot-dns-digitalocean/certbot_dns_digitalocean/_internal/dns_digitalocean.py @@ -21,6 +21,7 @@ class Authenticator(dns_common.DNSAuthenticator): description = 'Obtain certificates using a DNS TXT record (if you are ' + \ 'using DigitalOcean for DNS).' + ttl = 30 def __init__(self, *args, **kwargs): super(Authenticator, self).__init__(*args, **kwargs) @@ -45,7 +46,8 @@ class Authenticator(dns_common.DNSAuthenticator): ) def _perform(self, domain, validation_name, validation): - self._get_digitalocean_client().add_txt_record(domain, validation_name, validation) + self._get_digitalocean_client().add_txt_record(domain, validation_name, validation, + self.ttl) def _cleanup(self, domain, validation_name, validation): self._get_digitalocean_client().del_txt_record(domain, validation_name, validation) @@ -62,13 +64,15 @@ class _DigitalOceanClient: def __init__(self, token): self.manager = digitalocean.Manager(token=token) - def add_txt_record(self, domain_name, record_name, record_content): + def add_txt_record(self, domain_name: str, record_name: str, record_content: str, + record_ttl: int): """ Add a TXT record using the supplied information. :param str domain_name: The domain to use to associate the record with. :param str record_name: The record name (typically beginning with '_acme-challenge.'). :param str record_content: The record content (typically the challenge validation). + :param int record_ttl: The record TTL. :raises certbot.errors.PluginError: if an error occurs communicating with the DigitalOcean API """ @@ -89,7 +93,8 @@ class _DigitalOceanClient: result = domain.create_new_domain_record( type='TXT', name=self._compute_record_name(domain, record_name), - data=record_content) + data=record_content, + ttl=record_ttl) # ttl kwarg is only effective starting python-digitalocean 1.15.0 record_id = result['domain_record']['id'] @@ -99,7 +104,7 @@ class _DigitalOceanClient: raise errors.PluginError('Error adding TXT record using the DigitalOcean API: {0}' .format(e)) - def del_txt_record(self, domain_name, record_name, record_content): + def del_txt_record(self, domain_name: str, record_name: str, record_content: str): """ Delete a TXT record using the supplied information. diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index 763961e79..106eb3cac 100644 --- a/certbot-dns-digitalocean/setup.py +++ b/certbot-dns-digitalocean/setup.py @@ -9,7 +9,7 @@ version = '1.14.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ - 'python-digitalocean>=1.11', + 'python-digitalocean>=1.11', # 1.15.0 or newer is recommended for TTL support 'setuptools>=39.0.1', 'zope.interface', ] diff --git a/certbot-dns-digitalocean/tests/dns_digitalocean_test.py b/certbot-dns-digitalocean/tests/dns_digitalocean_test.py index a752f52d0..84bb35b1f 100644 --- a/certbot-dns-digitalocean/tests/dns_digitalocean_test.py +++ b/certbot-dns-digitalocean/tests/dns_digitalocean_test.py @@ -40,7 +40,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic def test_perform(self): self.auth.perform([self.achall]) - expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY)] + expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, 30)] self.assertEqual(expected, self.mock_client.mock_calls) def test_cleanup(self): @@ -58,6 +58,7 @@ class DigitalOceanClientTest(unittest.TestCase): record_prefix = "_acme-challenge" record_name = record_prefix + "." + DOMAIN record_content = "bar" + record_ttl = 60 def setUp(self): from certbot_dns_digitalocean._internal.dns_digitalocean import _DigitalOceanClient @@ -78,25 +79,27 @@ class DigitalOceanClientTest(unittest.TestCase): self.manager.get_all_domains.return_value = [wrong_domain_mock, domain_mock] - self.digitalocean_client.add_txt_record(DOMAIN, self.record_name, self.record_content) + self.digitalocean_client.add_txt_record(DOMAIN, self.record_name, self.record_content, + self.record_ttl) domain_mock.create_new_domain_record.assert_called_with(type='TXT', name=self.record_prefix, - data=self.record_content) + data=self.record_content, + ttl=self.record_ttl) def test_add_txt_record_fail_to_find_domain(self): self.manager.get_all_domains.return_value = [] self.assertRaises(errors.PluginError, self.digitalocean_client.add_txt_record, - DOMAIN, self.record_name, self.record_content) + DOMAIN, self.record_name, self.record_content, self.record_ttl) def test_add_txt_record_error_finding_domain(self): self.manager.get_all_domains.side_effect = API_ERROR self.assertRaises(errors.PluginError, self.digitalocean_client.add_txt_record, - DOMAIN, self.record_name, self.record_content) + DOMAIN, self.record_name, self.record_content, self.record_ttl) def test_add_txt_record_error_creating_record(self): domain_mock = mock.MagicMock() @@ -107,7 +110,7 @@ class DigitalOceanClientTest(unittest.TestCase): self.assertRaises(errors.PluginError, self.digitalocean_client.add_txt_record, - DOMAIN, self.record_name, self.record_content) + DOMAIN, self.record_name, self.record_content, self.record_ttl) def test_del_txt_record(self): first_record_mock = mock.MagicMock() diff --git a/certbot/CHANGELOG.md b/certbot/CHANGELOG.md index c316ec376..ccbe6a43b 100644 --- a/certbot/CHANGELOG.md +++ b/certbot/CHANGELOG.md @@ -11,6 +11,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). ### Changed * certbot-auto no longer checks for updates on any operating system. +* The DigitalOcean plugin now creates TXT records for the DNS-01 challenge with a lower 30s TTL. ### Fixed diff --git a/tools/dev_constraints.txt b/tools/dev_constraints.txt index 10308bd39..f4059a3f9 100644 --- a/tools/dev_constraints.txt +++ b/tools/dev_constraints.txt @@ -51,6 +51,7 @@ jedi==0.17.1 Jinja2==2.9.6 jmespath==0.9.4 josepy==1.1.0 +jsonpickle==2.0.0 jsonschema==2.6.0 lazy-object-proxy==1.4.3 logger==1.4 @@ -93,7 +94,7 @@ pytest-xdist==1.22.5 pytest-sugar==0.9.2 pytest-rerunfailures==4.2 python-dateutil==2.8.1 -python-digitalocean==1.11 +python-digitalocean==1.15.0 python-dotenv==0.14.0 pywin32==300 PyYAML==5.3.1