From 93e69ef7dee7d7315ec44e5567fba1f3bfa145a6 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 1 Nov 2015 12:22:19 +0000 Subject: [PATCH] tls-sni-01 for standalone --- docs/using.rst | 2 +- examples/cli.ini | 2 +- letsencrypt/achallenges.py | 5 +++-- letsencrypt/plugins/standalone.py | 30 ++++++++++++++------------ letsencrypt/plugins/standalone_test.py | 30 +++++++++++++------------- tests/boulder-integration.sh | 2 +- 6 files changed, 37 insertions(+), 34 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 1bc7fab99..8e8fd132e 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -135,7 +135,7 @@ Plugin A I Notes and status ========== = = ================================================================ standalone Y N Very stable. Uses port 80 (force by ``--standalone-supported-challenges http-01``) or 443 - (force by ``--standalone-supported-challenges dvsni``). + (force by ``--standalone-supported-challenges tls-sni-01``). apache Y Y Alpha. Automates Apache installation, works fairly well but on Debian-based distributions only for now. webroot Y N Works with already running webserver, by writing necessary files diff --git a/examples/cli.ini b/examples/cli.ini index 34fb8ab02..a20764ed8 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -16,7 +16,7 @@ server = https://acme-staging.api.letsencrypt.org/directory # Uncomment to use the standalone authenticator on port 443 # authenticator = standalone -# standalone-supported-challenges = dvsni +# standalone-supported-challenges = tls-sni-01 # Uncomment to use the webroot authenticator. Replace webroot-path with the # path to the public_html / webroot folder being served by your web server. diff --git a/letsencrypt/achallenges.py b/letsencrypt/achallenges.py index f08c6a396..4e46d6af9 100644 --- a/letsencrypt/achallenges.py +++ b/letsencrypt/achallenges.py @@ -49,9 +49,10 @@ class KeyAuthorizationAnnotatedChallenge(AnnotatedChallenge): """Client annotated `KeyAuthorizationChallenge` challenge.""" __slots__ = ('challb', 'domain', 'account_key') - def response_and_validation(self): + def response_and_validation(self, *args, **kwargs): """Generate response and validation.""" - return self.challb.chall.response_and_validation(self.account_key) + return self.challb.chall.response_and_validation( + self.account_key, *args, **kwargs) class DVSNI(AnnotatedChallenge): diff --git a/letsencrypt/plugins/standalone.py b/letsencrypt/plugins/standalone.py index 3975e9292..717434052 100644 --- a/letsencrypt/plugins/standalone.py +++ b/letsencrypt/plugins/standalone.py @@ -11,7 +11,6 @@ import six import zope.interface from acme import challenges -from acme import crypto_util as acme_crypto_util from acme import standalone as acme_standalone from letsencrypt import errors @@ -51,19 +50,19 @@ class ServerManager(object): :param int port: Port to run the server on. :param challenge_type: Subclass of `acme.challenges.Challenge`, - either `acme.challenge.HTTP01` or `acme.challenges.DVSNI`. + either `acme.challenge.HTTP01` or `acme.challenges.TLSSNI01`. :returns: Server instance. :rtype: ACMEServerMixin """ - assert challenge_type in (challenges.DVSNI, challenges.HTTP01) + assert challenge_type in (challenges.TLSSNI01, challenges.HTTP01) if port in self._instances: return self._instances[port].server address = ("", port) try: - if challenge_type is challenges.DVSNI: + if challenge_type is challenges.TLSSNI01: server = acme_standalone.DVSNIServer(address, self.certs) else: # challenges.HTTP01 server = acme_standalone.HTTP01Server( @@ -109,7 +108,7 @@ class ServerManager(object): in six.iteritems(self._instances)) -SUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.HTTP01]) +SUPPORTED_CHALLENGES = set([challenges.TLSSNI01, challenges.HTTP01]) def supported_challenges_validator(data): @@ -138,7 +137,7 @@ class Authenticator(common.Plugin): """Standalone Authenticator. This authenticator creates its own ephemeral TCP listener on the - necessary port in order to respond to incoming DVSNI and HTTP01 + necessary port in order to respond to incoming tls-sni-01 and http-01 challenges from the certificate authority. Therefore, it does not rely on any existing server program. """ @@ -150,7 +149,7 @@ class Authenticator(common.Plugin): def __init__(self, *args, **kwargs): super(Authenticator, self).__init__(*args, **kwargs) - # one self-signed key for all DVSNI certificates + # one self-signed key for all tls-sni-01 certificates self.key = OpenSSL.crypto.PKey() self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048) @@ -183,15 +182,16 @@ class Authenticator(common.Plugin): necessary_ports = set() if challenges.HTTP01 in self.supported_challenges: necessary_ports.add(self.config.http01_port) - if challenges.DVSNI in self.supported_challenges: + if challenges.TLSSNI01 in self.supported_challenges: necessary_ports.add(self.config.dvsni_port) return necessary_ports def more_info(self): # pylint: disable=missing-docstring return("This authenticator creates its own ephemeral TCP listener " - "on the necessary port in order to respond to incoming DVSNI " - "and HTTP01 challenges from the certificate authority. " - "Therefore, it does not rely on any existing server program.") + "on the necessary port in order to respond to incoming " + "tls-sni-01 and http-01 challenges from the certificate " + "authority. Therefore, it does not rely on any existing " + "server program.") def prepare(self): # pylint: disable=missing-docstring pass @@ -241,9 +241,11 @@ class Authenticator(common.Plugin): acme_standalone.HTTP01RequestHandler.HTTP01Resource( chall=achall.chall, response=response, validation=validation)) - else: # DVSNI - server = self.servers.run(self.config.dvsni_port, challenges.DVSNI) - response, cert, _ = achall.gen_cert_and_response(self.key) + else: # tls-sni-01 + server = self.servers.run( + self.config.dvsni_port, challenges.TLSSNI01) + response, (cert, _) = achall.response_and_validation( + cert_key=self.key) self.certs[response.z_domain] = (self.key, cert) self.served[server].add(achall) responses.append(response) diff --git a/letsencrypt/plugins/standalone_test.py b/letsencrypt/plugins/standalone_test.py index c1de52ac8..79718e244 100644 --- a/letsencrypt/plugins/standalone_test.py +++ b/letsencrypt/plugins/standalone_test.py @@ -39,8 +39,8 @@ class ServerManagerTest(unittest.TestCase): self.mgr.stop(port=port) self.assertEqual(self.mgr.running(), {}) - def test_run_stop_dvsni(self): - self._test_run_stop(challenges.DVSNI) + def test_run_stop_tls_sni_01(self): + self._test_run_stop(challenges.TLSSNI01) def test_run_stop_http_01(self): self._test_run_stop(challenges.HTTP01) @@ -73,10 +73,10 @@ class SupportedChallengesValidatorTest(unittest.TestCase): return supported_challenges_validator(data) def test_correct(self): - self.assertEqual("dvsni", self._call("dvsni")) + self.assertEqual("tls-sni-01", self._call("tls-sni-01")) self.assertEqual("http-01", self._call("http-01")) - self.assertEqual("dvsni,http-01", self._call("dvsni,http-01")) - self.assertEqual("http-01,dvsni", self._call("http-01,dvsni")) + self.assertEqual("tls-sni-01,http-01", self._call("tls-sni-01,http-01")) + self.assertEqual("http-01,tls-sni-01", self._call("http-01,tls-sni-01")) def test_unrecognized(self): assert "foo" not in challenges.Challenge.TYPES @@ -93,23 +93,23 @@ class AuthenticatorTest(unittest.TestCase): from letsencrypt.plugins.standalone import Authenticator self.config = mock.MagicMock( dvsni_port=1234, http01_port=4321, - standalone_supported_challenges="dvsni,http-01") + standalone_supported_challenges="tls-sni-01,http-01") self.auth = Authenticator(self.config, name="standalone") def test_supported_challenges(self): self.assertEqual(self.auth.supported_challenges, - set([challenges.DVSNI, challenges.HTTP01])) + set([challenges.TLSSNI01, challenges.HTTP01])) def test_more_info(self): self.assertTrue(isinstance(self.auth.more_info(), six.string_types)) def test_get_chall_pref(self): self.assertEqual(set(self.auth.get_chall_pref(domain=None)), - set([challenges.DVSNI, challenges.HTTP01])) + set([challenges.TLSSNI01, challenges.HTTP01])) @mock.patch("letsencrypt.plugins.standalone.util") def test_perform_alredy_listening(self, mock_util): - for chall, port in ((challenges.DVSNI.typ, 1234), + for chall, port in ((challenges.TLSSNI01.typ, 1234), (challenges.HTTP01.typ, 4321)): mock_util.already_listening.return_value = True self.config.standalone_supported_challenges = chall @@ -155,8 +155,8 @@ class AuthenticatorTest(unittest.TestCase): 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) - dvsni = achallenges.DVSNI( - challb=acme_util.DVSNI_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() @@ -164,19 +164,19 @@ class AuthenticatorTest(unittest.TestCase): return "server{0}".format(port) self.auth.servers.run.side_effect = _run - responses = self.auth.perform2([http_01, dvsni]) + 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.DVSNIResponse)) + self.assertTrue(isinstance(responses[1], challenges.TLSSNI01Response)) self.assertEqual(self.auth.servers.run.mock_calls, [ mock.call(4321, challenges.HTTP01), - mock.call(1234, challenges.DVSNI), + mock.call(1234, challenges.TLSSNI01), ]) self.assertEqual(self.auth.served, { - "server1234": set([dvsni]), + "server1234": set([tls_sni_01]), "server4321": set([http_01]), }) self.assertEqual(1, len(self.auth.http_01_resources)) diff --git a/tests/boulder-integration.sh b/tests/boulder-integration.sh index 18a996926..d35ecbcff 100755 --- a/tests/boulder-integration.sh +++ b/tests/boulder-integration.sh @@ -27,7 +27,7 @@ common() { "$@" } -common --domains le1.wtf --standalone-supported-challenges dvsni auth +common --domains le1.wtf --standalone-supported-challenges tls-sni-01 auth common --domains le2.wtf --standalone-supported-challenges http-01 run common -a manual -d le.wtf auth