mirror of
https://github.com/certbot/certbot.git
synced 2026-07-30 16:14:44 +02:00
Fix ACMEv2 issues (#5612)
* Add post wrapper to automatically add acme_version * Add uri to authzr. * Only add kid when account is set. * Add content_type when downloading certificate. * Only save new_authz URL when it exists. * Handle combinations in ACMEv1 and ACMEv2. * Add tests for ACMEv2 "combinations".
This commit is contained in:
+11
-6
@@ -223,12 +223,17 @@ class AccountFileStorage(interfaces.AccountStorage):
|
||||
try:
|
||||
with open(self._regr_path(account_dir_path), "w") as regr_file:
|
||||
regr = account.regr
|
||||
with_uri = RegistrationResourceWithNewAuthzrURI(
|
||||
new_authzr_uri=acme.directory.new_authz,
|
||||
body=regr.body,
|
||||
uri=regr.uri,
|
||||
terms_of_service=regr.terms_of_service)
|
||||
regr_file.write(with_uri.json_dumps())
|
||||
# If we have a value for new-authz, save it for forwards
|
||||
# compatibility with older versions of Certbot. If we don't
|
||||
# have a value for new-authz, this is an ACMEv2 directory where
|
||||
# an older version of Certbot won't work anyway.
|
||||
if hasattr(acme.directory, "new-authz"):
|
||||
regr = RegistrationResourceWithNewAuthzrURI(
|
||||
new_authzr_uri=acme.directory.new_authz,
|
||||
body=regr.body,
|
||||
uri=regr.uri,
|
||||
terms_of_service=regr.terms_of_service)
|
||||
regr_file.write(regr.json_dumps())
|
||||
if not regr_only:
|
||||
with util.safe_open(self._key_path(account_dir_path),
|
||||
"w", chmod=0o400) as key_file:
|
||||
|
||||
@@ -24,7 +24,7 @@ class AuthHandler(object):
|
||||
:class:`~acme.challenges.Challenge` types
|
||||
:type auth: :class:`certbot.interfaces.IAuthenticator`
|
||||
|
||||
:ivar acme.client.Client acme: ACME client API.
|
||||
:ivar acme.client.BackwardsCompatibleClientV2 acme: ACME client API.
|
||||
|
||||
:ivar account: Client's Account
|
||||
:type account: :class:`certbot.account.Account`
|
||||
@@ -100,10 +100,16 @@ class AuthHandler(object):
|
||||
"""Retrieve necessary challenges to satisfy server."""
|
||||
logger.info("Performing the following challenges:")
|
||||
for dom in domains:
|
||||
dom_challenges = self.authzr[dom].body.challenges
|
||||
if self.acme.acme_version == 1:
|
||||
combinations = self.authzr[dom].body.combinations
|
||||
else:
|
||||
combinations = tuple((i,) for i in range(len(dom_challenges)))
|
||||
|
||||
path = gen_challenge_path(
|
||||
self.authzr[dom].body.challenges,
|
||||
dom_challenges,
|
||||
self._get_chall_pref(dom),
|
||||
self.authzr[dom].body.combinations)
|
||||
combinations)
|
||||
|
||||
dom_achalls = self._challenge_factory(
|
||||
dom, path)
|
||||
|
||||
+2
-2
@@ -212,8 +212,8 @@ class Client(object):
|
||||
:ivar .IAuthenticator auth: Prepared (`.IAuthenticator.prepare`)
|
||||
authenticator that can solve ACME challenges.
|
||||
:ivar .IInstaller installer: Installer.
|
||||
:ivar acme.client.Client acme: Optional ACME client API handle.
|
||||
You might already have one from `register`.
|
||||
:ivar acme.client.BackwardsCompatibleClientV2 acme: Optional ACME
|
||||
client API handle. You might already have one from `register`.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
||||
|
||||
self.mock_account = mock.Mock(key=util.Key("file_path", "PEM"))
|
||||
self.mock_net = mock.MagicMock(spec=acme_client.Client)
|
||||
self.mock_net.acme_version = 1
|
||||
|
||||
self.handler = AuthHandler(
|
||||
self.mock_auth, self.mock_net, self.mock_account, [])
|
||||
@@ -90,13 +91,13 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
@mock.patch("certbot.auth_handler.AuthHandler._poll_challenges")
|
||||
def test_name1_tls_sni_01_1(self, mock_poll):
|
||||
mock_poll.side_effect = self._validate_all
|
||||
|
||||
authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)
|
||||
def _test_name1_tls_sni_01_1_common(self, combos):
|
||||
authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, combos=combos)
|
||||
mock_order = mock.MagicMock(authorizations=[authzr])
|
||||
authzr = self.handler.handle_authorizations(mock_order)
|
||||
|
||||
with mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") as mock_poll:
|
||||
mock_poll.side_effect = self._validate_all
|
||||
authzr = self.handler.handle_authorizations(mock_order)
|
||||
|
||||
self.assertEqual(self.mock_net.answer_challenge.call_count, 1)
|
||||
|
||||
@@ -112,8 +113,15 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(len(authzr), 1)
|
||||
|
||||
def test_name1_tls_sni_01_1_acme_1(self):
|
||||
self._test_name1_tls_sni_01_1_common(combos=True)
|
||||
|
||||
def test_name1_tls_sni_01_1_acme_2(self):
|
||||
self.mock_net.acme_version = 2
|
||||
self._test_name1_tls_sni_01_1_common(combos=False)
|
||||
|
||||
@mock.patch("certbot.auth_handler.AuthHandler._poll_challenges")
|
||||
def test_name1_tls_sni_01_1_http_01_1_dns_1(self, mock_poll):
|
||||
def test_name1_tls_sni_01_1_http_01_1_dns_1_acme_1(self, mock_poll):
|
||||
mock_poll.side_effect = self._validate_all
|
||||
self.mock_auth.get_chall_pref.return_value.append(challenges.HTTP01)
|
||||
self.mock_auth.get_chall_pref.return_value.append(challenges.DNS01)
|
||||
@@ -138,17 +146,43 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
||||
self.assertEqual(len(authzr), 1)
|
||||
|
||||
@mock.patch("certbot.auth_handler.AuthHandler._poll_challenges")
|
||||
def test_name3_tls_sni_01_3(self, mock_poll):
|
||||
self.mock_net.request_domain_challenges.side_effect = functools.partial(
|
||||
gen_dom_authzr, challs=acme_util.CHALLENGES)
|
||||
|
||||
def test_name1_tls_sni_01_1_http_01_1_dns_1_acme_2(self, mock_poll):
|
||||
self.mock_net.acme_version = 2
|
||||
mock_poll.side_effect = self._validate_all
|
||||
self.mock_auth.get_chall_pref.return_value.append(challenges.HTTP01)
|
||||
self.mock_auth.get_chall_pref.return_value.append(challenges.DNS01)
|
||||
|
||||
authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, combos=False)
|
||||
mock_order = mock.MagicMock(authorizations=[authzr])
|
||||
authzr = self.handler.handle_authorizations(mock_order)
|
||||
|
||||
self.assertEqual(self.mock_net.answer_challenge.call_count, 1)
|
||||
|
||||
self.assertEqual(mock_poll.call_count, 1)
|
||||
chall_update = mock_poll.call_args[0][0]
|
||||
self.assertEqual(list(six.iterkeys(chall_update)), ["0"])
|
||||
self.assertEqual(len(chall_update.values()), 1)
|
||||
|
||||
self.assertEqual(self.mock_auth.cleanup.call_count, 1)
|
||||
cleaned_up_achalls = self.mock_auth.cleanup.call_args[0][0]
|
||||
self.assertEqual(len(cleaned_up_achalls), 1)
|
||||
self.assertEqual(cleaned_up_achalls[0].typ, "tls-sni-01")
|
||||
|
||||
# Length of authorizations list
|
||||
self.assertEqual(len(authzr), 1)
|
||||
|
||||
def _test_name3_tls_sni_01_3_common(self, combos):
|
||||
self.mock_net.request_domain_challenges.side_effect = functools.partial(
|
||||
gen_dom_authzr, challs=acme_util.CHALLENGES, combos=combos)
|
||||
|
||||
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES),
|
||||
gen_dom_authzr(domain="1", challs=acme_util.CHALLENGES),
|
||||
gen_dom_authzr(domain="2", challs=acme_util.CHALLENGES)]
|
||||
mock_order = mock.MagicMock(authorizations=authzrs)
|
||||
authzr = self.handler.handle_authorizations(mock_order)
|
||||
with mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") as mock_poll:
|
||||
mock_poll.side_effect = self._validate_all
|
||||
authzr = self.handler.handle_authorizations(mock_order)
|
||||
|
||||
self.assertEqual(self.mock_net.answer_challenge.call_count, 3)
|
||||
|
||||
@@ -167,6 +201,13 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(len(authzr), 3)
|
||||
|
||||
def test_name3_tls_sni_01_3_common_acme_1(self):
|
||||
self._test_name3_tls_sni_01_3_common(combos=True)
|
||||
|
||||
def test_name3_tls_sni_01_3_common_acme_2(self):
|
||||
self.mock_net.acme_version = 2
|
||||
self._test_name3_tls_sni_01_3_common(combos=False)
|
||||
|
||||
@mock.patch("certbot.auth_handler.AuthHandler._poll_challenges")
|
||||
def test_debug_challenges(self, mock_poll):
|
||||
zope.component.provideUtility(
|
||||
@@ -194,30 +235,44 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
||||
mock_order = mock.MagicMock(authorizations=[])
|
||||
self.assertRaises(errors.AuthorizationError, self.handler.handle_authorizations, mock_order)
|
||||
|
||||
@mock.patch("certbot.auth_handler.AuthHandler._poll_challenges")
|
||||
def test_preferred_challenge_choice(self, mock_poll):
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
|
||||
def _test_preferred_challenge_choice_common(self, combos):
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, combos=combos)]
|
||||
mock_order = mock.MagicMock(authorizations=authzrs)
|
||||
|
||||
mock_poll.side_effect = self._validate_all
|
||||
self.mock_auth.get_chall_pref.return_value.append(challenges.HTTP01)
|
||||
|
||||
self.handler.pref_challs.extend((challenges.HTTP01.typ,
|
||||
challenges.DNS01.typ,))
|
||||
|
||||
self.handler.handle_authorizations(mock_order)
|
||||
with mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") as mock_poll:
|
||||
mock_poll.side_effect = self._validate_all
|
||||
self.handler.handle_authorizations(mock_order)
|
||||
|
||||
self.assertEqual(self.mock_auth.cleanup.call_count, 1)
|
||||
self.assertEqual(
|
||||
self.mock_auth.cleanup.call_args[0][0][0].typ, "http-01")
|
||||
|
||||
def test_preferred_challenges_not_supported(self):
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
|
||||
def test_preferred_challenge_choice_common_acme_1(self):
|
||||
self._test_preferred_challenge_choice_common(combos=True)
|
||||
|
||||
def test_preferred_challenge_choice_common_acme_2(self):
|
||||
self.mock_net.acme_version = 2
|
||||
self._test_preferred_challenge_choice_common(combos=False)
|
||||
|
||||
def _test_preferred_challenges_not_supported_common(self, combos):
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, combos=combos)]
|
||||
mock_order = mock.MagicMock(authorizations=authzrs)
|
||||
self.handler.pref_challs.append(challenges.HTTP01.typ)
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self.handler.handle_authorizations, mock_order)
|
||||
|
||||
def test_preferred_challenges_not_supported_acme_1(self):
|
||||
self._test_preferred_challenges_not_supported_common(combos=True)
|
||||
|
||||
def test_preferred_challenges_not_supported_acme_2(self):
|
||||
self.mock_net.acme_version = 2
|
||||
self._test_preferred_challenges_not_supported_common(combos=False)
|
||||
|
||||
def _validate_all(self, unused_1, unused_2):
|
||||
for dom in six.iterkeys(self.handler.authzr):
|
||||
azr = self.handler.authzr[dom]
|
||||
|
||||
Reference in New Issue
Block a user