tls-sni-01 in apache and nginx plugins

This commit is contained in:
Jakub Warmuz 2015-11-07 18:10:56 +00:00
parent 5e8ed2bbd2
commit 937e3edfc1
No known key found for this signature in database
GPG key ID: 2A7BAD3A489B52EA
11 changed files with 76 additions and 79 deletions

View file

@ -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:

View file

@ -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),

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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)

View file

@ -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

View file

@ -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]))

View file

@ -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(

View file

@ -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):