def setUp(self): from certbot.plugins.script import Authenticator self.auth_return_value = "return from auth\n" self.script_nonexec = create_script(b'# empty') self.script_exec = create_script_exec(b'echo "return from auth\n"') self.config = mock.MagicMock( script_auth=self.script_exec, script_cleanup=self.script_exec, pref_challs=[ challenges.Challenge.TYPES["http-01"], challenges.Challenge.TYPES["dns-01"], challenges.Challenge.TYPES["tls-sni-01"] ]) self.tlssni_config = mock.MagicMock( script_auth=self.script_exec, script_cleanup=self.script_exec, pref_challs=[challenges.Challenge.TYPES["tls-sni-01"]]) self.nochall_config = mock.MagicMock( script_auth=self.script_exec, script_cleanup=self.script_exec, ) self.default = Authenticator(config=self.config, name="script") self.onlytlssni = Authenticator(config=self.tlssni_config, name="script") self.nochall = Authenticator(config=self.nochall_config, name="script") self.http01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="foo.com", account_key=KEY) self.dns01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.DNS01_P, domain="foo.com", account_key=KEY) self.achalls = [self.http01, self.dns01]
def setUp(self): kwargs = { "chall": acme_util.HTTP01, "uri": "uri", "status": messages.STATUS_INVALID, "error": messages.Error.with_code("tls", detail="detail"), } # Prevent future regressions if the error type changes self.assertTrue(kwargs["error"].description is not None) self.http01 = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="example.com", account_key="key") kwargs["chall"] = acme_util.TLSSNI01 self.tls_sni_same = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="example.com", account_key="key") kwargs["error"] = messages.Error(typ="dnssec", detail="detail") self.tls_sni_diff = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="foo.bar", account_key="key")
def test_perform2(self): domain = b'localhost' key = jose.JWK.load(test_util.load_vector('rsa512_key.pem')) http_01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain=domain, account_key=key) tls_sni_01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.TLSSNI01_P, domain=domain, account_key=key) self.auth.servers = mock.MagicMock() def _run(port, tls): # pylint: disable=unused-argument return "server{0}".format(port) self.auth.servers.run.side_effect = _run responses = self.auth.perform2([http_01, tls_sni_01]) self.assertTrue(isinstance(responses, list)) self.assertEqual(2, len(responses)) self.assertTrue(isinstance(responses[0], challenges.HTTP01Response)) self.assertTrue(isinstance(responses[1], challenges.TLSSNI01Response)) self.assertEqual(self.auth.servers.run.mock_calls, [ mock.call(4321, challenges.HTTP01), mock.call(1234, challenges.TLSSNI01), ]) self.assertEqual(self.auth.served, { "server1234": set([tls_sni_01]), "server4321": set([http_01]), }) self.assertEqual(1, len(self.auth.http_01_resources)) self.assertEqual(1, len(self.auth.certs)) self.assertEqual(list(self.auth.http_01_resources), [ acme_standalone.HTTP01RequestHandler.HTTP01Resource( acme_util.HTTP01, responses[0], mock.ANY) ])
def test_perform(self, mock_restart, mock_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded achall1 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=messages.ChallengeBody( chall=challenges.TLSSNI01(token="kNdwjwOeX0I_A8DXt9Msmg"), uri="https://ca.org/chall0_uri", status=messages.Status("pending"), ), domain="localhost", account_key=self.rsa512jwk) achall2 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=messages.ChallengeBody( chall=challenges.TLSSNI01(token="m8TdO1qik4JVFtgPPurJmg"), uri="https://ca.org/chall1_uri", status=messages.Status("pending"), ), domain="example.com", account_key=self.rsa512jwk) expected = [ achall1.response(self.rsa512jwk), achall2.response(self.rsa512jwk), ] mock_perform.return_value = expected responses = self.config.perform([achall1, achall2]) self.assertEqual(mock_perform.call_count, 1) self.assertEqual(responses, expected) self.assertEqual(mock_restart.call_count, 1)
def _get_achalls(cls): domain = b'localhost' key = jose.JWK.load(test_util.load_vector('rsa512_key.pem')) http_01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain=domain, account_key=key) tls_sni_01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.TLSSNI01_P, domain=domain, account_key=key) return [http_01, tls_sni_01]
class TLSSNI01Test(unittest.TestCase): """Tests for certbot.plugins.common.TLSSNI01.""" auth_key = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01(token=b'token1'), "pending"), domain="encryption-example.demo", account_key=auth_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01(token=b'token2'), "pending"), domain="certbot.demo", account_key=auth_key), ] def setUp(self): from certbot.plugins.common import TLSSNI01 self.sni = TLSSNI01(configurator=mock.MagicMock()) def test_add_chall(self): self.sni.add_chall(self.achalls[0], 0) self.assertEqual(1, len(self.sni.achalls)) self.assertEqual([0], self.sni.indices) def test_setup_challenge_cert(self): # This is a helper function that can be used for handling # open context managers more elegantly. It avoids dealing with # __enter__ and __exit__ calls. # http://www.voidspace.org.uk/python/mock/helpers.html#mock.mock_open mock_open, mock_safe_open = mock.mock_open(), mock.mock_open() response = challenges.TLSSNI01Response() achall = mock.MagicMock() key = test_util.load_pyopenssl_private_key("rsa512_key.pem") achall.response_and_validation.return_value = (response, ( test_util.load_cert("cert.pem"), key)) with mock.patch("certbot.plugins.common.open", mock_open, create=True): with mock.patch("certbot.plugins.common.util.safe_open", mock_safe_open): # pylint: disable=protected-access self.assertEqual( response, self.sni._setup_challenge_cert(achall, "randomS1")) # pylint: disable=no-member mock_open.assert_called_once_with(self.sni.get_cert_path(achall), "wb") mock_open.return_value.write.assert_called_once_with( test_util.load_vector("cert.pem")) mock_safe_open.assert_called_once_with(self.sni.get_key_path(achall), "wb", chmod=0o400) mock_safe_open.return_value.write.assert_called_once_with( OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key))
def test_same_vhost(self): vhost = next(v for v in self.config.vhosts if v.name == "certbot.demo") achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=((b'a' * 16))), "pending"), domain=vhost.name, account_key=self.account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=((b'b' * 16))), "pending"), domain=next(iter(vhost.aliases)), account_key=self.account_key) ] self.common_perform_test(achalls, [vhost])
def test_it(self): self.assertEqual( self._call(acme_util.HTTP01_P), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, account_key="account_key", domain="domain"), )
class BaseAuthenticatorTest: """ A base test class to reduce duplication between test code for DNS Authenticator Plugins. Assumes: * That subclasses also subclass unittest.TestCase * That the authenticator is stored as self.auth """ achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.DNS01, domain=DOMAIN, account_key=KEY) def test_more_info(self): self.assertTrue(isinstance(self.auth.more_info(), str)) # pylint: disable=no-member def test_get_chall_pref(self): self.assertEqual(self.auth.get_chall_pref(None), [challenges.DNS01]) # pylint: disable=no-member def test_parser_arguments(self): m = mock.MagicMock() self.auth.add_parser_arguments(m) # pylint: disable=no-member m.assert_any_call('propagation-seconds', type=int, default=mock.ANY, help=mock.ANY)
def setUp(self, *args, **kwargs): super(ApacheHttp01Test, self).setUp(*args, **kwargs) self.account_key = self.rsa512jwk self.achalls = [] # type: List[achallenges.KeyAuthorizationAnnotatedChallenge] vh_truth = util.get_vh_truth( self.temp_dir, "debian_apache_2_4/multiple_vhosts") # Takes the vhosts for encryption-example.demo, certbot.demo, and # vhost.in.rootconf self.vhosts = [vh_truth[0], vh_truth[3], vh_truth[10]] for i in range(NUM_ACHALLS): self.achalls.append( achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=((chr(ord('a') + i).encode() * 16))), "pending"), domain=self.vhosts[i].name, account_key=self.account_key)) modules = ["rewrite", "authz_core", "authz_host"] for mod in modules: self.config.parser.modules.add("mod_{0}.c".format(mod)) self.config.parser.modules.add(mod + "_module") from certbot_apache.http_01 import ApacheHttp01 self.http = ApacheHttp01(self.config)
def test_perform_and_cleanup(self, mock_revert, mock_restart, mock_http_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=messages.ChallengeBody( chall=challenges.HTTP01(token=b"m8TdO1qik4JVFtgPPurJmg"), uri="https://ca.org/chall1_uri", status=messages.Status("pending"), ), domain="example.com", account_key=self.rsa512jwk) expected = [ achall.response(self.rsa512jwk), ] mock_http_perform.return_value = expected[:] responses = self.config.perform([achall]) self.assertEqual(mock_http_perform.call_count, 1) self.assertEqual(responses, expected) self.config.cleanup([achall]) self.assertEqual(0, self.config._chall_out) # pylint: disable=protected-access self.assertEqual(mock_revert.call_count, 1) self.assertEqual(mock_restart.call_count, 2)
class WebrootActionTest(unittest.TestCase): """Tests for webroot argparse actions.""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) def setUp(self): from certbot.plugins.webroot import Authenticator self.path = tempfile.mkdtemp() self.parser = argparse.ArgumentParser() self.parser.add_argument("-d", "--domains", action="append", default=[]) Authenticator.inject_parser_options(self.parser, "webroot") def test_webroot_map_action(self): args = self.parser.parse_args( ["--webroot-map", json.dumps({'thing.com': self.path})]) self.assertEqual(args.webroot_map["thing.com"], self.path) def test_domain_before_webroot(self): args = self.parser.parse_args("-d {0} -w {1}".format( self.achall.domain, self.path).split()) config = self._get_config_after_perform(args) self.assertEqual(config.webroot_map[self.achall.domain], self.path) def test_domain_before_webroot_error(self): self.assertRaises(errors.PluginError, self.parser.parse_args, "-d foo -w bar -w baz".split()) self.assertRaises(errors.PluginError, self.parser.parse_args, "-d foo -w bar -d baz -w qux".split()) def test_multiwebroot(self): args = self.parser.parse_args("-w {0} -d {1} -w {2} -d bar".format( self.path, self.achall.domain, tempfile.mkdtemp()).split()) self.assertEqual(args.webroot_map[self.achall.domain], self.path) config = self._get_config_after_perform(args) self.assertEqual(config.webroot_map[self.achall.domain], self.path) def test_webroot_map_partial_without_perform(self): # This test acknowledges the fact that webroot_map content will be partial if webroot # plugin perform method is not invoked (corner case when all auths are already valid). # To not be a problem, the webroot_path must always been conserved during renew. # This condition is challenged by: # certbot.tests.renewal_tests::RenewalTest::test_webroot_params_conservation # See https://github.com/certbot/certbot/pull/7095 for details. other_webroot_path = tempfile.mkdtemp() args = self.parser.parse_args("-w {0} -d {1} -w {2} -d bar".format( self.path, self.achall.domain, other_webroot_path).split()) self.assertEqual(args.webroot_map, {self.achall.domain: self.path}) self.assertEqual(args.webroot_path, [self.path, other_webroot_path]) def _get_config_after_perform(self, config): from certbot.plugins.webroot import Authenticator auth = Authenticator(config, "webroot") auth.perform([self.achall]) return auth.config
def get_achalls(self): """Return testing achallenges.""" account_key = self.rsa512jwk achall1 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01( token="jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q"), "pending"), domain="encryption-example.demo", account_key=account_key) achall2 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01( token="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"), "pending"), domain="certbot.demo", account_key=account_key) return account_key, achall1, achall2
def test_anonymous_vhost(self): vhosts = [v for v in self.config.vhosts if not v.ssl] achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=((b'a' * 16))), "pending"), domain="something.nonexistent", account_key=self.account_key)] self.common_perform_test(achalls, vhosts)
def test_configure_multiple_vhosts(self): vhosts = [v for v in self.config.vhosts if "duplicate.example.com" in v.get_names()] self.assertEqual(len(vhosts), 2) achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=((b'a' * 16))), "pending"), domain="duplicate.example.com", account_key=self.account_key)] self.common_perform_test(achalls, vhosts)
def test_mod_config_deduplicate(self, mock_add_server_directives): """A vhost that appears in both HTTP and HTTPS vhosts only gets modded once""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=b"kNdwjxOeX0I_A8DXt9Msmg"), "pending"), domain="ssl.both.com", account_key=AUTH_KEY) self.http01.add_chall(achall) self.http01._mod_config() # pylint: disable=protected-access # Should only get called 5 times, rather than 6, because two vhosts are the same self.assertEqual(mock_add_server_directives.call_count, 5*2)
def setUp(self): from certbot.plugins.manual import Authenticator self.config = mock.MagicMock(http01_port=8080, manual_test_mode=False, manual_public_ip_logging_ok=False, noninteractive_mode=True) self.auth = Authenticator(config=self.config, name="manual") self.http01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="foo.com", account_key=KEY) self.dns01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.DNS01_P, domain="foo.com", account_key=KEY) self.achalls = [self.http01, self.dns01] config_test_mode = mock.MagicMock(http01_port=8080, manual_test_mode=True, noninteractive_mode=True) self.auth_test_mode = Authenticator(config=config_test_mode, name="manual")
def test_perform_new_webroot_not_in_map(self, mock_get_utility): new_webroot = tempfile.mkdtemp() self.config.webroot_path = [] self.config.webroot_map = {"whatever.com": self.path} mock_display = mock_get_utility() mock_display.menu.side_effect = ((display_util.OK, 0), (display_util.OK, new_webroot)) achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="something.com", account_key=KEY) with mock.patch('certbot.display.ops.validated_directory') as m: m.return_value = (display_util.OK, new_webroot,) self.auth.perform([achall]) self.assertEqual(self.config.webroot_map[achall.domain], new_webroot)
def test_configure_name_and_blank(self): domain = "certbot.demo" vhosts = [ v for v in self.config.vhosts if v.name == domain or v.name is None ] achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=((b'a' * 16))), "pending"), domain=domain, account_key=self.account_key), ] self.common_perform_test(achalls, vhosts)
def test_activate_disabled_vhost(self): vhosts = [v for v in self.config.vhosts if v.name == "certbot.demo"] achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=((b'a' * 16))), "pending"), domain="certbot.demo", account_key=self.account_key)] vhosts[0].enabled = False self.common_perform_test(achalls, vhosts) matches = self.config.parser.find_dir( "Include", vhosts[0].filep, get_aug_path(self.config.parser.loc["default"])) self.assertEqual(len(matches), 1)
def test_foreign_webconfig_multiple_domains(self): # Covers bug https://github.com/certbot/certbot/issues/9091 achall_2 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb(challenges.HTTP01(token=b"bingo"), "pending"), domain="second-thing.com", account_key=KEY) self.config.webroot_map["second-thing.com"] = self.path challenge_path = os.path.join(self.path, ".well-known", "acme-challenge") filesystem.makedirs(challenge_path) webconfig_path = os.path.join(challenge_path, "web.config") with open(webconfig_path, "w") as file: file.write("something") self.auth.perform([self.achall, achall_2])
def test_perform_cleanup_multiple_challenges(self): bingo_achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=b"bingo"), "pending"), domain="thing.com", account_key=KEY) bingo_validation_path = "YmluZ28" os.mkdir(self.partial_root_challenge_path) self.auth.prepare() self.auth.perform([bingo_achall, self.achall]) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(bingo_validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) self.auth.cleanup([bingo_achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertFalse(os.path.exists(self.root_challenge_path))
class WebrootActionTest(unittest.TestCase): """Tests for webroot argparse actions.""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) def setUp(self): from certbot.plugins.webroot import Authenticator self.path = tempfile.mkdtemp() self.parser = argparse.ArgumentParser() self.parser.add_argument("-d", "--domains", action="append", default=[]) Authenticator.inject_parser_options(self.parser, "webroot") def test_webroot_map_action(self): args = self.parser.parse_args( ["--webroot-map", json.dumps({'thing.com': self.path})]) self.assertEqual(args.webroot_map["thing.com"], self.path) def test_domain_before_webroot(self): args = self.parser.parse_args("-d {0} -w {1}".format( self.achall.domain, self.path).split()) config = self._get_config_after_perform(args) self.assertEqual(config.webroot_map[self.achall.domain], self.path) def test_domain_before_webroot_error(self): self.assertRaises(errors.PluginError, self.parser.parse_args, "-d foo -w bar -w baz".split()) self.assertRaises(errors.PluginError, self.parser.parse_args, "-d foo -w bar -d baz -w qux".split()) def test_multiwebroot(self): args = self.parser.parse_args("-w {0} -d {1} -w {2} -d bar".format( self.path, self.achall.domain, tempfile.mkdtemp()).split()) self.assertEqual(args.webroot_map[self.achall.domain], self.path) config = self._get_config_after_perform(args) self.assertEqual(config.webroot_map[self.achall.domain], self.path) def _get_config_after_perform(self, config): from certbot.plugins.webroot import Authenticator auth = Authenticator(config, "webroot") auth.perform([self.achall]) return auth.config
def _create_achalls(plugin): """Returns a list of annotated challenges to test on plugin""" achalls = list() names = plugin.get_testable_domain_names() for domain in names: prefs = plugin.get_chall_pref(domain) for chall_type in prefs: if chall_type == challenges.HTTP01: chall = challenges.HTTP01( token=os.urandom(challenges.HTTP01.TOKEN_SIZE)) challb = acme_util.chall_to_challb( chall, messages.STATUS_PENDING) achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=challb, domain=domain, account_key=util.JWK) achalls.append(achall) return achalls
def _create_achalls(plugin: common.Proxy) -> List[achallenges.AnnotatedChallenge]: """Returns a list of annotated challenges to test on plugin""" achalls: List[achallenges.AnnotatedChallenge] = [] names = plugin.get_testable_domain_names() for domain in names: prefs = plugin.get_chall_pref(domain) for chall_type in prefs: if chall_type == challenges.HTTP01: # challenges.HTTP01.TOKEN_SIZE is a float but os.urandom # expects an integer. chall = challenges.HTTP01( token=os.urandom(int(challenges.HTTP01.TOKEN_SIZE))) challb = acme_util.chall_to_challb( chall, messages.STATUS_PENDING) achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=challb, domain=domain, account_key=util.JWK) achalls.append(achall) return achalls
def challb_to_achall(challb: messages.ChallengeBody, account_key: josepy.JWK, domain: str) -> achallenges.AnnotatedChallenge: """Converts a ChallengeBody object to an AnnotatedChallenge. :param .ChallengeBody challb: ChallengeBody :param .JWK account_key: Authorized Account Key :param str domain: Domain of the challb :returns: Appropriate AnnotatedChallenge :rtype: :class:`certbot.achallenges.AnnotatedChallenge` """ chall = challb.chall logger.info("%s challenge for %s", chall.typ, domain) if isinstance(chall, challenges.KeyAuthorizationChallenge): return achallenges.KeyAuthorizationAnnotatedChallenge( challb=challb, domain=domain, account_key=account_key) elif isinstance(chall, challenges.DNS): return achallenges.DNS(challb=challb, domain=domain) raise errors.Error(f"Received unsupported challenge of type: {chall.typ}")
def setUp(self, *args, **kwargs): super(ApacheHttp01Test, self).setUp(*args, **kwargs) self.account_key = self.rsa512jwk self.achalls = [] for i in range(NUM_ACHALLS): self.achalls.append( achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=((chr(ord('a') + i) * 16))), "pending"), domain="example{0}.com".format(i), account_key=self.account_key)) modules = ["alias", "authz_core", "authz_host"] for mod in modules: self.config.parser.modules.add("mod_{0}.c".format(mod)) self.config.parser.modules.add(mod + "_module") from certbot_apache.http_01 import ApacheHttp01 self.http = ApacheHttp01(self.config)
except ImportError: # pragma: no cover from unittest import mock from acme import challenges from certbot import achallenges from certbot import crypto_util from certbot import errors from certbot.compat import filesystem from certbot.compat import os from certbot.tests import acme_util from certbot.tests import util as test_util AUTH_KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) ACHALL = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb(challenges.HTTP01(token=b'token1'), "pending"), domain="encryption-example.demo", account_key=AUTH_KEY) class NamespaceFunctionsTest(unittest.TestCase): """Tests for certbot.plugins.common.*_namespace functions.""" def test_option_namespace(self): from certbot.plugins.common import option_namespace self.assertEqual("foo-", option_namespace("foo")) def test_dest_namespace(self): from certbot.plugins.common import dest_namespace self.assertEqual("foo_", dest_namespace("foo")) def test_dest_namespace_with_dashes(self):
class HttpPerformTest(util.NginxTest): """Test the NginxHttp01 challenge.""" account_key = AUTH_KEY achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=b"kNdwjwOeX0I_A8DXt9Msmg"), "pending"), domain="www.example.com", account_key=account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01( token=b"\xba\xa9\xda?<m\xaewmx\xea\xad\xadv\xf4\x02\xc9y" b"\x80\xe2_X\t\xe7\xc7\xa4\t\xca\xf7&\x945" ), "pending"), domain="ipv6.com", account_key=account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01( token=b"\x8c\x8a\xbf_-f\\cw\xee\xd6\xf8/\xa5\xe3\xfd" b"\xeb9\xf1\xf5\xb9\xefVM\xc9w\xa4u\x9c\xe1\x87\xb4" ), "pending"), domain="www.example.org", account_key=account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=b"kNdwjxOeX0I_A8DXt9Msmg"), "pending"), domain="migration.com", account_key=account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=b"kNdwjxOeX0I_A8DXt9Msmg"), "pending"), domain="ipv6ssl.com", account_key=account_key), ] def setUp(self): super().setUp() config = self.get_nginx_configurator( self.config_path, self.config_dir, self.work_dir, self.logs_dir) from certbot_nginx._internal import http_01 self.http01 = http_01.NginxHttp01(config) def test_perform0(self): responses = self.http01.perform() self.assertEqual([], responses) @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.save") def test_perform1(self, mock_save): self.http01.add_chall(self.achalls[0]) response = self.achalls[0].response(self.account_key) responses = self.http01.perform() self.assertEqual([response], responses) self.assertEqual(mock_save.call_count, 1) def test_perform2(self): acme_responses = [] for achall in self.achalls: self.http01.add_chall(achall) acme_responses.append(achall.response(self.account_key)) http_responses = self.http01.perform() self.assertEqual(len(http_responses), 5) for i in range(5): self.assertEqual(http_responses[i], acme_responses[i]) def test_mod_config(self): self.http01.add_chall(self.achalls[0]) self.http01.add_chall(self.achalls[2]) self.http01._mod_config() # pylint: disable=protected-access self.http01.configurator.save() self.http01.configurator.parser.load() # vhosts = self.http01.configurator.parser.get_vhosts() # for vhost in vhosts: # pass # if the name matches # check that the location block is in there and is correct # if vhost.addrs == set(v_addr1): # response = self.achalls[0].response(self.account_key) # else: # response = self.achalls[2].response(self.account_key) # self.assertEqual(vhost.addrs, set(v_addr2_print)) # self.assertEqual(vhost.names, set([response.z_domain.decode('ascii')])) @mock.patch('certbot_nginx._internal.parser.NginxParser.add_server_directives') def test_mod_config_http_and_https(self, mock_add_server_directives): """A server_name with both HTTP and HTTPS vhosts should get modded in both vhosts""" self.configuration.https_port = 443 self.http01.add_chall(self.achalls[3]) # migration.com self.http01._mod_config() # pylint: disable=protected-access # Domain has an HTTP and HTTPS vhost # 2 * 'rewrite' + 2 * 'return 200 keyauthz' = 4 self.assertEqual(mock_add_server_directives.call_count, 4) @mock.patch('certbot_nginx._internal.parser.nginxparser.dump') @mock.patch('certbot_nginx._internal.parser.NginxParser.add_server_directives') def test_mod_config_only_https(self, mock_add_server_directives, mock_dump): """A server_name with only an HTTPS vhost should get modded""" self.http01.add_chall(self.achalls[4]) # ipv6ssl.com self.http01._mod_config() # pylint: disable=protected-access # It should modify the existing HTTPS vhost self.assertEqual(mock_add_server_directives.call_count, 2) # since there was no suitable HTTP vhost or default HTTP vhost, a non-empty one # should have been created and written to the challenge conf file self.assertNotEqual(mock_dump.call_args[0][0], []) @mock.patch('certbot_nginx._internal.parser.NginxParser.add_server_directives') def test_mod_config_deduplicate(self, mock_add_server_directives): """A vhost that appears in both HTTP and HTTPS vhosts only gets modded once""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=b"kNdwjxOeX0I_A8DXt9Msmg"), "pending"), domain="ssl.both.com", account_key=AUTH_KEY) self.http01.add_chall(achall) self.http01._mod_config() # pylint: disable=protected-access # Should only get called 5 times, rather than 6, because two vhosts are the same self.assertEqual(mock_add_server_directives.call_count, 5*2) @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.ipv6_info") def test_default_listen_addresses_no_memoization(self, ipv6_info): # pylint: disable=protected-access ipv6_info.return_value = (True, True) self.http01._default_listen_addresses() self.assertEqual(ipv6_info.call_count, 1) ipv6_info.return_value = (False, False) self.http01._default_listen_addresses() self.assertEqual(ipv6_info.call_count, 2) @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.ipv6_info") def test_default_listen_addresses_t_t(self, ipv6_info): # pylint: disable=protected-access ipv6_info.return_value = (True, True) addrs = self.http01._default_listen_addresses() http_addr = Addr.fromstring("80") http_ipv6_addr = Addr.fromstring("[::]:80") self.assertEqual(addrs, [http_addr, http_ipv6_addr]) @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.ipv6_info") def test_default_listen_addresses_t_f(self, ipv6_info): # pylint: disable=protected-access ipv6_info.return_value = (True, False) addrs = self.http01._default_listen_addresses() http_addr = Addr.fromstring("80") http_ipv6_addr = Addr.fromstring("[::]:80 ipv6only=on") self.assertEqual(addrs, [http_addr, http_ipv6_addr]) @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.ipv6_info") def test_default_listen_addresses_f_f(self, ipv6_info): # pylint: disable=protected-access ipv6_info.return_value = (False, False) addrs = self.http01._default_listen_addresses() http_addr = Addr.fromstring("80") self.assertEqual(addrs, [http_addr])
class AuthenticatorTest(unittest.TestCase): """Tests for certbot.plugins.webroot.Authenticator.""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) def setUp(self): from certbot.plugins.webroot import Authenticator # On Linux directories created by tempfile.mkdtemp inherit their permissions from their # parent directory. So the actual permissions are inconsistent over various tests env. # To circumvent this, a dedicated sub-workspace is created under the workspace, using # filesystem.mkdir to get consistent permissions. self.workspace = tempfile.mkdtemp() self.path = os.path.join(self.workspace, 'webroot') filesystem.mkdir(self.path) self.partial_root_challenge_path = os.path.join( self.path, ".well-known") self.root_challenge_path = os.path.join(self.path, ".well-known", "acme-challenge") self.validation_path = os.path.join( self.root_challenge_path, "ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ") self.config = mock.MagicMock(webroot_path=self.path, webroot_map={"thing.com": self.path}) self.auth = Authenticator(self.config, "webroot") def tearDown(self): shutil.rmtree(self.path) def test_more_info(self): more_info = self.auth.more_info() self.assertTrue(isinstance(more_info, six.string_types)) self.assertTrue(self.path in more_info) def test_add_parser_arguments(self): add = mock.MagicMock() self.auth.add_parser_arguments(add) self.assertEqual(2, add.call_count) def test_prepare(self): self.auth.prepare() # shouldn't raise any exceptions @test_util.patch_get_utility() def test_webroot_from_list(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {"otherthing.com": self.path} mock_display = mock_get_utility() mock_display.menu.return_value = ( display_util.OK, 1, ) self.auth.perform([self.achall]) self.assertTrue(mock_display.menu.called) for call in mock_display.menu.call_args_list: self.assertTrue(self.achall.domain in call[0][0]) self.assertTrue( all(webroot in call[0][1] for webroot in six.itervalues(self.config.webroot_map))) self.assertEqual(self.config.webroot_map[self.achall.domain], self.path) @test_util.patch_get_utility() def test_webroot_from_list_help_and_cancel(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {"otherthing.com": self.path} mock_display = mock_get_utility() mock_display.menu.side_effect = ((display_util.CANCEL, -1), ) self.assertRaises(errors.PluginError, self.auth.perform, [self.achall]) self.assertTrue(mock_display.menu.called) for call in mock_display.menu.call_args_list: self.assertTrue(self.achall.domain in call[0][0]) self.assertTrue( all(webroot in call[0][1] for webroot in six.itervalues(self.config.webroot_map))) @test_util.patch_get_utility() def test_new_webroot(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {"something.com": self.path} mock_display = mock_get_utility() mock_display.menu.return_value = ( display_util.OK, 0, ) with mock.patch('certbot.display.ops.validated_directory') as m: m.side_effect = ((display_util.CANCEL, -1), ( display_util.OK, self.path, )) self.auth.perform([self.achall]) self.assertEqual(self.config.webroot_map[self.achall.domain], self.path) @test_util.patch_get_utility() def test_new_webroot_empty_map_cancel(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {} mock_display = mock_get_utility() mock_display.menu.return_value = ( display_util.OK, 0, ) with mock.patch('certbot.display.ops.validated_directory') as m: m.return_value = (display_util.CANCEL, -1) self.assertRaises(errors.PluginError, self.auth.perform, [self.achall]) def test_perform_missing_root(self): self.config.webroot_path = None self.config.webroot_map = {} self.assertRaises(errors.PluginError, self.auth.perform, []) def test_perform_reraises_other_errors(self): self.auth.full_path = os.path.join(self.path, "null") permission_canary = os.path.join(self.path, "rnd") with open(permission_canary, "w") as f: f.write("thingimy") filesystem.chmod(self.path, 0o000) try: open(permission_canary, "r") print("Warning, running tests as root skips permissions tests...") except IOError: # ok, permissions work, test away... self.assertRaises(errors.PluginError, self.auth.perform, []) filesystem.chmod(self.path, 0o700) @mock.patch( "certbot.plugins.webroot.filesystem.copy_ownership_and_apply_mode") def test_failed_chown(self, mock_ownership): mock_ownership.side_effect = OSError(errno.EACCES, "msg") self.auth.perform([self.achall]) # exception caught and logged @test_util.patch_get_utility() def test_perform_new_webroot_not_in_map(self, mock_get_utility): new_webroot = tempfile.mkdtemp() self.config.webroot_path = [] self.config.webroot_map = {"whatever.com": self.path} mock_display = mock_get_utility() mock_display.menu.side_effect = ((display_util.OK, 0), (display_util.OK, new_webroot)) achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="something.com", account_key=KEY) with mock.patch('certbot.display.ops.validated_directory') as m: m.return_value = ( display_util.OK, new_webroot, ) self.auth.perform([achall]) self.assertEqual(self.config.webroot_map[achall.domain], new_webroot) def test_perform_permissions(self): self.auth.prepare() # Remove exec bit from permission check, so that it # matches the file self.auth.perform([self.achall]) self.assertTrue(filesystem.check_mode(self.validation_path, 0o644)) # Check permissions of the directories for dirpath, dirnames, _ in os.walk(self.path): for directory in dirnames: full_path = os.path.join(dirpath, directory) self.assertTrue(filesystem.check_mode(full_path, 0o755)) self.assertTrue( filesystem.has_same_ownership(self.validation_path, self.path)) def test_perform_cleanup(self): self.auth.prepare() responses = self.auth.perform([self.achall]) self.assertEqual(1, len(responses)) self.assertTrue(os.path.exists(self.validation_path)) with open(self.validation_path) as validation_f: validation = validation_f.read() self.assertTrue( challenges.KeyAuthorizationChallengeResponse( key_authorization=validation).verify(self.achall.chall, KEY.public_key())) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertFalse(os.path.exists(self.root_challenge_path)) self.assertFalse(os.path.exists(self.partial_root_challenge_path)) def test_perform_cleanup_existing_dirs(self): filesystem.mkdir(self.partial_root_challenge_path) self.auth.prepare() self.auth.perform([self.achall]) self.auth.cleanup([self.achall]) # Ensure we don't "clean up" directories that previously existed self.assertFalse(os.path.exists(self.validation_path)) self.assertFalse(os.path.exists(self.root_challenge_path)) def test_perform_cleanup_multiple_challenges(self): bingo_achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb(challenges.HTTP01(token=b"bingo"), "pending"), domain="thing.com", account_key=KEY) bingo_validation_path = "YmluZ28" filesystem.mkdir(self.partial_root_challenge_path) self.auth.prepare() self.auth.perform([bingo_achall, self.achall]) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(bingo_validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) self.auth.cleanup([bingo_achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertFalse(os.path.exists(self.root_challenge_path)) def test_cleanup_leftovers(self): self.auth.prepare() self.auth.perform([self.achall]) leftover_path = os.path.join(self.root_challenge_path, 'leftover') filesystem.mkdir(leftover_path) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) os.rmdir(leftover_path) @mock.patch('certbot.compat.os.rmdir') def test_cleanup_failure(self, mock_rmdir): self.auth.prepare() self.auth.perform([self.achall]) os_error = OSError() os_error.errno = errno.EACCES mock_rmdir.side_effect = os_error self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path))