mirror of
https://github.com/certbot/certbot.git
synced 2026-06-05 06:42:10 -04:00
tls-sni-01 in apache and nginx plugins
This commit is contained in:
parent
5e8ed2bbd2
commit
937e3edfc1
11 changed files with 76 additions and 79 deletions
|
|
@ -13,7 +13,6 @@ import zope.interface
|
|||
|
||||
from acme import challenges
|
||||
|
||||
from letsencrypt import achallenges
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import interfaces
|
||||
from letsencrypt import le_util
|
||||
|
|
@ -1117,7 +1116,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
###########################################################################
|
||||
def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use
|
||||
"""Return list of challenge preferences."""
|
||||
return [challenges.DVSNI]
|
||||
return [challenges.TLSSNI01]
|
||||
|
||||
def perform(self, achalls):
|
||||
"""Perform the configuration related challenge.
|
||||
|
|
@ -1132,11 +1131,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
apache_dvsni = dvsni.ApacheDvsni(self)
|
||||
|
||||
for i, achall in enumerate(achalls):
|
||||
if isinstance(achall, achallenges.DVSNI):
|
||||
# Currently also have dvsni hold associated index
|
||||
# of the challenge. This helps to put all of the responses back
|
||||
# together when they are all complete.
|
||||
apache_dvsni.add_chall(achall, i)
|
||||
# Currently also have dvsni hold associated index
|
||||
# of the challenge. This helps to put all of the responses back
|
||||
# together when they are all complete.
|
||||
apache_dvsni.add_chall(achall, i)
|
||||
|
||||
sni_response = apache_dvsni.perform()
|
||||
if sni_response:
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ from letsencrypt_apache import obj
|
|||
from letsencrypt_apache import parser
|
||||
|
||||
|
||||
class ApacheDvsni(common.Dvsni):
|
||||
class ApacheDvsni(common.TLSSNI01):
|
||||
"""Class performs DVSNI challenges within the Apache configurator.
|
||||
|
||||
:ivar configurator: ApacheConfigurator object
|
||||
:type configurator: :class:`~apache.configurator.ApacheConfigurator`
|
||||
|
||||
:ivar list achalls: Annotated :class:`~letsencrypt.achallenges.DVSNI`
|
||||
challenges.
|
||||
:ivar list achalls: Annotated tls-sni-01
|
||||
(`.KeyAuthorizationAnnotatedChallenge`) challenges.
|
||||
|
||||
:param list indices: Meant to hold indices of challenges in a
|
||||
larger array. ApacheDvsni is capable of solving many challenges
|
||||
|
|
@ -145,8 +145,8 @@ class ApacheDvsni(common.Dvsni):
|
|||
def _get_config_text(self, achall, ip_addrs):
|
||||
"""Chocolate virtual server configuration text
|
||||
|
||||
:param achall: Annotated DVSNI challenge.
|
||||
:type achall: :class:`letsencrypt.achallenges.DVSNI`
|
||||
:param .KeyAuthorizationAnnotatedChallenge achall: Annotated
|
||||
DVSNI challenge.
|
||||
|
||||
:param list ip_addrs: addresses of challenged domain
|
||||
:class:`list` of type `~.obj.Addr`
|
||||
|
|
@ -165,7 +165,7 @@ class ApacheDvsni(common.Dvsni):
|
|||
# https://docs.python.org/2.7/reference/lexical_analysis.html
|
||||
return self.VHOST_TEMPLATE.format(
|
||||
vhost=ips,
|
||||
server_name=achall.gen_response(achall.account_key).z_domain,
|
||||
server_name=achall.response(achall.account_key).z_domain,
|
||||
ssl_options_conf_path=self.configurator.mod_ssl_conf,
|
||||
cert_path=self.get_cert_path(achall),
|
||||
key_path=self.get_key_path(achall),
|
||||
|
|
|
|||
|
|
@ -382,8 +382,8 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
account_key, achall1, achall2 = self.get_achalls()
|
||||
|
||||
dvsni_ret_val = [
|
||||
achall1.gen_response(account_key),
|
||||
achall2.gen_response(account_key),
|
||||
achall1.response(account_key),
|
||||
achall2.response(account_key),
|
||||
]
|
||||
|
||||
mock_dvsni_perform.return_value = dvsni_ret_val
|
||||
|
|
@ -592,15 +592,15 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
def get_achalls(self):
|
||||
"""Return testing achallenges."""
|
||||
account_key = self.rsa512jwk
|
||||
achall1 = achallenges.DVSNI(
|
||||
achall1 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.DVSNI(
|
||||
challenges.TLSSNI01(
|
||||
token="jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q"),
|
||||
"pending"),
|
||||
domain="encryption-example.demo", account_key=account_key)
|
||||
achall2 = achallenges.DVSNI(
|
||||
achall2 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.DVSNI(
|
||||
challenges.TLSSNI01(
|
||||
token="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"),
|
||||
"pending"),
|
||||
domain="letsencrypt.demo", account_key=account_key)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ from letsencrypt_apache.tests import util
|
|||
class DvsniPerformTest(util.ApacheTest):
|
||||
"""Test the ApacheDVSNI challenge."""
|
||||
|
||||
auth_key = common_test.DvsniTest.auth_key
|
||||
achalls = common_test.DvsniTest.achalls
|
||||
auth_key = common_test.TLSSNI01Test.auth_key
|
||||
achalls = common_test.TLSSNI01Test.achalls
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(DvsniPerformTest, self).setUp()
|
||||
|
|
@ -46,7 +46,7 @@ class DvsniPerformTest(util.ApacheTest):
|
|||
|
||||
achall = self.achalls[0]
|
||||
self.sni.add_chall(achall)
|
||||
response = self.achalls[0].gen_response(self.auth_key)
|
||||
response = self.achalls[0].response(self.auth_key)
|
||||
mock_setup_cert = mock.MagicMock(return_value=response)
|
||||
# pylint: disable=protected-access
|
||||
self.sni._setup_challenge_cert = mock_setup_cert
|
||||
|
|
@ -72,7 +72,7 @@ class DvsniPerformTest(util.ApacheTest):
|
|||
acme_responses = []
|
||||
for achall in self.achalls:
|
||||
self.sni.add_chall(achall)
|
||||
acme_responses.append(achall.gen_response(self.auth_key))
|
||||
acme_responses.append(achall.response(self.auth_key))
|
||||
|
||||
mock_setup_cert = mock.MagicMock(side_effect=acme_responses)
|
||||
# pylint: disable=protected-access
|
||||
|
|
@ -100,7 +100,7 @@ class DvsniPerformTest(util.ApacheTest):
|
|||
z_domains = []
|
||||
for achall in self.achalls:
|
||||
self.sni.add_chall(achall)
|
||||
z_domain = achall.gen_response(self.auth_key).z_domain
|
||||
z_domain = achall.response(self.auth_key).z_domain
|
||||
z_domains.append(set([z_domain]))
|
||||
|
||||
self.sni._mod_config() # pylint: disable=protected-access
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ def test_authenticator(plugin, config, temp_dir):
|
|||
"Plugin failed to complete %s for %s in %s",
|
||||
type(achalls[i]), achalls[i].domain, config)
|
||||
success = False
|
||||
elif isinstance(responses[i], challenges.DVSNIResponse):
|
||||
elif isinstance(responses[i], challenges.TLSSNI01):
|
||||
verify = functools.partial(responses[i].simple_verify, achalls[i],
|
||||
achalls[i].domain,
|
||||
util.JWK.public_key(),
|
||||
|
|
@ -68,10 +68,10 @@ def test_authenticator(plugin, config, temp_dir):
|
|||
port=plugin.https_port)
|
||||
if _try_until_true(verify):
|
||||
logger.info(
|
||||
"DVSNI verification for %s succeeded", achalls[i].domain)
|
||||
"tls-sni-01 verification for %s succeeded", achalls[i].domain)
|
||||
else:
|
||||
logger.error(
|
||||
"DVSNI verification for %s in %s failed",
|
||||
"tls-sni-01 verification for %s in %s failed",
|
||||
achalls[i].domain, config)
|
||||
success = False
|
||||
|
||||
|
|
@ -99,12 +99,12 @@ def _create_achalls(plugin):
|
|||
for domain in names:
|
||||
prefs = plugin.get_chall_pref(domain)
|
||||
for chall_type in prefs:
|
||||
if chall_type == challenges.DVSNI:
|
||||
chall = challenges.DVSNI(
|
||||
token=os.urandom(challenges.DVSNI.TOKEN_SIZE))
|
||||
if chall_type == challenges.TLSSNI01:
|
||||
chall = challenges.TLSSNI01(
|
||||
token=os.urandom(challenges.TLSSNI01.TOKEN_SIZE))
|
||||
challb = acme_util.chall_to_challb(
|
||||
chall, messages.STATUS_PENDING)
|
||||
achall = achallenges.DVSNI(
|
||||
achall = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=challb, domain=domain, account_key=util.JWK)
|
||||
achalls.append(achall)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import zope.interface
|
|||
from acme import challenges
|
||||
from acme import crypto_util as acme_crypto_util
|
||||
|
||||
from letsencrypt import achallenges
|
||||
from letsencrypt import constants as core_constants
|
||||
from letsencrypt import crypto_util
|
||||
from letsencrypt import errors
|
||||
|
|
@ -537,7 +536,7 @@ class NginxConfigurator(common.Plugin):
|
|||
###########################################################################
|
||||
def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use
|
||||
"""Return list of challenge preferences."""
|
||||
return [challenges.DVSNI]
|
||||
return [challenges.TLSSNI01]
|
||||
|
||||
# Entry point in main.py for performing challenges
|
||||
def perform(self, achalls):
|
||||
|
|
@ -553,11 +552,10 @@ class NginxConfigurator(common.Plugin):
|
|||
nginx_dvsni = dvsni.NginxDvsni(self)
|
||||
|
||||
for i, achall in enumerate(achalls):
|
||||
if isinstance(achall, achallenges.DVSNI):
|
||||
# Currently also have dvsni hold associated index
|
||||
# of the challenge. This helps to put all of the responses back
|
||||
# together when they are all complete.
|
||||
nginx_dvsni.add_chall(achall, i)
|
||||
# Currently also have dvsni hold associated index
|
||||
# of the challenge. This helps to put all of the responses back
|
||||
# together when they are all complete.
|
||||
nginx_dvsni.add_chall(achall, i)
|
||||
|
||||
sni_response = nginx_dvsni.perform()
|
||||
# Must restart in order to activate the challenges.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from letsencrypt_nginx import nginxparser
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NginxDvsni(common.Dvsni):
|
||||
class NginxDvsni(common.TLSSNI01):
|
||||
"""Class performs DVSNI challenges within the Nginx configurator.
|
||||
|
||||
:ivar configurator: NginxConfigurator object
|
||||
|
|
@ -141,7 +141,7 @@ class NginxDvsni(common.Dvsni):
|
|||
block = [['listen', str(addr)] for addr in addrs]
|
||||
|
||||
block.extend([['server_name',
|
||||
achall.gen_response(achall.account_key).z_domain],
|
||||
achall.response(achall.account_key).z_domain],
|
||||
['include', self.configurator.parser.loc["ssl_options"]],
|
||||
# access and error logs necessary for
|
||||
# integration testing (non-root)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
errors.PluginError, self.config.enhance, 'myhost', 'redirect')
|
||||
|
||||
def test_get_chall_pref(self):
|
||||
self.assertEqual([challenges.DVSNI],
|
||||
self.assertEqual([challenges.TLSSNI01],
|
||||
self.config.get_chall_pref('myhost'))
|
||||
|
||||
def test_save(self):
|
||||
|
|
@ -210,22 +210,22 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
def test_perform(self, mock_restart, mock_dvsni_perform):
|
||||
# Only tests functionality specific to configurator.perform
|
||||
# Note: As more challenges are offered this will have to be expanded
|
||||
achall1 = achallenges.DVSNI(
|
||||
achall1 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=messages.ChallengeBody(
|
||||
chall=challenges.DVSNI(token="kNdwjwOeX0I_A8DXt9Msmg"),
|
||||
chall=challenges.TLSSNI01(token="kNdwjwOeX0I_A8DXt9Msmg"),
|
||||
uri="https://ca.org/chall0_uri",
|
||||
status=messages.Status("pending"),
|
||||
), domain="localhost", account_key=self.rsa512jwk)
|
||||
achall2 = achallenges.DVSNI(
|
||||
achall2 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=messages.ChallengeBody(
|
||||
chall=challenges.DVSNI(token="m8TdO1qik4JVFtgPPurJmg"),
|
||||
chall=challenges.TLSSNI01(token="m8TdO1qik4JVFtgPPurJmg"),
|
||||
uri="https://ca.org/chall1_uri",
|
||||
status=messages.Status("pending"),
|
||||
), domain="example.com", account_key=self.rsa512jwk)
|
||||
|
||||
dvsni_ret_val = [
|
||||
achall1.gen_response(self.rsa512jwk),
|
||||
achall2.gen_response(self.rsa512jwk),
|
||||
achall1.response(self.rsa512jwk),
|
||||
achall2.response(self.rsa512jwk),
|
||||
]
|
||||
|
||||
mock_dvsni_perform.return_value = dvsni_ret_val
|
||||
|
|
|
|||
|
|
@ -19,22 +19,22 @@ from letsencrypt_nginx.tests import util
|
|||
class DvsniPerformTest(util.NginxTest):
|
||||
"""Test the NginxDVSNI challenge."""
|
||||
|
||||
account_key = common_test.DvsniTest.auth_key
|
||||
account_key = common_test.TLSSNI01Test.auth_key
|
||||
achalls = [
|
||||
achallenges.DVSNI(
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.DVSNI(token="kNdwjwOeX0I_A8DXt9Msmg"), "pending"),
|
||||
challenges.TLSSNI01(token="kNdwjwOeX0I_A8DXt9Msmg"), "pending"),
|
||||
domain="www.example.com", account_key=account_key),
|
||||
achallenges.DVSNI(
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.DVSNI(
|
||||
challenges.TLSSNI01(
|
||||
token="\xba\xa9\xda?<m\xaewmx\xea\xad\xadv\xf4\x02\xc9y"
|
||||
"\x80\xe2_X\t\xe7\xc7\xa4\t\xca\xf7&\x945"
|
||||
), "pending"),
|
||||
domain="blah", account_key=account_key),
|
||||
achallenges.DVSNI(
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.DVSNI(
|
||||
challenges.TLSSNI01(
|
||||
token="\x8c\x8a\xbf_-f\\cw\xee\xd6\xf8/\xa5\xe3\xfd"
|
||||
"\xeb9\xf1\xf5\xb9\xefVM\xc9w\xa4u\x9c\xe1\x87\xb4"
|
||||
), "pending"),
|
||||
|
|
@ -70,7 +70,7 @@ class DvsniPerformTest(util.NginxTest):
|
|||
@mock.patch("letsencrypt_nginx.configurator.NginxConfigurator.save")
|
||||
def test_perform1(self, mock_save):
|
||||
self.sni.add_chall(self.achalls[0])
|
||||
response = self.achalls[0].gen_response(self.account_key)
|
||||
response = self.achalls[0].response(self.account_key)
|
||||
mock_setup_cert = mock.MagicMock(return_value=response)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
|
@ -92,7 +92,7 @@ class DvsniPerformTest(util.NginxTest):
|
|||
acme_responses = []
|
||||
for achall in self.achalls:
|
||||
self.sni.add_chall(achall)
|
||||
acme_responses.append(achall.gen_response(self.account_key))
|
||||
acme_responses.append(achall.response(self.account_key))
|
||||
|
||||
mock_setup_cert = mock.MagicMock(side_effect=acme_responses)
|
||||
# pylint: disable=protected-access
|
||||
|
|
@ -139,9 +139,9 @@ class DvsniPerformTest(util.NginxTest):
|
|||
|
||||
for vhost in vhs:
|
||||
if vhost.addrs == set(v_addr1):
|
||||
response = self.achalls[0].gen_response(self.account_key)
|
||||
response = self.achalls[0].response(self.account_key)
|
||||
else:
|
||||
response = self.achalls[2].gen_response(self.account_key)
|
||||
response = self.achalls[2].response(self.account_key)
|
||||
self.assertEqual(vhost.addrs, set(v_addr2))
|
||||
self.assertEqual(vhost.names, set([response.z_domain]))
|
||||
|
||||
|
|
|
|||
|
|
@ -135,22 +135,22 @@ class Addr(object):
|
|||
return self.__class__((self.tup[0], port))
|
||||
|
||||
|
||||
class Dvsni(object):
|
||||
"""Class that perform DVSNI challenges."""
|
||||
class TLSSNI01(object):
|
||||
"""Class that performs tls-sni-01 challenges."""
|
||||
|
||||
def __init__(self, configurator):
|
||||
self.configurator = configurator
|
||||
self.achalls = []
|
||||
self.indices = []
|
||||
self.challenge_conf = os.path.join(
|
||||
configurator.config.config_dir, "le_dvsni_cert_challenge.conf")
|
||||
configurator.config.config_dir, "le_tls_sni_01_cert_challenge.conf")
|
||||
# self.completed = 0
|
||||
|
||||
def add_chall(self, achall, idx=None):
|
||||
"""Add challenge to DVSNI object to perform at once.
|
||||
"""Add challenge to TLSSNI01 object to perform at once.
|
||||
|
||||
:param achall: Annotated DVSNI challenge.
|
||||
:type achall: :class:`letsencrypt.achallenges.DVSNI`
|
||||
:param .KeyAuthorizationAnnotatedChallenge achall: Annotated
|
||||
TLSSNI01 challenge.
|
||||
|
||||
:param int idx: index to challenge in a larger array
|
||||
|
||||
|
|
@ -162,8 +162,8 @@ class Dvsni(object):
|
|||
def get_cert_path(self, achall):
|
||||
"""Returns standardized name for challenge certificate.
|
||||
|
||||
:param achall: Annotated DVSNI challenge.
|
||||
:type achall: :class:`letsencrypt.achallenges.DVSNI`
|
||||
:param .KeyAuthorizationAnnotatedChallenge achall: Annotated
|
||||
tls-sni-01 challenge.
|
||||
|
||||
:returns: certificate file name
|
||||
:rtype: str
|
||||
|
|
@ -177,7 +177,7 @@ class Dvsni(object):
|
|||
return os.path.join(self.configurator.config.work_dir,
|
||||
achall.chall.encode("token") + '.pem')
|
||||
|
||||
def _setup_challenge_cert(self, achall, s=None):
|
||||
def _setup_challenge_cert(self, achall, cert_key=None):
|
||||
|
||||
"""Generate and write out challenge certificate."""
|
||||
cert_path = self.get_cert_path(achall)
|
||||
|
|
@ -186,7 +186,8 @@ class Dvsni(object):
|
|||
self.configurator.reverter.register_file_creation(True, key_path)
|
||||
self.configurator.reverter.register_file_creation(True, cert_path)
|
||||
|
||||
response, cert, key = achall.gen_cert_and_response(s)
|
||||
response, (cert, key) = achall.response_and_validation(
|
||||
cert_key=cert_key)
|
||||
cert_pem = OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||
key_pem = OpenSSL.crypto.dump_privatekey(
|
||||
|
|
|
|||
|
|
@ -115,24 +115,24 @@ class AddrTest(unittest.TestCase):
|
|||
self.assertEqual(set_a, set_b)
|
||||
|
||||
|
||||
class DvsniTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.plugins.common.DvsniTest."""
|
||||
class TLSSNI01Test(unittest.TestCase):
|
||||
"""Tests for letsencrypt.plugins.common.TLSSNI01."""
|
||||
|
||||
auth_key = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
|
||||
achalls = [
|
||||
achallenges.DVSNI(
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.DVSNI(token=b'dvsni1'), "pending"),
|
||||
challenges.TLSSNI01(token=b'token1'), "pending"),
|
||||
domain="encryption-example.demo", account_key=auth_key),
|
||||
achallenges.DVSNI(
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.DVSNI(token=b'dvsni2'), "pending"),
|
||||
challenges.TLSSNI01(token=b'token2'), "pending"),
|
||||
domain="letsencrypt.demo", account_key=auth_key),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.plugins.common import Dvsni
|
||||
self.sni = Dvsni(configurator=mock.MagicMock())
|
||||
from letsencrypt.plugins.common import TLSSNI01
|
||||
self.sni = TLSSNI01(configurator=mock.MagicMock())
|
||||
|
||||
def test_add_chall(self):
|
||||
self.sni.add_chall(self.achalls[0], 0)
|
||||
|
|
@ -146,11 +146,11 @@ class DvsniTest(unittest.TestCase):
|
|||
# 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.DVSNIResponse(validation=mock.Mock())
|
||||
response = challenges.TLSSNI01Response()
|
||||
achall = mock.MagicMock()
|
||||
key = test_util.load_pyopenssl_private_key("rsa512_key.pem")
|
||||
achall.gen_cert_and_response.return_value = (
|
||||
response, test_util.load_cert("cert.pem"), key)
|
||||
achall.response_and_validation.return_value = (
|
||||
response, (test_util.load_cert("cert.pem"), key))
|
||||
|
||||
with mock.patch("letsencrypt.plugins.common.open",
|
||||
mock_open, create=True):
|
||||
|
|
|
|||
Loading…
Reference in a new issue