mirror of
https://github.com/certbot/certbot.git
synced 2026-07-30 16:14:44 +02:00
tls-sni-01 for standalone
This commit is contained in:
+1
-1
@@ -135,7 +135,7 @@ Plugin A I Notes and status
|
|||||||
========== = = ================================================================
|
========== = = ================================================================
|
||||||
standalone Y N Very stable. Uses port 80 (force by
|
standalone Y N Very stable. Uses port 80 (force by
|
||||||
``--standalone-supported-challenges http-01``) or 443
|
``--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
|
apache Y Y Alpha. Automates Apache installation, works fairly well but on
|
||||||
Debian-based distributions only for now.
|
Debian-based distributions only for now.
|
||||||
webroot Y N Works with already running webserver, by writing necessary files
|
webroot Y N Works with already running webserver, by writing necessary files
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ server = https://acme-staging.api.letsencrypt.org/directory
|
|||||||
|
|
||||||
# Uncomment to use the standalone authenticator on port 443
|
# Uncomment to use the standalone authenticator on port 443
|
||||||
# authenticator = standalone
|
# authenticator = standalone
|
||||||
# standalone-supported-challenges = dvsni
|
# standalone-supported-challenges = tls-sni-01
|
||||||
|
|
||||||
# Uncomment to use the webroot authenticator. Replace webroot-path with the
|
# Uncomment to use the webroot authenticator. Replace webroot-path with the
|
||||||
# path to the public_html / webroot folder being served by your web server.
|
# path to the public_html / webroot folder being served by your web server.
|
||||||
|
|||||||
@@ -49,9 +49,10 @@ class KeyAuthorizationAnnotatedChallenge(AnnotatedChallenge):
|
|||||||
"""Client annotated `KeyAuthorizationChallenge` challenge."""
|
"""Client annotated `KeyAuthorizationChallenge` challenge."""
|
||||||
__slots__ = ('challb', 'domain', 'account_key')
|
__slots__ = ('challb', 'domain', 'account_key')
|
||||||
|
|
||||||
def response_and_validation(self):
|
def response_and_validation(self, *args, **kwargs):
|
||||||
"""Generate response and validation."""
|
"""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):
|
class DVSNI(AnnotatedChallenge):
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import six
|
|||||||
import zope.interface
|
import zope.interface
|
||||||
|
|
||||||
from acme import challenges
|
from acme import challenges
|
||||||
from acme import crypto_util as acme_crypto_util
|
|
||||||
from acme import standalone as acme_standalone
|
from acme import standalone as acme_standalone
|
||||||
|
|
||||||
from letsencrypt import errors
|
from letsencrypt import errors
|
||||||
@@ -51,19 +50,19 @@ class ServerManager(object):
|
|||||||
|
|
||||||
:param int port: Port to run the server on.
|
:param int port: Port to run the server on.
|
||||||
:param challenge_type: Subclass of `acme.challenges.Challenge`,
|
: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.
|
:returns: Server instance.
|
||||||
:rtype: ACMEServerMixin
|
:rtype: ACMEServerMixin
|
||||||
|
|
||||||
"""
|
"""
|
||||||
assert challenge_type in (challenges.DVSNI, challenges.HTTP01)
|
assert challenge_type in (challenges.TLSSNI01, challenges.HTTP01)
|
||||||
if port in self._instances:
|
if port in self._instances:
|
||||||
return self._instances[port].server
|
return self._instances[port].server
|
||||||
|
|
||||||
address = ("", port)
|
address = ("", port)
|
||||||
try:
|
try:
|
||||||
if challenge_type is challenges.DVSNI:
|
if challenge_type is challenges.TLSSNI01:
|
||||||
server = acme_standalone.DVSNIServer(address, self.certs)
|
server = acme_standalone.DVSNIServer(address, self.certs)
|
||||||
else: # challenges.HTTP01
|
else: # challenges.HTTP01
|
||||||
server = acme_standalone.HTTP01Server(
|
server = acme_standalone.HTTP01Server(
|
||||||
@@ -109,7 +108,7 @@ class ServerManager(object):
|
|||||||
in six.iteritems(self._instances))
|
in six.iteritems(self._instances))
|
||||||
|
|
||||||
|
|
||||||
SUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.HTTP01])
|
SUPPORTED_CHALLENGES = set([challenges.TLSSNI01, challenges.HTTP01])
|
||||||
|
|
||||||
|
|
||||||
def supported_challenges_validator(data):
|
def supported_challenges_validator(data):
|
||||||
@@ -138,7 +137,7 @@ class Authenticator(common.Plugin):
|
|||||||
"""Standalone Authenticator.
|
"""Standalone Authenticator.
|
||||||
|
|
||||||
This authenticator creates its own ephemeral TCP listener on the
|
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
|
challenges from the certificate authority. Therefore, it does not
|
||||||
rely on any existing server program.
|
rely on any existing server program.
|
||||||
"""
|
"""
|
||||||
@@ -150,7 +149,7 @@ class Authenticator(common.Plugin):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Authenticator, self).__init__(*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 = OpenSSL.crypto.PKey()
|
||||||
self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048)
|
self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048)
|
||||||
|
|
||||||
@@ -183,15 +182,16 @@ class Authenticator(common.Plugin):
|
|||||||
necessary_ports = set()
|
necessary_ports = set()
|
||||||
if challenges.HTTP01 in self.supported_challenges:
|
if challenges.HTTP01 in self.supported_challenges:
|
||||||
necessary_ports.add(self.config.http01_port)
|
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)
|
necessary_ports.add(self.config.dvsni_port)
|
||||||
return necessary_ports
|
return necessary_ports
|
||||||
|
|
||||||
def more_info(self): # pylint: disable=missing-docstring
|
def more_info(self): # pylint: disable=missing-docstring
|
||||||
return("This authenticator creates its own ephemeral TCP listener "
|
return("This authenticator creates its own ephemeral TCP listener "
|
||||||
"on the necessary port in order to respond to incoming DVSNI "
|
"on the necessary port in order to respond to incoming "
|
||||||
"and HTTP01 challenges from the certificate authority. "
|
"tls-sni-01 and http-01 challenges from the certificate "
|
||||||
"Therefore, it does not rely on any existing server program.")
|
"authority. Therefore, it does not rely on any existing "
|
||||||
|
"server program.")
|
||||||
|
|
||||||
def prepare(self): # pylint: disable=missing-docstring
|
def prepare(self): # pylint: disable=missing-docstring
|
||||||
pass
|
pass
|
||||||
@@ -241,9 +241,11 @@ class Authenticator(common.Plugin):
|
|||||||
acme_standalone.HTTP01RequestHandler.HTTP01Resource(
|
acme_standalone.HTTP01RequestHandler.HTTP01Resource(
|
||||||
chall=achall.chall, response=response,
|
chall=achall.chall, response=response,
|
||||||
validation=validation))
|
validation=validation))
|
||||||
else: # DVSNI
|
else: # tls-sni-01
|
||||||
server = self.servers.run(self.config.dvsni_port, challenges.DVSNI)
|
server = self.servers.run(
|
||||||
response, cert, _ = achall.gen_cert_and_response(self.key)
|
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.certs[response.z_domain] = (self.key, cert)
|
||||||
self.served[server].add(achall)
|
self.served[server].add(achall)
|
||||||
responses.append(response)
|
responses.append(response)
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ class ServerManagerTest(unittest.TestCase):
|
|||||||
self.mgr.stop(port=port)
|
self.mgr.stop(port=port)
|
||||||
self.assertEqual(self.mgr.running(), {})
|
self.assertEqual(self.mgr.running(), {})
|
||||||
|
|
||||||
def test_run_stop_dvsni(self):
|
def test_run_stop_tls_sni_01(self):
|
||||||
self._test_run_stop(challenges.DVSNI)
|
self._test_run_stop(challenges.TLSSNI01)
|
||||||
|
|
||||||
def test_run_stop_http_01(self):
|
def test_run_stop_http_01(self):
|
||||||
self._test_run_stop(challenges.HTTP01)
|
self._test_run_stop(challenges.HTTP01)
|
||||||
@@ -73,10 +73,10 @@ class SupportedChallengesValidatorTest(unittest.TestCase):
|
|||||||
return supported_challenges_validator(data)
|
return supported_challenges_validator(data)
|
||||||
|
|
||||||
def test_correct(self):
|
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("http-01", self._call("http-01"))
|
||||||
self.assertEqual("dvsni,http-01", self._call("dvsni,http-01"))
|
self.assertEqual("tls-sni-01,http-01", self._call("tls-sni-01,http-01"))
|
||||||
self.assertEqual("http-01,dvsni", self._call("http-01,dvsni"))
|
self.assertEqual("http-01,tls-sni-01", self._call("http-01,tls-sni-01"))
|
||||||
|
|
||||||
def test_unrecognized(self):
|
def test_unrecognized(self):
|
||||||
assert "foo" not in challenges.Challenge.TYPES
|
assert "foo" not in challenges.Challenge.TYPES
|
||||||
@@ -93,23 +93,23 @@ class AuthenticatorTest(unittest.TestCase):
|
|||||||
from letsencrypt.plugins.standalone import Authenticator
|
from letsencrypt.plugins.standalone import Authenticator
|
||||||
self.config = mock.MagicMock(
|
self.config = mock.MagicMock(
|
||||||
dvsni_port=1234, http01_port=4321,
|
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")
|
self.auth = Authenticator(self.config, name="standalone")
|
||||||
|
|
||||||
def test_supported_challenges(self):
|
def test_supported_challenges(self):
|
||||||
self.assertEqual(self.auth.supported_challenges,
|
self.assertEqual(self.auth.supported_challenges,
|
||||||
set([challenges.DVSNI, challenges.HTTP01]))
|
set([challenges.TLSSNI01, challenges.HTTP01]))
|
||||||
|
|
||||||
def test_more_info(self):
|
def test_more_info(self):
|
||||||
self.assertTrue(isinstance(self.auth.more_info(), six.string_types))
|
self.assertTrue(isinstance(self.auth.more_info(), six.string_types))
|
||||||
|
|
||||||
def test_get_chall_pref(self):
|
def test_get_chall_pref(self):
|
||||||
self.assertEqual(set(self.auth.get_chall_pref(domain=None)),
|
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")
|
@mock.patch("letsencrypt.plugins.standalone.util")
|
||||||
def test_perform_alredy_listening(self, mock_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)):
|
(challenges.HTTP01.typ, 4321)):
|
||||||
mock_util.already_listening.return_value = True
|
mock_util.already_listening.return_value = True
|
||||||
self.config.standalone_supported_challenges = chall
|
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'))
|
key = jose.JWK.load(test_util.load_vector('rsa512_key.pem'))
|
||||||
http_01 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
http_01 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||||
challb=acme_util.HTTP01_P, domain=domain, account_key=key)
|
challb=acme_util.HTTP01_P, domain=domain, account_key=key)
|
||||||
dvsni = achallenges.DVSNI(
|
tls_sni_01 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||||
challb=acme_util.DVSNI_P, domain=domain, account_key=key)
|
challb=acme_util.TLSSNI01_P, domain=domain, account_key=key)
|
||||||
|
|
||||||
self.auth.servers = mock.MagicMock()
|
self.auth.servers = mock.MagicMock()
|
||||||
|
|
||||||
@@ -164,19 +164,19 @@ class AuthenticatorTest(unittest.TestCase):
|
|||||||
return "server{0}".format(port)
|
return "server{0}".format(port)
|
||||||
|
|
||||||
self.auth.servers.run.side_effect = _run
|
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.assertTrue(isinstance(responses, list))
|
||||||
self.assertEqual(2, len(responses))
|
self.assertEqual(2, len(responses))
|
||||||
self.assertTrue(isinstance(responses[0], challenges.HTTP01Response))
|
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, [
|
self.assertEqual(self.auth.servers.run.mock_calls, [
|
||||||
mock.call(4321, challenges.HTTP01),
|
mock.call(4321, challenges.HTTP01),
|
||||||
mock.call(1234, challenges.DVSNI),
|
mock.call(1234, challenges.TLSSNI01),
|
||||||
])
|
])
|
||||||
self.assertEqual(self.auth.served, {
|
self.assertEqual(self.auth.served, {
|
||||||
"server1234": set([dvsni]),
|
"server1234": set([tls_sni_01]),
|
||||||
"server4321": set([http_01]),
|
"server4321": set([http_01]),
|
||||||
})
|
})
|
||||||
self.assertEqual(1, len(self.auth.http_01_resources))
|
self.assertEqual(1, len(self.auth.http_01_resources))
|
||||||
|
|||||||
@@ -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 --domains le2.wtf --standalone-supported-challenges http-01 run
|
||||||
common -a manual -d le.wtf auth
|
common -a manual -d le.wtf auth
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user