mirror of
https://github.com/certbot/certbot.git
synced 2026-05-28 04:34:11 -04:00
Merge master in so Travis will test this PR.
This commit is contained in:
commit
f2586fbc11
19 changed files with 249 additions and 136 deletions
|
|
@ -226,7 +226,7 @@ class JSONObjectWithFields(util.ImmutableMap, interfaces.JSONDeSerializable):
|
|||
|
||||
:param str name: Name of the field to be encoded.
|
||||
|
||||
:raises erors.SerializationError: if field cannot be serialized
|
||||
:raises errors.SerializationError: if field cannot be serialized
|
||||
:raises errors.Error: if field could not be found
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class ImmutableMap(collections.Mapping, collections.Hashable):
|
|||
"""Immutable key to value mapping with attribute access."""
|
||||
|
||||
__slots__ = ()
|
||||
"""Must be overriden in subclasses."""
|
||||
"""Must be overridden in subclasses."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if set(kwargs) != set(self.__slots__):
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ class Error(jose.JSONObjectWithFields, errors.Error):
|
|||
('connection', 'The server could not connect to the client to '
|
||||
'verify the domain'),
|
||||
('dnssec', 'The server could not validate a DNSSEC signed domain'),
|
||||
('invalidEmail',
|
||||
'The provided email for a registration was invalid'),
|
||||
('malformed', 'The request message was malformed'),
|
||||
('rateLimited', 'There were too many requests of a given type'),
|
||||
('serverInternal', 'The server experienced an internal error'),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ version = '0.2.0.dev0'
|
|||
install_requires = [
|
||||
# load_pem_private/public_key (>=0.6)
|
||||
# rsa_recover_prime_factors (>=0.8)
|
||||
'cryptography>=0.8',
|
||||
'cryptography>=0.8,<1.2',
|
||||
# Connection.set_tlsext_host_name (>=0.13), X509Req.get_extensions (>=0.15)
|
||||
'PyOpenSSL>=0.15',
|
||||
'pyrfc3339',
|
||||
|
|
@ -33,7 +33,7 @@ else:
|
|||
|
||||
# Keep in sync with conditional_requirements.py.
|
||||
if sys.version_info < (2, 7, 9):
|
||||
# For secure SSL connexion with Python 2.7 (InsecurePlatformWarning)
|
||||
# For secure SSL connection with Python 2.7 (InsecurePlatformWarning)
|
||||
install_requires.append('ndg-httpsclient')
|
||||
install_requires.append('pyasn1')
|
||||
|
||||
|
|
|
|||
|
|
@ -374,12 +374,12 @@ OS-level dependencies can be installed like so:
|
|||
In general...
|
||||
|
||||
* ``sudo`` is required as a suggested way of running privileged process
|
||||
* `Python`_ 2.6/2.7 is required
|
||||
* `Augeas`_ is required for the Python bindings
|
||||
* ``virtualenv`` and ``pip`` are used for managing other python library
|
||||
dependencies
|
||||
|
||||
What follow are OS-specific notes for the :ref:`developers <hacking>` reference.
|
||||
|
||||
.. _Python: https://wiki.python.org/moin/BeginnersGuide/Download
|
||||
.. _Augeas: http://augeas.net/
|
||||
.. _Virtualenv: https://virtualenv.pypa.io
|
||||
|
||||
|
|
|
|||
|
|
@ -1302,6 +1302,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
"""
|
||||
self.config_test()
|
||||
logger.debug(self.reverter.view_config_changes(for_logging=True))
|
||||
self._reload()
|
||||
|
||||
def _reload(self):
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
"""A class that performs TLS-SNI-01 challenges for Apache"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
from letsencrypt.plugins import common
|
||||
|
||||
from letsencrypt_apache import obj
|
||||
from letsencrypt_apache import parser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ApacheTlsSni01(common.TLSSNI01):
|
||||
"""Class that performs TLS-SNI-01 challenges within the Apache configurator
|
||||
|
|
@ -104,6 +106,7 @@ class ApacheTlsSni01(common.TLSSNI01):
|
|||
self.configurator.reverter.register_file_creation(
|
||||
True, self.challenge_conf)
|
||||
|
||||
logger.debug("writing a config file with text: %s", config_text)
|
||||
with open(self.challenge_conf, "w") as new_conf:
|
||||
new_conf.write(config_text)
|
||||
|
||||
|
|
|
|||
|
|
@ -311,17 +311,11 @@ class NginxConfigurator(common.Plugin):
|
|||
"""
|
||||
snakeoil_cert, snakeoil_key = self._get_snakeoil_paths()
|
||||
ssl_block = [['listen', '{0} ssl'.format(self.config.tls_sni_01_port)],
|
||||
# access and error logs necessary for integration
|
||||
# testing (non-root)
|
||||
['access_log', os.path.join(
|
||||
self.config.work_dir, 'access.log')],
|
||||
['error_log', os.path.join(
|
||||
self.config.work_dir, 'error.log')],
|
||||
['ssl_certificate', snakeoil_cert],
|
||||
['ssl_certificate_key', snakeoil_key],
|
||||
['include', self.parser.loc["ssl_options"]]]
|
||||
self.parser.add_server_directives(
|
||||
vhost.filep, vhost.names, ssl_block)
|
||||
vhost.filep, vhost.names, ssl_block, replace=False)
|
||||
vhost.ssl = True
|
||||
vhost.raw.extend(ssl_block)
|
||||
vhost.addrs.add(obj.Addr(
|
||||
|
|
@ -384,7 +378,7 @@ class NginxConfigurator(common.Plugin):
|
|||
[['return', '301 https://$host$request_uri']]
|
||||
]]
|
||||
self.parser.add_server_directives(
|
||||
vhost.filep, vhost.names, redirect_block)
|
||||
vhost.filep, vhost.names, redirect_block, replace=False)
|
||||
logger.info("Redirecting all traffic to ssl in %s", vhost.filep)
|
||||
|
||||
######################################
|
||||
|
|
@ -393,11 +387,10 @@ class NginxConfigurator(common.Plugin):
|
|||
def restart(self):
|
||||
"""Restarts nginx server.
|
||||
|
||||
:returns: Success
|
||||
:rtype: bool
|
||||
:raises .errors.MisconfigurationError: If either the reload fails.
|
||||
|
||||
"""
|
||||
return nginx_restart(self.conf('ctl'), self.nginx_conf)
|
||||
nginx_restart(self.conf('ctl'), self.nginx_conf)
|
||||
|
||||
def config_test(self): # pylint: disable=no-self-use
|
||||
"""Check the configuration of Nginx for errors.
|
||||
|
|
@ -631,19 +624,16 @@ def nginx_restart(nginx_ctl, nginx_conf="/etc/nginx.conf"):
|
|||
|
||||
if nginx_proc.returncode != 0:
|
||||
# Enter recovery routine...
|
||||
logger.error("Nginx Restart Failed!\n%s\n%s", stdout, stderr)
|
||||
return False
|
||||
raise errors.MisconfigurationError(
|
||||
"nginx restart failed:\n%s\n%s" % (stdout, stderr))
|
||||
|
||||
except (OSError, ValueError):
|
||||
logger.fatal("Nginx Restart Failed - Please Check the Configuration")
|
||||
sys.exit(1)
|
||||
raise errors.MisconfigurationError("nginx restart failed")
|
||||
# Nginx can take a moment to recognize a newly added TLS SNI servername, so sleep
|
||||
# for a second. TODO: Check for expected servername and loop until it
|
||||
# appears or return an error if looping too long.
|
||||
time.sleep(1)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def temp_install(options_ssl):
|
||||
"""Temporary install for convenience."""
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ class NginxParser(object):
|
|||
if ext:
|
||||
filename = filename + os.path.extsep + ext
|
||||
try:
|
||||
logger.debug('Dumping to %s:\n%s', filename, nginxparser.dumps(tree))
|
||||
with open(filename, 'w') as _file:
|
||||
nginxparser.dump(tree, _file)
|
||||
except IOError:
|
||||
|
|
@ -252,7 +253,7 @@ class NginxParser(object):
|
|||
return server_names == names
|
||||
|
||||
def add_server_directives(self, filename, names, directives,
|
||||
replace=False):
|
||||
replace):
|
||||
"""Add or replace directives in the first server block with names.
|
||||
|
||||
..note :: If replace is True, this raises a misconfiguration error
|
||||
|
|
@ -269,20 +270,27 @@ class NginxParser(object):
|
|||
:param bool replace: Whether to only replace existing directives
|
||||
|
||||
"""
|
||||
_do_for_subarray(self.parsed[filename],
|
||||
lambda x: self._has_server_names(x, names),
|
||||
lambda x: _add_directives(x, directives, replace))
|
||||
try:
|
||||
_do_for_subarray(self.parsed[filename],
|
||||
lambda x: self._has_server_names(x, names),
|
||||
lambda x: _add_directives(x, directives, replace))
|
||||
except errors.MisconfigurationError as err:
|
||||
raise errors.MisconfigurationError("Problem in %s: %s" % (filename, err.message))
|
||||
|
||||
def add_http_directives(self, filename, directives):
|
||||
"""Adds directives to the first encountered HTTP block in filename.
|
||||
|
||||
We insert new directives at the top of the block to work around
|
||||
https://trac.nginx.org/nginx/ticket/810: If the first server block
|
||||
doesn't enable OCSP stapling, stapling is broken for all blocks.
|
||||
|
||||
:param str filename: The absolute filename of the config file
|
||||
:param list directives: The directives to add
|
||||
|
||||
"""
|
||||
_do_for_subarray(self.parsed[filename],
|
||||
lambda x: x[0] == ['http'],
|
||||
lambda x: _add_directives(x[1], [directives], False))
|
||||
lambda x: x[1].insert(0, directives))
|
||||
|
||||
def get_all_certs_keys(self):
|
||||
"""Gets all certs and keys in the nginx config.
|
||||
|
|
@ -467,9 +475,14 @@ def _parse_server(server):
|
|||
return parsed_server
|
||||
|
||||
|
||||
def _add_directives(block, directives, replace=False):
|
||||
"""Adds or replaces directives in a block. If the directive doesn't exist in
|
||||
the entry already, raises a misconfiguration error.
|
||||
def _add_directives(block, directives, replace):
|
||||
"""Adds or replaces directives in a config block.
|
||||
|
||||
When replace=False, it's an error to try and add a directive that already
|
||||
exists in the config block with a conflicting value.
|
||||
|
||||
When replace=True, a directive with the same name MUST already exist in the
|
||||
config block, and the first instance will be replaced.
|
||||
|
||||
..todo :: Find directives that are in included files.
|
||||
|
||||
|
|
@ -478,21 +491,43 @@ def _add_directives(block, directives, replace=False):
|
|||
|
||||
"""
|
||||
for directive in directives:
|
||||
if not replace:
|
||||
# We insert new directives at the top of the block, mostly
|
||||
# to work around https://trac.nginx.org/nginx/ticket/810
|
||||
# Only add directive if its not already in the block
|
||||
if directive not in block:
|
||||
block.insert(0, directive)
|
||||
else:
|
||||
changed = False
|
||||
if len(directive) == 0:
|
||||
continue
|
||||
for index, line in enumerate(block):
|
||||
if len(line) > 0 and line[0] == directive[0]:
|
||||
block[index] = directive
|
||||
changed = True
|
||||
if not changed:
|
||||
_add_directive(block, directive, replace)
|
||||
|
||||
repeatable_directives = set(['server_name', 'listen', 'include'])
|
||||
|
||||
def _add_directive(block, directive, replace):
|
||||
"""Adds or replaces a single directive in a config block.
|
||||
|
||||
See _add_directives for more documentation.
|
||||
|
||||
"""
|
||||
location = -1
|
||||
# Find the index of a config line where the name of the directive matches
|
||||
# the name of the directive we want to add.
|
||||
for index, line in enumerate(block):
|
||||
if len(line) > 0 and line[0] == directive[0]:
|
||||
location = index
|
||||
break
|
||||
if replace:
|
||||
if location == -1:
|
||||
raise errors.MisconfigurationError(
|
||||
'expected directive for %s in the Nginx '
|
||||
'config but did not find it.' % directive[0])
|
||||
block[location] = directive
|
||||
else:
|
||||
# Append directive. Fail if the name is not a repeatable directive name,
|
||||
# and there is already a copy of that directive with a different value
|
||||
# in the config file.
|
||||
directive_name = directive[0]
|
||||
directive_value = directive[1]
|
||||
if location != -1 and directive_name.__str__() not in repeatable_directives:
|
||||
if block[location][1] == directive_value:
|
||||
# There's a conflict, but the existing value matches the one we
|
||||
# want to insert, so it's fine.
|
||||
pass
|
||||
else:
|
||||
raise errors.MisconfigurationError(
|
||||
'Let\'s Encrypt expected directive for %s in the Nginx '
|
||||
'config but did not find it.' % directive[0])
|
||||
'tried to insert directive "%s" but found conflicting "%s".' % (
|
||||
directive, block[location]))
|
||||
else:
|
||||
block.append(directive)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,23 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
self.assertEquals((1, 6, 2), self.config.version)
|
||||
self.assertEquals(5, len(self.config.parser.parsed))
|
||||
|
||||
@mock.patch("letsencrypt_nginx.configurator.le_util.exe_exists")
|
||||
@mock.patch("letsencrypt_nginx.configurator.subprocess.Popen")
|
||||
def test_prepare_initializes_version(self, mock_popen, mock_exe_exists):
|
||||
mock_popen().communicate.return_value = (
|
||||
"", "\n".join(["nginx version: nginx/1.6.2",
|
||||
"built by clang 6.0 (clang-600.0.56)"
|
||||
" (based on LLVM 3.5svn)",
|
||||
"TLS SNI support enabled",
|
||||
"configure arguments: --prefix=/usr/local/Cellar/"
|
||||
"nginx/1.6.2 --with-http_ssl_module"]))
|
||||
|
||||
mock_exe_exists.return_value = True
|
||||
|
||||
self.config.version = None
|
||||
self.config.prepare()
|
||||
self.assertEquals((1, 6, 2), self.config.version)
|
||||
|
||||
@mock.patch("letsencrypt_nginx.configurator.socket.gethostbyaddr")
|
||||
def test_get_all_names(self, mock_gethostbyaddr):
|
||||
mock_gethostbyaddr.return_value = ('155.225.50.69.nephoscale.net', [], [])
|
||||
|
|
@ -65,16 +82,19 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
filep = self.config.parser.abs_path('sites-enabled/example.com')
|
||||
self.config.parser.add_server_directives(
|
||||
filep, set(['.example.com', 'example.*']),
|
||||
[['listen', '5001 ssl']])
|
||||
[['listen', '5001 ssl']],
|
||||
replace=False)
|
||||
self.config.save()
|
||||
|
||||
# pylint: disable=protected-access
|
||||
parsed = self.config.parser._parse_files(filep, override=True)
|
||||
self.assertEqual([[['server'], [['listen', '5001 ssl'],
|
||||
self.assertEqual([[['server'], [
|
||||
['listen', '69.50.225.155:9000'],
|
||||
['listen', '127.0.0.1'],
|
||||
['server_name', '.example.com'],
|
||||
['server_name', 'example.*']]]],
|
||||
['server_name', 'example.*'],
|
||||
['listen', '5001 ssl']
|
||||
]]],
|
||||
parsed[0])
|
||||
|
||||
def test_choose_vhost(self):
|
||||
|
|
@ -91,12 +111,26 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
'test.www.example.com': foo_conf,
|
||||
'abc.www.foo.com': foo_conf,
|
||||
'www.bar.co.uk': localhost_conf}
|
||||
|
||||
conf_path = {'localhost': "etc_nginx/nginx.conf",
|
||||
'alias': "etc_nginx/nginx.conf",
|
||||
'example.com': "etc_nginx/sites-enabled/example.com",
|
||||
'example.com.uk.test': "etc_nginx/sites-enabled/example.com",
|
||||
'www.example.com': "etc_nginx/sites-enabled/example.com",
|
||||
'test.www.example.com': "etc_nginx/foo.conf",
|
||||
'abc.www.foo.com': "etc_nginx/foo.conf",
|
||||
'www.bar.co.uk': "etc_nginx/nginx.conf"}
|
||||
|
||||
bad_results = ['www.foo.com', 'example', 't.www.bar.co',
|
||||
'69.255.225.155']
|
||||
|
||||
for name in results:
|
||||
self.assertEqual(results[name],
|
||||
self.config.choose_vhost(name).names)
|
||||
vhost = self.config.choose_vhost(name)
|
||||
path = os.path.relpath(vhost.filep, self.temp_dir)
|
||||
|
||||
self.assertEqual(results[name], vhost.names)
|
||||
self.assertEqual(conf_path[name], path)
|
||||
|
||||
for name in bad_results:
|
||||
self.assertEqual(set([name]), self.config.choose_vhost(name).names)
|
||||
|
||||
|
|
@ -154,38 +188,36 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
parsed_server_conf = util.filter_comments(self.config.parser.parsed[server_conf])
|
||||
parsed_nginx_conf = util.filter_comments(self.config.parser.parsed[nginx_conf])
|
||||
|
||||
access_log = os.path.join(self.work_dir, "access.log")
|
||||
error_log = os.path.join(self.work_dir, "error.log")
|
||||
self.assertEqual([[['server'],
|
||||
[['include', self.config.parser.loc["ssl_options"]],
|
||||
['ssl_certificate_key', 'example/key.pem'],
|
||||
['ssl_certificate', 'example/fullchain.pem'],
|
||||
['error_log', error_log],
|
||||
['access_log', access_log],
|
||||
|
||||
['listen', '5001 ssl'],
|
||||
[
|
||||
['listen', '69.50.225.155:9000'],
|
||||
['listen', '127.0.0.1'],
|
||||
['server_name', '.example.com'],
|
||||
['server_name', 'example.*']]]],
|
||||
['server_name', 'example.*'],
|
||||
|
||||
['listen', '5001 ssl'],
|
||||
['ssl_certificate', 'example/fullchain.pem'],
|
||||
['ssl_certificate_key', 'example/key.pem'],
|
||||
['include', self.config.parser.loc["ssl_options"]]
|
||||
]]],
|
||||
parsed_example_conf)
|
||||
self.assertEqual([['server_name', 'somename alias another.alias']],
|
||||
parsed_server_conf)
|
||||
self.assertTrue(util.contains_at_depth(parsed_nginx_conf,
|
||||
[['server'],
|
||||
[['include', self.config.parser.loc["ssl_options"]],
|
||||
['ssl_certificate_key', '/etc/nginx/key.pem'],
|
||||
['ssl_certificate', '/etc/nginx/fullchain.pem'],
|
||||
['error_log', error_log],
|
||||
['access_log', access_log],
|
||||
['listen', '5001 ssl'],
|
||||
['listen', '8000'],
|
||||
['listen', 'somename:8080'],
|
||||
['include', 'server.conf'],
|
||||
[['location', '/'],
|
||||
[['root', 'html'],
|
||||
['index', 'index.html index.htm']]]]],
|
||||
2))
|
||||
self.assertTrue(util.contains_at_depth(
|
||||
parsed_nginx_conf,
|
||||
[['server'],
|
||||
[
|
||||
['listen', '8000'],
|
||||
['listen', 'somename:8080'],
|
||||
['include', 'server.conf'],
|
||||
[['location', '/'],
|
||||
[['root', 'html'],
|
||||
['index', 'index.html index.htm']]],
|
||||
['listen', '5001 ssl'],
|
||||
['ssl_certificate', '/etc/nginx/fullchain.pem'],
|
||||
['ssl_certificate_key', '/etc/nginx/key.pem'],
|
||||
['include', self.config.parser.loc["ssl_options"]]]],
|
||||
2))
|
||||
|
||||
def test_get_all_certs_keys(self):
|
||||
nginx_conf = self.config.parser.abs_path('nginx.conf')
|
||||
|
|
@ -297,19 +329,19 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
mocked = mock_popen()
|
||||
mocked.communicate.return_value = ('', '')
|
||||
mocked.returncode = 0
|
||||
self.assertTrue(self.config.restart())
|
||||
self.config.restart()
|
||||
|
||||
@mock.patch("letsencrypt_nginx.configurator.subprocess.Popen")
|
||||
def test_nginx_restart_fail(self, mock_popen):
|
||||
mocked = mock_popen()
|
||||
mocked.communicate.return_value = ('', '')
|
||||
mocked.returncode = 1
|
||||
self.assertFalse(self.config.restart())
|
||||
self.assertRaises(errors.MisconfigurationError, self.config.restart)
|
||||
|
||||
@mock.patch("letsencrypt_nginx.configurator.subprocess.Popen")
|
||||
def test_no_nginx_start(self, mock_popen):
|
||||
mock_popen.side_effect = OSError("Can't find program")
|
||||
self.assertRaises(SystemExit, self.config.restart)
|
||||
self.assertRaises(errors.MisconfigurationError, self.config.restart)
|
||||
|
||||
@mock.patch("letsencrypt_nginx.configurator.subprocess.Popen")
|
||||
def test_config_test(self, mock_popen):
|
||||
|
|
@ -330,6 +362,17 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
OpenSSL.crypto.load_privatekey(
|
||||
OpenSSL.crypto.FILETYPE_PEM, key_file.read())
|
||||
|
||||
def test_redirect_enhance(self):
|
||||
expected = [
|
||||
['if', '($scheme != "https")'],
|
||||
[['return', '301 https://$host$request_uri']]
|
||||
]
|
||||
|
||||
example_conf = self.config.parser.abs_path('sites-enabled/example.com')
|
||||
self.config.enhance("www.example.com", "redirect")
|
||||
|
||||
generated_conf = self.config.parser.parsed[example_conf]
|
||||
self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ class NginxParserTest(util.NginxTest):
|
|||
set(['localhost',
|
||||
r'~^(www\.)?(example|bar)\.']),
|
||||
[['foo', 'bar'], ['ssl_certificate',
|
||||
'/etc/ssl/cert.pem']])
|
||||
'/etc/ssl/cert.pem']],
|
||||
replace=False)
|
||||
ssl_re = re.compile(r'\n\s+ssl_certificate /etc/ssl/cert.pem')
|
||||
dump = nginxparser.dumps(nparser.parsed[nparser.abs_path('nginx.conf')])
|
||||
self.assertEqual(1, len(re.findall(ssl_re, dump)))
|
||||
|
|
@ -136,12 +137,15 @@ class NginxParserTest(util.NginxTest):
|
|||
names = set(['alias', 'another.alias', 'somename'])
|
||||
nparser.add_server_directives(server_conf, names,
|
||||
[['foo', 'bar'], ['ssl_certificate',
|
||||
'/etc/ssl/cert2.pem']])
|
||||
nparser.add_server_directives(server_conf, names, [['foo', 'bar']])
|
||||
'/etc/ssl/cert2.pem']],
|
||||
replace=False)
|
||||
nparser.add_server_directives(server_conf, names, [['foo', 'bar']],
|
||||
replace=False)
|
||||
self.assertEqual(nparser.parsed[server_conf],
|
||||
[['ssl_certificate', '/etc/ssl/cert2.pem'],
|
||||
[['server_name', 'somename alias another.alias'],
|
||||
['foo', 'bar'],
|
||||
['server_name', 'somename alias another.alias']])
|
||||
['ssl_certificate', '/etc/ssl/cert2.pem']
|
||||
])
|
||||
|
||||
def test_add_http_directives(self):
|
||||
nparser = parser.NginxParser(self.config_path, self.ssl_options)
|
||||
|
|
@ -165,17 +169,19 @@ class NginxParserTest(util.NginxTest):
|
|||
target = set(['.example.com', 'example.*'])
|
||||
filep = nparser.abs_path('sites-enabled/example.com')
|
||||
nparser.add_server_directives(
|
||||
filep, target, [['server_name', 'foo bar']], True)
|
||||
filep, target, [['server_name', 'foobar.com']], replace=True)
|
||||
self.assertEqual(
|
||||
nparser.parsed[filep],
|
||||
[[['server'], [['listen', '69.50.225.155:9000'],
|
||||
['listen', '127.0.0.1'],
|
||||
['server_name', 'foo bar'],
|
||||
['server_name', 'foo bar']]]])
|
||||
['server_name', 'foobar.com'],
|
||||
['server_name', 'example.*'],
|
||||
]]])
|
||||
self.assertRaises(errors.MisconfigurationError,
|
||||
nparser.add_server_directives,
|
||||
filep, set(['foo', 'bar']),
|
||||
[['ssl_certificate', 'cert.pem']], True)
|
||||
filep, set(['foobar.com', 'example.*']),
|
||||
[['ssl_certificate', 'cert.pem']],
|
||||
replace=True)
|
||||
|
||||
def test_get_best_match(self):
|
||||
target_name = 'www.eff.org'
|
||||
|
|
@ -217,7 +223,8 @@ class NginxParserTest(util.NginxTest):
|
|||
set(['.example.com', 'example.*']),
|
||||
[['ssl_certificate', 'foo.pem'],
|
||||
['ssl_certificate_key', 'bar.key'],
|
||||
['listen', '443 ssl']])
|
||||
['listen', '443 ssl']],
|
||||
replace=False)
|
||||
c_k = nparser.get_all_certs_keys()
|
||||
self.assertEqual(set([('foo.pem', 'bar.key', filep)]), c_k)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,14 @@ events {
|
|||
}
|
||||
|
||||
http {
|
||||
# Set an array of temp and cache file options that will otherwise default to
|
||||
# Set an array of temp, cache and log file options that will otherwise default to
|
||||
# restricted locations accessible only to root.
|
||||
client_body_temp_path $root/client_body;
|
||||
fastcgi_temp_path $root/fastcgi_temp;
|
||||
proxy_temp_path $root/proxy_temp;
|
||||
#scgi_temp_path $root/scgi_temp;
|
||||
#uwsgi_temp_path $root/uwsgi_temp;
|
||||
access_log $root/error.log;
|
||||
|
||||
# This should be turned off in a Virtualbox VM, as it can cause some
|
||||
# interesting issues with data corruption in delivered files.
|
||||
|
|
@ -54,9 +55,6 @@ http {
|
|||
|
||||
root $root/webroot;
|
||||
|
||||
access_log $root/access.log;
|
||||
error_log $root/error.log;
|
||||
|
||||
location / {
|
||||
# First attempt to serve request as file, then as directory, then fall
|
||||
# back to index.html.
|
||||
|
|
|
|||
|
|
@ -540,16 +540,11 @@ def _generate_failed_chall_msg(failed_achalls):
|
|||
|
||||
"""
|
||||
typ = failed_achalls[0].error.typ
|
||||
msg = [
|
||||
"The following '{0}' errors were reported by the server:".format(typ)]
|
||||
msg = ["The following errors were reported by the server:"]
|
||||
|
||||
problems = dict()
|
||||
for achall in failed_achalls:
|
||||
problems.setdefault(achall.error.description, set()).add(achall.domain)
|
||||
for problem in problems:
|
||||
msg.append("\n\nDomains: ")
|
||||
msg.append(", ".join(sorted(problems[problem])))
|
||||
msg.append("\nError: {0}".format(problem))
|
||||
msg.append("\n\nDomain: %s\nType: %s\nDetail: %s" % (
|
||||
achall.domain, achall.error.typ, achall.error.detail))
|
||||
|
||||
if typ in _ERROR_HELP:
|
||||
msg.append("\n\n")
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class Reverter(object):
|
|||
"Unable to load checkpoint during rollback")
|
||||
rollback -= 1
|
||||
|
||||
def view_config_changes(self):
|
||||
def view_config_changes(self, for_logging=False):
|
||||
"""Displays all saved checkpoints.
|
||||
|
||||
All checkpoints are printed by
|
||||
|
|
@ -144,6 +144,8 @@ class Reverter(object):
|
|||
|
||||
output.append(os.linesep)
|
||||
|
||||
if for_logging:
|
||||
return os.linesep.join(output)
|
||||
zope.component.getUtility(interfaces.IDisplay).notification(
|
||||
os.linesep.join(output), display_util.HEIGHT)
|
||||
|
||||
|
|
|
|||
|
|
@ -467,7 +467,7 @@ class ReportFailedChallsTest(unittest.TestCase):
|
|||
auth_handler._report_failed_challs([self.http01, self.tls_sni_same])
|
||||
call_list = mock_zope().add_message.call_args_list
|
||||
self.assertTrue(len(call_list) == 1)
|
||||
self.assertTrue("Domains: example.com\n" in call_list[0][0][0])
|
||||
self.assertTrue("Domain: example.com\nType: tls\nDetail: detail" in call_list[0][0][0])
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.zope.component.getUtility")
|
||||
def test_different_errors_and_domains(self, mock_zope):
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -33,7 +33,7 @@ version = meta['version']
|
|||
install_requires = [
|
||||
'acme=={0}'.format(version),
|
||||
'configobj',
|
||||
'cryptography>=0.7', # load_pem_x509_certificate
|
||||
'cryptography>=0.7,<1.2', # load_pem_x509_certificate
|
||||
'parsedatetime',
|
||||
'psutil>=2.1.0', # net_connections introduced in 2.1.0
|
||||
'PyOpenSSL',
|
||||
|
|
|
|||
|
|
@ -77,6 +77,15 @@ parser.add_argument('--saveinstances',
|
|||
parser.add_argument('--alt_pip',
|
||||
default='',
|
||||
help="server from which to pull candidate release packages")
|
||||
parser.add_argument('--killboulder',
|
||||
action='store_true',
|
||||
help="do not leave a persistent boulder server running")
|
||||
parser.add_argument('--boulderonly',
|
||||
action='store_true',
|
||||
help="only make a boulder server")
|
||||
parser.add_argument('--fast',
|
||||
action='store_true',
|
||||
help="use larger instance types to run faster (saves about a minute, probably not worth it)")
|
||||
cl_args = parser.parse_args()
|
||||
|
||||
# Credential Variables
|
||||
|
|
@ -292,6 +301,30 @@ def grab_letsencrypt_log():
|
|||
sudo('if [ -f ./letsencrypt.log ]; then \
|
||||
cat ./letsencrypt.log; else echo "[nolocallog]"; fi')
|
||||
|
||||
def create_client_instances(targetlist):
|
||||
"Create a fleet of client instances"
|
||||
instances = []
|
||||
print("Creating instances: ", end="")
|
||||
for target in targetlist:
|
||||
if target['virt'] == 'hvm':
|
||||
machine_type = 't2.medium' if cl_args.fast else 't2.micro'
|
||||
else:
|
||||
# 32 bit systems
|
||||
machine_type = 'c1.medium' if cl_args.fast else 't1.micro'
|
||||
if 'userdata' in target.keys():
|
||||
userdata = target['userdata']
|
||||
else:
|
||||
userdata = ''
|
||||
name = 'le-%s'%target['name']
|
||||
print(name, end=" ")
|
||||
instances.append(make_instance(name,
|
||||
target['ami'],
|
||||
KEYNAME,
|
||||
machine_type=machine_type,
|
||||
userdata=userdata))
|
||||
print()
|
||||
return instances
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# SCRIPT BEGINS
|
||||
#-------------------------------------------------------------------------------
|
||||
|
|
@ -352,30 +385,28 @@ if not sg_exists:
|
|||
make_security_group()
|
||||
time.sleep(30)
|
||||
|
||||
boulder_preexists = False
|
||||
boulder_servers = EC2.instances.filter(Filters=[
|
||||
{'Name': 'tag:Name', 'Values': ['le-boulderserver']},
|
||||
{'Name': 'instance-state-name', 'Values': ['running']}])
|
||||
|
||||
boulder_server = next(iter(boulder_servers), None)
|
||||
|
||||
print("Requesting Instances...")
|
||||
boulder_server = make_instance('le-boulderserver',
|
||||
BOULDER_AMI,
|
||||
KEYNAME,
|
||||
#machine_type='t2.micro',
|
||||
machine_type='t2.medium',
|
||||
security_groups=['letsencrypt_test'])
|
||||
|
||||
instances = []
|
||||
for target in targetlist:
|
||||
if target['virt'] == 'hvm':
|
||||
machine_type = 't2.micro'
|
||||
else:
|
||||
machine_type = 't1.micro'
|
||||
if 'userdata' in target.keys():
|
||||
userdata = target['userdata']
|
||||
else:
|
||||
userdata = ''
|
||||
instances.append(make_instance('le-%s'%target['name'],
|
||||
target['ami'],
|
||||
if boulder_server:
|
||||
print("Found existing boulder server:", boulder_server)
|
||||
boulder_preexists = True
|
||||
else:
|
||||
print("Can't find a boulder server, starting one...")
|
||||
boulder_server = make_instance('le-boulderserver',
|
||||
BOULDER_AMI,
|
||||
KEYNAME,
|
||||
machine_type=machine_type,
|
||||
userdata=userdata))
|
||||
machine_type='t2.micro',
|
||||
#machine_type='t2.medium',
|
||||
security_groups=['letsencrypt_test'])
|
||||
|
||||
if not cl_args.boulderonly:
|
||||
instances = create_client_instances(targetlist)
|
||||
|
||||
# Configure and launch boulder server
|
||||
#-------------------------------------------------------------------------------
|
||||
|
|
@ -383,21 +414,24 @@ print("Waiting on Boulder Server")
|
|||
boulder_server = block_until_instance_ready(boulder_server)
|
||||
print(" server %s"%boulder_server)
|
||||
|
||||
print("Configuring and Launching Boulder")
|
||||
|
||||
# env.host_string defines the ssh user and host for connection
|
||||
env.host_string = "ubuntu@%s"%boulder_server.public_ip_address
|
||||
print("Boulder Server at (SSH):", env.host_string)
|
||||
config_and_launch_boulder(boulder_server)
|
||||
# blocking often unnecessary, but cheap EC2 VMs can get very slow
|
||||
block_until_http_ready('http://%s:4000'%boulder_server.public_ip_address,
|
||||
wait_time=10,
|
||||
timeout=500)
|
||||
if not boulder_preexists:
|
||||
print("Configuring and Launching Boulder")
|
||||
config_and_launch_boulder(boulder_server)
|
||||
# blocking often unnecessary, but cheap EC2 VMs can get very slow
|
||||
block_until_http_ready('http://%s:4000'%boulder_server.public_ip_address,
|
||||
wait_time=10, timeout=500)
|
||||
|
||||
boulder_url = "http://%s:4000/directory"%boulder_server.private_ip_address
|
||||
print("Boulder Server at (public ip): http://%s:4000/directory"%boulder_server.public_ip_address)
|
||||
print("Boulder Server at (EC2 private ip): %s"%boulder_url)
|
||||
|
||||
if cl_args.boulderonly:
|
||||
sys.exit(0)
|
||||
|
||||
# Install and launch client scripts in parallel
|
||||
#-------------------------------------------------------------------------------
|
||||
print("Uploading and running test script in parallel: %s"%cl_args.test_script)
|
||||
|
|
@ -480,7 +514,8 @@ results_file.close()
|
|||
if not cl_args.saveinstances:
|
||||
print('Logs in ', LOGDIR)
|
||||
print('Terminating EC2 Instances and Cleaning Dangling EBS Volumes')
|
||||
boulder_server.terminate()
|
||||
if cl_args.killboulder:
|
||||
boulder_server.terminate()
|
||||
terminate_and_clean(instances)
|
||||
else:
|
||||
# print login information for the boxes for debugging
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ cd letsencrypt
|
|||
#git checkout v0.1.0 use --branch instead
|
||||
SAVE="$PIP_EXTRA_INDEX_URL"
|
||||
unset PIP_EXTRA_INDEX_URL
|
||||
export PIP_INDEX_URL="https://isnot.org/pip/0.1.0/"
|
||||
./letsencrypt-auto -v --debug --version
|
||||
unset PIP_INDEX_URL
|
||||
|
||||
export PIP_EXTRA_INDEX_URL="$SAVE"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
cd letsencrypt
|
||||
# help installs virtualenv and does nothing else
|
||||
./letsencrypt-auto -v --help all
|
||||
./letsencrypt-auto -v --debug --help all
|
||||
|
|
|
|||
Loading…
Reference in a new issue