Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add webroot delay #9650

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions certbot/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
* acme.client.ClientV2 now provides separate `begin_finalization`
and `poll_finalization` methods, in addition to the existing
`finalize_order` method.
* The webroot plugin now also supports the `--webroot-propagation-seconds`
option. Note that this is usually not required, but for some systems
with distributed webroots requiring syncing this might be useful.

### Changed

Expand Down
13 changes: 12 additions & 1 deletion certbot/certbot/_internal/plugins/webroot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import collections
import json
import logging
from time import sleep
from typing import Any
from typing import Callable
from typing import DefaultDict
Expand Down Expand Up @@ -87,6 +88,9 @@ def add_parser_arguments(cls, add: Callable[..., None]) -> None:
"-d entries. At present, if you put webroot-map in a config "
"file, it needs to be on a single line, like: webroot-map = "
'{"example.com":"/var/www"}.')
add("propagation-seconds", default=0, type=int,
help="The number of seconds to wait for the webroot challenge file(s) to propagate "
"before asking the ACME server to verify the challenge.")

def auth_hint(self, failed_achalls: List[AnnotatedChallenge]) -> str: # pragma: no cover
return ("The Certificate Authority failed to download the temporary challenge files "
Expand All @@ -113,7 +117,14 @@ def perform(self, achalls: List[AnnotatedChallenge]) -> List[challenges.Challeng

self._create_challenge_dirs()

return [self._perform_single(achall) for achall in achalls]
resps = [self._perform_single(achall) for achall in achalls]

if self.conf('propagation-seconds'):
display_util.notify("Waiting %d seconds for webroot challenge file(s) to propagate" %
self.conf('propagation-seconds'))
osirisinferi marked this conversation as resolved.
Show resolved Hide resolved
sleep(self.conf('propagation-seconds'))

return resps

def _set_webroots(self, achalls: Iterable[AnnotatedChallenge]) -> None:
if self.conf("path"):
Expand Down
30 changes: 28 additions & 2 deletions certbot/certbot/_internal/tests/plugins/webroot_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def setUp(self):
self.root_challenge_path,
"ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ")
self.config = mock.MagicMock(webroot_path=self.path,
webroot_map={"thing.com": self.path})
webroot_map={"thing.com": self.path},
webroot_propagation_seconds=0)
self.auth = Authenticator(self.config, "webroot")

def tearDown(self):
Expand All @@ -64,7 +65,7 @@ def test_more_info(self):
def test_add_parser_arguments(self):
add = mock.MagicMock()
self.auth.add_parser_arguments(add)
assert 2 == add.call_count
assert 3 == add.call_count

def test_prepare(self):
self.auth.prepare() # shouldn't raise any exceptions
Expand Down Expand Up @@ -302,6 +303,23 @@ def test_cleanup_failure(self, mock_rmdir):
assert not os.path.exists(self.validation_path)
assert os.path.exists(self.root_challenge_path)

@mock.patch('certbot.display.util.notify')
def test_propagation_seconds_default(self, mock_notify):
self.auth.prepare()
self.auth.perform([self.achall])

mock_notify.assert_not_called()

@mock.patch('certbot.display.util.notify')
def test_propagation_seconds_non_default(self, mock_notify):
self.config.webroot_propagation_seconds = 1
self.auth.prepare()
self.auth.perform([self.achall])

mock_notify.assert_called_once_with(
"Waiting 1 seconds for webroot challenge file(s) to propagate"
)


class WebrootActionTest(unittest.TestCase):
"""Tests for webroot argparse actions."""
Expand Down Expand Up @@ -354,6 +372,14 @@ def test_webroot_map_partial_without_perform(self):
assert args.webroot_map == {self.achall.domain: self.path}
assert args.webroot_path == [self.path, other_webroot_path]

def test_propagation_seconds_default_action(self):
args = self.parser.parse_args([])
assert args.webroot_propagation_seconds == 0

def test_propagation_seconds_non_default_action(self):
args = self.parser.parse_args(["--webroot-propagation-seconds", "10"])
assert args.webroot_propagation_seconds == 10

def _get_config_after_perform(self, config):
from certbot._internal.plugins.webroot import Authenticator
auth = Authenticator(config, "webroot")
Expand Down