http-01 for standalone

This commit is contained in:
Jakub Warmuz
2015-11-01 11:01:22 +00:00
parent fd209eab66
commit ea3611afe6
7 changed files with 66 additions and 68 deletions
+17 -19
View File
@@ -14,7 +14,6 @@ from acme import challenges
from acme import crypto_util as acme_crypto_util
from acme import standalone as acme_standalone
from letsencrypt import achallenges
from letsencrypt import errors
from letsencrypt import interfaces
@@ -33,7 +32,7 @@ class ServerManager(object):
`acme.crypto_util.SSLSocket.certs` and
`acme.crypto_util.SSLSocket.simple_http_resources` respectively. All
created servers share the same certificates and resources, so if
you're running both TLS and non-TLS instances, SimpleHTTP handlers
you're running both TLS and non-TLS instances, HTTP01 handlers
will serve the same URLs!
"""
@@ -52,13 +51,13 @@ class ServerManager(object):
:param int port: Port to run the server on.
:param challenge_type: Subclass of `acme.challenges.Challenge`,
either `acme.challenge.SimpleHTTP` or `acme.challenges.DVSNI`.
either `acme.challenge.HTTP01` or `acme.challenges.DVSNI`.
:returns: Server instance.
:rtype: ACMEServerMixin
"""
assert challenge_type in (challenges.DVSNI, challenges.SimpleHTTP)
assert challenge_type in (challenges.DVSNI, challenges.HTTP01)
if port in self._instances:
return self._instances[port].server
@@ -66,8 +65,8 @@ class ServerManager(object):
try:
if challenge_type is challenges.DVSNI:
server = acme_standalone.DVSNIServer(address, self.certs)
else: # challenges.SimpleHTTP
server = acme_standalone.SimpleHTTPServer(
else: # challenges.HTTP01
server = acme_standalone.HTTP01Server(
address, self.simple_http_resources)
except socket.error as error:
raise errors.StandaloneBindError(error, port)
@@ -110,7 +109,7 @@ class ServerManager(object):
in six.iteritems(self._instances))
SUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.SimpleHTTP])
SUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.HTTP01])
def supported_challenges_validator(data):
@@ -139,7 +138,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 SimpleHTTP
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.
"""
@@ -151,10 +150,10 @@ class Authenticator(common.Plugin):
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
# one self-signed key for all DVSNI and SimpleHTTP certificates
# one self-signed key for all DVSNI and HTTP01 certificates
self.key = OpenSSL.crypto.PKey()
self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048)
# TODO: generate only when the first SimpleHTTP challenge is solved
# TODO: generate only when the first HTTP01 challenge is solved
self.simple_http_cert = acme_crypto_util.gen_ss_cert(
self.key, domains=["temp server"])
@@ -185,7 +184,7 @@ class Authenticator(common.Plugin):
@property
def _necessary_ports(self):
necessary_ports = set()
if challenges.SimpleHTTP in self.supported_challenges:
if challenges.HTTP01 in self.supported_challenges:
necessary_ports.add(self.config.simple_http_port)
if challenges.DVSNI in self.supported_challenges:
necessary_ports.add(self.config.dvsni_port)
@@ -193,9 +192,9 @@ class Authenticator(common.Plugin):
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 SimpleHTTP 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 DVSNI "
"and HTTP01 challenges from the certificate authority. "
"Therefore, it does not rely on any existing server program.")
def prepare(self): # pylint: disable=missing-docstring
pass
@@ -237,13 +236,12 @@ class Authenticator(common.Plugin):
responses = []
for achall in achalls:
if isinstance(achall, achallenges.SimpleHTTP):
if isinstance(achall.chall, challenges.HTTP01):
server = self.servers.run(
self.config.simple_http_port, challenges.SimpleHTTP)
response, validation = achall.gen_response_and_validation(
tls=False)
self.config.simple_http_port, challenges.HTTP01)
response, validation = achall.response_and_validation()
self.simple_http_resources.add(
acme_standalone.SimpleHTTPRequestHandler.SimpleHTTPResource(
acme_standalone.HTTP01RequestHandler.HTTP01Resource(
chall=achall.chall, response=response,
validation=validation))
cert = self.simple_http_cert
+19 -18
View File
@@ -43,12 +43,12 @@ class ServerManagerTest(unittest.TestCase):
self._test_run_stop(challenges.DVSNI)
def test_run_stop_simplehttp(self):
self._test_run_stop(challenges.SimpleHTTP)
self._test_run_stop(challenges.HTTP01)
def test_run_idempotent(self):
server = self.mgr.run(port=0, challenge_type=challenges.SimpleHTTP)
server = self.mgr.run(port=0, challenge_type=challenges.HTTP01)
port = server.socket.getsockname()[1] # pylint: disable=no-member
server2 = self.mgr.run(port=port, challenge_type=challenges.SimpleHTTP)
server2 = self.mgr.run(port=port, challenge_type=challenges.HTTP01)
self.assertEqual(self.mgr.running(), {port: server})
self.assertTrue(server is server2)
self.mgr.stop(port)
@@ -60,7 +60,7 @@ class ServerManagerTest(unittest.TestCase):
port = some_server.getsockname()[1]
self.assertRaises(
errors.StandaloneBindError, self.mgr.run, port,
challenge_type=challenges.SimpleHTTP)
challenge_type=challenges.HTTP01)
self.assertEqual(self.mgr.running(), {})
@@ -74,9 +74,9 @@ class SupportedChallengesValidatorTest(unittest.TestCase):
def test_correct(self):
self.assertEqual("dvsni", self._call("dvsni"))
self.assertEqual("simpleHttp", self._call("simpleHttp"))
self.assertEqual("dvsni,simpleHttp", self._call("dvsni,simpleHttp"))
self.assertEqual("simpleHttp,dvsni", self._call("simpleHttp,dvsni"))
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"))
def test_unrecognized(self):
assert "foo" not in challenges.Challenge.TYPES
@@ -91,25 +91,26 @@ class AuthenticatorTest(unittest.TestCase):
def setUp(self):
from letsencrypt.plugins.standalone import Authenticator
self.config = mock.MagicMock(dvsni_port=1234, simple_http_port=4321,
standalone_supported_challenges="dvsni,simpleHttp")
self.config = mock.MagicMock(
dvsni_port=1234, simple_http_port=4321,
standalone_supported_challenges="dvsni,http-01")
self.auth = Authenticator(self.config, name="standalone")
def test_supported_challenges(self):
self.assertEqual(self.auth.supported_challenges,
set([challenges.DVSNI, challenges.SimpleHTTP]))
set([challenges.DVSNI, 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.SimpleHTTP]))
set([challenges.DVSNI, challenges.HTTP01]))
@mock.patch("letsencrypt.plugins.standalone.util")
def test_perform_alredy_listening(self, mock_util):
for chall, port in ((challenges.DVSNI.typ, 1234),
(challenges.SimpleHTTP.typ, 4321)):
(challenges.HTTP01.typ, 4321)):
mock_util.already_listening.return_value = True
self.config.standalone_supported_challenges = chall
self.assertRaises(
@@ -152,8 +153,8 @@ class AuthenticatorTest(unittest.TestCase):
def test_perform2(self):
domain = b'localhost'
key = jose.JWK.load(test_util.load_vector('rsa512_key.pem'))
simple_http = achallenges.SimpleHTTP(
challb=acme_util.SIMPLE_HTTP_P, domain=domain, account_key=key)
simple_http = 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)
@@ -167,11 +168,11 @@ class AuthenticatorTest(unittest.TestCase):
self.assertTrue(isinstance(responses, list))
self.assertEqual(2, len(responses))
self.assertTrue(isinstance(responses[0], challenges.SimpleHTTPResponse))
self.assertTrue(isinstance(responses[0], challenges.HTTP01Response))
self.assertTrue(isinstance(responses[1], challenges.DVSNIResponse))
self.assertEqual(self.auth.servers.run.mock_calls, [
mock.call(4321, challenges.SimpleHTTP),
mock.call(4321, challenges.HTTP01),
mock.call(1234, challenges.DVSNI),
])
self.assertEqual(self.auth.served, {
@@ -181,8 +182,8 @@ class AuthenticatorTest(unittest.TestCase):
self.assertEqual(1, len(self.auth.simple_http_resources))
self.assertEqual(2, len(self.auth.certs))
self.assertEqual(list(self.auth.simple_http_resources), [
acme_standalone.SimpleHTTPRequestHandler.SimpleHTTPResource(
acme_util.SIMPLE_HTTP, responses[0], mock.ANY)])
acme_standalone.HTTP01RequestHandler.HTTP01Resource(
acme_util.HTTP01, responses[0], mock.ANY)])
def test_cleanup(self):
self.auth.servers = mock.Mock()