mirror of
https://github.com/certbot/certbot.git
synced 2026-06-03 13:59:02 -04:00
Add extra challenge info to --debug-challenges (#9208)
* Add challenge info to `--debug-challenges` * Expand/add tests * Add changelog entry * Make tests Python 3.6 and 3.7 compatible * Don't use `config.namespace` * And don't use `config.namespace` in tests too * Expand tests to check for token/thumbprint * Add test for the DNS-01 challenge Changed the Apache authenticator to the manual authenticator. Doesn't seem to make a difference to the tests, but makes more sense if the DNS-01 challenge is being used. * Reword changelog entry * Mention feature in --help output * Better variable assignment in test Co-authored-by: alexzorin <alex@zor.io> * Better variable assignment in test Co-authored-by: alexzorin <alex@zor.io> * Remove unnecessary `verbose_count` assignment Co-authored-by: alexzorin <alex@zor.io> * Use terminology from RFC 8555 * Compress the two new tests into one * s/world wide web/internet * Move new code into separate function * Remove superfluous newline with mixed challs Co-authored-by: alexzorin <alex@zor.io>
This commit is contained in:
parent
d9dd3134f0
commit
96847ba779
4 changed files with 90 additions and 6 deletions
|
|
@ -6,6 +6,9 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
|
|||
|
||||
### Added
|
||||
|
||||
* When the `--debug-challenges` option is used in combination with `-v`, Certbot
|
||||
now displays the challenge URLs (for `http-01` challenges) or FQDNs (for
|
||||
`dns-01` challenges) and their expected return values.
|
||||
*
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ class AuthHandler:
|
|||
# If debug is on, wait for user input before starting the verification process.
|
||||
if config.debug_challenges:
|
||||
display_util.notification(
|
||||
'Challenges loaded. Press continue to submit to CA. '
|
||||
'Pass "-v" for more info about challenges.', pause=True)
|
||||
'Challenges loaded. Press continue to submit to CA.\n' +
|
||||
self._debug_challenges_msg(achalls, config), pause=True)
|
||||
except errors.AuthorizationError as error:
|
||||
logger.critical('Failure in setting up challenges.')
|
||||
logger.info('Attempting to clean up outstanding challenges...')
|
||||
|
|
@ -324,6 +324,44 @@ class AuthHandler:
|
|||
|
||||
display_util.notify("".join(msg))
|
||||
|
||||
def _debug_challenges_msg(self, achalls: List[achallenges.AnnotatedChallenge],
|
||||
config: configuration.NamespaceConfig) -> str:
|
||||
"""Construct message for debug challenges prompt
|
||||
|
||||
:param list achalls: A list of
|
||||
:class:`certbot.achallenges.AnnotatedChallenge`.
|
||||
:param certbot.configuration.NamespaceConfig config: current Certbot configuration
|
||||
:returns: Message containing challenge debug info
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
if config.verbose_count > 0:
|
||||
msg = []
|
||||
http01_achalls = {}
|
||||
dns01_achalls = {}
|
||||
for achall in achalls:
|
||||
if isinstance(achall.chall, challenges.HTTP01):
|
||||
http01_achalls[achall.chall.uri(achall.domain)] = (
|
||||
achall.validation(achall.account_key) + "\n"
|
||||
)
|
||||
if isinstance(achall.chall, challenges.DNS01):
|
||||
dns01_achalls[achall.validation_domain_name(achall.domain)] = (
|
||||
achall.validation(achall.account_key) + "\n"
|
||||
)
|
||||
if http01_achalls:
|
||||
msg.append("The following URLs should be accessible from the "
|
||||
"internet and return the value mentioned:\n")
|
||||
for uri, key_authz in http01_achalls.items():
|
||||
msg.append(f"URL: {uri}\nExpected value: {key_authz}")
|
||||
if dns01_achalls:
|
||||
msg.append("The following FQDNs should return a TXT resource "
|
||||
"record with the value mentioned:\n")
|
||||
for fqdn, key_authz_hash in dns01_achalls.items():
|
||||
msg.append(f"FQDN: {fqdn}\nExpected value: {key_authz_hash}")
|
||||
return "\n" + "\n".join(msg)
|
||||
else:
|
||||
return 'Pass "-v" for more info about challenges.'
|
||||
|
||||
|
||||
def challb_to_achall(challb: messages.ChallengeBody, account_key: josepy.JWK,
|
||||
domain: str) -> achallenges.AnnotatedChallenge:
|
||||
|
|
|
|||
|
|
@ -264,7 +264,9 @@ def prepare_and_parse_args(plugins: plugins_disco.PluginsRegistry, args: List[st
|
|||
[None, "certonly", "run"], "--debug-challenges", action="store_true",
|
||||
default=flag_default("debug_challenges"),
|
||||
help="After setting up challenges, wait for user input before "
|
||||
"submitting to CA")
|
||||
"submitting to CA. When used in combination with the `-v` "
|
||||
"option, the challenge URLs or FQDNs and their expected "
|
||||
"return values are shown.")
|
||||
helpful.add(
|
||||
"testing", "--no-verify-ssl", action="store_true",
|
||||
help=config_help("no_verify_ssl"),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import functools
|
|||
import logging
|
||||
import unittest
|
||||
|
||||
from josepy import b64encode
|
||||
try:
|
||||
import mock
|
||||
except ImportError: # pragma: no cover
|
||||
|
|
@ -72,13 +73,13 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
|||
with mock.patch("zope.component.provideUtility"):
|
||||
display_obj.set_display(self.mock_display)
|
||||
|
||||
self.mock_auth = mock.MagicMock(name="ApacheConfigurator")
|
||||
self.mock_auth = mock.MagicMock(name="Authenticator")
|
||||
|
||||
self.mock_auth.get_chall_pref.return_value = [challenges.HTTP01]
|
||||
|
||||
self.mock_auth.perform.side_effect = gen_auth_resp
|
||||
|
||||
self.mock_account = mock.Mock(key=util.Key("file_path", "PEM"))
|
||||
self.mock_account = mock.MagicMock()
|
||||
self.mock_net = mock.MagicMock(spec=acme_client.ClientV2)
|
||||
self.mock_net.acme_version = 1
|
||||
self.mock_net.retry_after.side_effect = acme_client.ClientV2.retry_after
|
||||
|
|
@ -190,16 +191,56 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
|||
self._test_name3_http_01_3_common(combos=False)
|
||||
|
||||
def test_debug_challenges(self):
|
||||
config = mock.Mock(debug_challenges=True)
|
||||
config = mock.Mock(debug_challenges=True, verbose_count=0)
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
|
||||
mock_order = mock.MagicMock(authorizations=authzrs)
|
||||
|
||||
account_key_thumbprint = b"foobarbaz"
|
||||
self.mock_account.key.thumbprint.return_value = account_key_thumbprint
|
||||
|
||||
self.mock_net.poll.side_effect = _gen_mock_on_poll()
|
||||
|
||||
self.handler.handle_authorizations(mock_order, config)
|
||||
|
||||
self.assertEqual(self.mock_net.answer_challenge.call_count, 1)
|
||||
self.assertEqual(self.mock_display.notification.call_count, 1)
|
||||
self.assertIn('Pass "-v" for more info',
|
||||
self.mock_display.notification.call_args[0][0])
|
||||
self.assertNotIn(f"http://{authzrs[0].body.identifier.value}/.well-known/acme-challenge/" +
|
||||
b64encode(authzrs[0].body.challenges[0].chall.token).decode(),
|
||||
self.mock_display.notification.call_args[0][0])
|
||||
self.assertNotIn(b64encode(account_key_thumbprint).decode(),
|
||||
self.mock_display.notification.call_args[0][0])
|
||||
|
||||
def test_debug_challenges_verbose(self):
|
||||
config = mock.Mock(debug_challenges=True, verbose_count=1)
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=[acme_util.HTTP01]),
|
||||
gen_dom_authzr(domain="1", challs=[acme_util.DNS01])]
|
||||
mock_order = mock.MagicMock(authorizations=authzrs)
|
||||
|
||||
account_key_thumbprint = b"foobarbaz"
|
||||
self.mock_account.key.thumbprint.return_value = account_key_thumbprint
|
||||
|
||||
self.mock_net.poll.side_effect = _gen_mock_on_poll()
|
||||
|
||||
self.mock_auth.get_chall_pref.return_value = [challenges.HTTP01,
|
||||
challenges.DNS01]
|
||||
|
||||
self.handler.handle_authorizations(mock_order, config)
|
||||
|
||||
self.assertEqual(self.mock_net.answer_challenge.call_count, 2)
|
||||
self.assertEqual(self.mock_display.notification.call_count, 1)
|
||||
self.assertNotIn('Pass "-v" for more info',
|
||||
self.mock_display.notification.call_args[0][0])
|
||||
self.assertIn(f"http://{authzrs[0].body.identifier.value}/.well-known/acme-challenge/" +
|
||||
b64encode(authzrs[0].body.challenges[0].chall.token).decode(),
|
||||
self.mock_display.notification.call_args[0][0])
|
||||
self.assertIn(b64encode(account_key_thumbprint).decode(),
|
||||
self.mock_display.notification.call_args[0][0])
|
||||
self.assertIn(f"_acme-challenge.{authzrs[1].body.identifier.value}",
|
||||
self.mock_display.notification.call_args[0][0])
|
||||
self.assertIn(authzrs[1].body.challenges[0].validation(self.mock_account.key),
|
||||
self.mock_display.notification.call_args[0][0])
|
||||
|
||||
def test_perform_failure(self):
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue