From d76cd9c31510f4c51b74e00d51bebd786e539af3 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Tue, 1 Dec 2015 15:57:02 -0800 Subject: [PATCH 001/159] remove duplicate docstring line --- letsencrypt-apache/letsencrypt_apache/parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index ec5211ae4..aad990e3b 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -19,7 +19,6 @@ class ApacheParser(object): :ivar str root: Normalized absolute path to the server root directory. Without trailing slash. - :ivar str root: Server root :ivar set modules: All module names that are currently enabled. :ivar dict loc: Location to place directives, root - configuration origin, default - user config file, name - NameVirtualHost, From e268e718a0e16f8e3e51da2c98012d7fb1b7390a Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Wed, 2 Dec 2015 16:04:38 +0200 Subject: [PATCH 002/159] Remove py26reqs.txt ConfigArgParse 0.10 from PyPI supports Python 2.6, so there's no more need to install a fixed version directly from a git branch. --- Dockerfile | 1 - Dockerfile-dev | 1 - MANIFEST.in | 1 - bootstrap/README | 4 ++-- bootstrap/dev/venv.sh | 1 - bootstrap/venv.sh | 2 +- letsencrypt-auto | 4 +--- py26reqs.txt | 2 -- tox.ini | 2 +- 9 files changed, 5 insertions(+), 13 deletions(-) delete mode 100644 py26reqs.txt diff --git a/Dockerfile b/Dockerfile index 02aa0f0d7..da0110604 100644 --- a/Dockerfile +++ b/Dockerfile @@ -49,7 +49,6 @@ COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/ COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/ -# py26reqs.txt not installed! RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ /opt/letsencrypt/venv/bin/pip install \ -e /opt/letsencrypt/src/acme \ diff --git a/Dockerfile-dev b/Dockerfile-dev index b89411c90..3c5b53966 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -32,7 +32,6 @@ RUN /opt/letsencrypt/src/ubuntu.sh && \ # the above is not likely to change, so by putting it further up the # Dockerfile we make sure we cache as much as possible -# py26reqs.txt not installed! COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/letsencrypt/src/ # all above files are necessary for setup.py, however, package source diff --git a/MANIFEST.in b/MANIFEST.in index a82c7dd8c..a6f9ae2b6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ -include py26reqs.txt include README.rst include CHANGES.rst include CONTRIBUTING.md diff --git a/bootstrap/README b/bootstrap/README index 89fd8b6ba..d8d9f6939 100644 --- a/bootstrap/README +++ b/bootstrap/README @@ -2,6 +2,6 @@ This directory contains scripts that install necessary OS-specific prerequisite dependencies (see docs/using.rst). General dependencies: -- git-core: py26reqs.txt git+https://* +- git-core: git+https://* - ca-certificates: communication with demo ACMO server at - https://www.letsencrypt-demo.org, py26reqs.txt git+https://* + https://www.letsencrypt-demo.org, git+https://* diff --git a/bootstrap/dev/venv.sh b/bootstrap/dev/venv.sh index 2bd32a89b..11ab417dd 100755 --- a/bootstrap/dev/venv.sh +++ b/bootstrap/dev/venv.sh @@ -4,7 +4,6 @@ export VENV_ARGS="--python python2" ./bootstrap/dev/_venv_common.sh \ - -r py26reqs.txt \ -e acme[testing] \ -e .[dev,docs,testing] \ -e letsencrypt-apache \ diff --git a/bootstrap/venv.sh b/bootstrap/venv.sh index ff1a50c6c..5042178d9 100755 --- a/bootstrap/venv.sh +++ b/bootstrap/venv.sh @@ -20,7 +20,7 @@ fi pip install -U setuptools pip install -U pip -pip install -U -r py26reqs.txt letsencrypt letsencrypt-apache # letsencrypt-nginx +pip install -U letsencrypt letsencrypt-apache # letsencrypt-nginx echo echo "Congratulations, Let's Encrypt has been successfully installed/updated!" diff --git a/letsencrypt-auto b/letsencrypt-auto index c88028b72..e8d4adf9a 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -175,7 +175,7 @@ if [ "$VERBOSE" = 1 ] ; then echo $VENV_BIN/pip install -U setuptools $VENV_BIN/pip install -U pip - $VENV_BIN/pip install -r "$LEA_PATH"/py26reqs.txt -U letsencrypt letsencrypt-apache + $VENV_BIN/pip install -U letsencrypt letsencrypt-apache # nginx is buggy / disabled for now, but upgrade it if the user has # installed it manually if $VENV_BIN/pip freeze | grep -q letsencrypt-nginx ; then @@ -187,8 +187,6 @@ else $VENV_BIN/pip install -U pip > /dev/null printf . # nginx is buggy / disabled for now... - $VENV_BIN/pip install -r "$LEA_PATH"/py26reqs.txt > /dev/null - printf . $VENV_BIN/pip install -U letsencrypt > /dev/null printf . $VENV_BIN/pip install -U letsencrypt-apache > /dev/null diff --git a/py26reqs.txt b/py26reqs.txt deleted file mode 100644 index a94b22c0c..000000000 --- a/py26reqs.txt +++ /dev/null @@ -1,2 +0,0 @@ -# https://github.com/bw2/ConfigArgParse/issues/17 -git+https://github.com/kuba/ConfigArgParse.git@python2.6-0.9.3#egg=ConfigArgParse diff --git a/tox.ini b/tox.ini index d1fafe20f..1abe1cf39 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,7 @@ envlist = py26,py27,py33,py34,py35,cover,lint commands = pip install -e acme[testing] nosetests -v acme - pip install -r py26reqs.txt -e .[testing] + pip install -e .[testing] nosetests -v letsencrypt pip install -e letsencrypt-apache nosetests -v letsencrypt_apache From afb6d2813a8e3cff7b47203a250042e715b1dc3d Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Thu, 3 Dec 2015 09:55:21 +0200 Subject: [PATCH 003/159] git+https://* is no longer used --- bootstrap/README | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bootstrap/README b/bootstrap/README index d8d9f6939..d91780903 100644 --- a/bootstrap/README +++ b/bootstrap/README @@ -2,6 +2,5 @@ This directory contains scripts that install necessary OS-specific prerequisite dependencies (see docs/using.rst). General dependencies: -- git-core: git+https://* - ca-certificates: communication with demo ACMO server at - https://www.letsencrypt-demo.org, git+https://* + https://www.letsencrypt-demo.org From 9fbec030a28e1a1cc92d7ea27c251ccdec0a8253 Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Thu, 3 Dec 2015 09:58:39 +0200 Subject: [PATCH 004/159] Require ConfigArgParse >= 0.10 for Python 2.6 support --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 40c6ac16c..7c4e49311 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ version = meta['version'] install_requires = [ 'acme=={0}'.format(version), - 'ConfigArgParse', + 'ConfigArgParse>=0.10.0', # python2.6 support, upstream #17 'configobj', 'cryptography>=0.7', # load_pem_x509_certificate 'parsedatetime', From 51f17115c6f76d0adfa0914e8202854127760f4c Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Thu, 3 Dec 2015 10:22:50 +0200 Subject: [PATCH 005/159] Allow older ConfigArgParse for users of modern Pythons (I think this is a bad idea because of https://github.com/pypa/pip/issues/3025, but letsencrypt maintainers insist, so *shrug*. Also the same problem exists for the versioned 'mock' dependency, so I'm not introducing a new one here.) --- setup.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 7c4e49311..36d354354 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,6 @@ version = meta['version'] install_requires = [ 'acme=={0}'.format(version), - 'ConfigArgParse>=0.10.0', # python2.6 support, upstream #17 'configobj', 'cryptography>=0.7', # load_pem_x509_certificate 'parsedatetime', @@ -54,9 +53,13 @@ if sys.version_info < (2, 7): # only some distros recognize stdlib argparse as already satisfying 'argparse', 'mock<1.1.0', + 'ConfigArgParse>=0.10.0', # python2.6 support, upstream #17 ]) else: - install_requires.append('mock') + install_requires.extend([ + 'mock', + 'ConfigArgParse', + ]) dev_extras = [ # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 From 85dc829d9f3b5e2b69efd6246c1ed5f9845ebc47 Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Thu, 3 Dec 2015 10:51:16 +0200 Subject: [PATCH 006/159] Order imports alphabetically --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 36d354354..40749bf2a 100644 --- a/setup.py +++ b/setup.py @@ -52,13 +52,13 @@ if sys.version_info < (2, 7): install_requires.extend([ # only some distros recognize stdlib argparse as already satisfying 'argparse', - 'mock<1.1.0', 'ConfigArgParse>=0.10.0', # python2.6 support, upstream #17 + 'mock<1.1.0', ]) else: install_requires.extend([ - 'mock', 'ConfigArgParse', + 'mock', ]) dev_extras = [ From 5b4bf427b81af5094abdb8dd6123df750425274e Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Thu, 3 Dec 2015 12:22:28 +0200 Subject: [PATCH 007/159] Added apache mod_ssl check & installation to bootstrap --- bootstrap/_rpm_common.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bootstrap/_rpm_common.sh b/bootstrap/_rpm_common.sh index b975da444..b80a9555b 100755 --- a/bootstrap/_rpm_common.sh +++ b/bootstrap/_rpm_common.sh @@ -47,3 +47,11 @@ then echo "Could not install additional dependencies. Aborting bootstrap!" exit 1 fi + + +if yum list installed "httpd" >/dev/null 2>&1; then + if ! $tool install -y mod_ssl + then + echo "Apache found, but mod_ssl could not be installed." + fi +fi From 7d307d1cf4c4ce5fbb5f41e408893e87e051d84c Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Thu, 3 Dec 2015 14:14:02 +0200 Subject: [PATCH 008/159] Per-OS constants --- .../letsencrypt_apache/configurator.py | 12 +++++----- .../letsencrypt_apache/constants.py | 22 ++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 98b0b8820..ba3bdcd7d 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -86,17 +86,17 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): @classmethod def add_parser_arguments(cls, add): - add("ctl", default=constants.CLI_DEFAULTS["ctl"], + add("ctl", default=constants.os_constant("ctl"), help="Path to the 'apache2ctl' binary, used for 'configtest', " "retrieving the Apache2 version number, and initialization " "parameters.") - add("enmod", default=constants.CLI_DEFAULTS["enmod"], + add("enmod", default=constants.os_constant("enmod"), help="Path to the Apache 'a2enmod' binary.") - add("dismod", default=constants.CLI_DEFAULTS["dismod"], + add("dismod", default=constants.os_constant("dismod"), help="Path to the Apache 'a2enmod' binary.") - add("le-vhost-ext", default=constants.CLI_DEFAULTS["le_vhost_ext"], + add("le-vhost-ext", default=constants.os_constant("le_vhost_ext"), help="SSL vhost configuration extension.") - add("server-root", default=constants.CLI_DEFAULTS["server_root"], + add("server-root", default=constants.os_constant("server_root"), help="Apache server root directory.") le_util.add_deprecated_argument(add, "init-script", 1) @@ -583,7 +583,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): Duplicates vhost and adds default ssl options New vhost will reside as (nonssl_vhost.path) + - ``letsencrypt_apache.constants.CLI_DEFAULTS["le_vhost_ext"]`` + ``letsencrypt_apache.constants.os_constant("le_vhost_ext")`` .. note:: This function saves the configuration diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 202fc3e21..e302a29fe 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -1,14 +1,27 @@ """Apache plugin constants.""" import pkg_resources +from letsencrypt import le_util -CLI_DEFAULTS = dict( +CLI_DEFAULTS_DEBIAN = dict( server_root="/etc/apache2", ctl="apache2ctl", enmod="a2enmod", dismod="a2dismod", le_vhost_ext="-le-ssl.conf", ) +CLI_DEFAULTS_CENTOS = dict( + server_root="/etc/httpd", + ctl="apachectl", + enmod=None, + dismod=None, + le_vhost_ext="-le-ssl.conf", +) +CLI_DEFAULTS = { + "debian": CLI_DEFAULTS_DEBIAN, + "ubuntu": CLI_DEFAULTS_DEBIAN, + "centos": CLI_DEFAULTS_CENTOS +} """CLI defaults.""" MOD_SSL_CONF_DEST = "options-ssl-apache.conf" @@ -38,3 +51,10 @@ UIR_ARGS = ["always", "set", "Content-Security-Policy", HEADER_ARGS = {"Strict-Transport-Security": HSTS_ARGS, "Upgrade-Insecure-Requests": UIR_ARGS} +def os_constant(key): + os_info = le_util.get_os_info() + try: + constants = CLI_DEFAULTS[os_info[0].lower()] + except KeyError: + constants = CLI_DEFAULTS["debian"] + return constants[key] From ce2ce697bdbe249e55001f48fe6c0e8e45e5e036 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 3 Dec 2015 12:12:38 -0800 Subject: [PATCH 009/159] check for missed define statements at the end of parsing --- letsencrypt-apache/letsencrypt_apache/parser.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index aad990e3b..4ed83e652 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -35,6 +35,7 @@ class ApacheParser(object): # https://httpd.apache.org/docs/2.4/mod/core.html#ifdefine # This only handles invocation parameters and Define directives! self.variables = {} + self.unparsable = False self.update_runtime_variables(ctl) self.aug = aug @@ -58,6 +59,10 @@ class ApacheParser(object): # Must also attempt to parse sites-available or equivalent # Sites-available is not included naturally in configuration self._parse_file(os.path.join(self.root, "sites-available") + "/*") + #TODO check to see if there were unparsed define statements + if self.unparsable: + if self.find_dir("Define", exclude=False): + raise errors.PluginError("Error parsing runtime variables") def init_modules(self): """Iterates on the configuration until no new modules are loaded. @@ -100,7 +105,9 @@ class ApacheParser(object): try: matches.remove("DUMP_RUN_CFG") except ValueError: - raise errors.PluginError("Unable to parse runtime variables") + self.unparsable = True + return + #raise errors.PluginError("Unable to parse runtime variables") for match in matches: if match.count("=") > 1: From 1bf9fbcc727b42c7a633d8ab935e1b103d960fc6 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 3 Dec 2015 14:25:49 -0800 Subject: [PATCH 010/159] don't enable socache on apache 2.2 --- letsencrypt-apache/letsencrypt_apache/configurator.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index a0b58c5ff..fda02c7ff 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -546,7 +546,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ if "ssl_module" not in self.parser.modules: self.enable_mod("ssl", temp=temp) - + if self.version >= (2, 4) and "socache_shmcb_module" not in self.parser.modules: + self.enable_mod("socache_shmcb", temp=temp) # Check for Listen # Note: This could be made to also look for ip:443 combo if not self.parser.find_dir("Listen", port): @@ -1320,7 +1321,7 @@ def _get_mod_deps(mod_name): """ deps = { - "ssl": ["setenvif", "mime", "socache_shmcb"] + "ssl": ["setenvif", "mime"] } return deps.get(mod_name, []) From d6929a8efb3a8cec25d6c73c5e61f99a06c8942a Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Fri, 4 Dec 2015 05:10:44 -0800 Subject: [PATCH 011/159] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..bf0416817 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# letstest +simple aws testfarm scripts for letsencrypt client testing From 6dab44816d214a3be861f62a17bdaa551381a757 Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Fri, 4 Dec 2015 06:32:24 -0800 Subject: [PATCH 012/159] initial commit of scripts --- .pylintrc | 335 ++++++++++++ README.md | 34 ++ apache2_targets.yaml | 57 +++ multitester.py | 482 ++++++++++++++++++ scripts/boulder_config.sh | 32 ++ scripts/boulder_install.sh | 28 + scripts/test_letsencrypt_auto_apache2.sh | 24 + ...st_letsencrypt_auto_certonly_standalone.sh | 14 + scripts/test_letsencrypt_auto_venv_only.sh | 7 + targets.yaml | 99 ++++ 10 files changed, 1112 insertions(+) create mode 100644 .pylintrc create mode 100644 apache2_targets.yaml create mode 100644 multitester.py create mode 100755 scripts/boulder_config.sh create mode 100755 scripts/boulder_install.sh create mode 100755 scripts/test_letsencrypt_auto_apache2.sh create mode 100755 scripts/test_letsencrypt_auto_certonly_standalone.sh create mode 100755 scripts/test_letsencrypt_auto_venv_only.sh create mode 100644 targets.yaml diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000..4f978cdd2 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,335 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Profiled execution. +profile=no + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +#load-plugins=linter_plugin + + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=fixme,locally-disabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes +# abstract-class-not-used cannot be disabled locally (at least in +# pylint 1.4.1), same for abstract-class-little-used + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (RP0004). +comment=no + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input,file + +# Good variable names which should always be accepted, separated by a comma +good-names=f,i,j,k,ex,Run,_,fd,logger + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,40}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,40}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,50}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,50}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=(__.*__)|(test_[A-Za-z0-9_]*)|(_.*)|(.*Test$) + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging,logger + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=(unused)?_.*|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=6 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=yes + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled +no-space-check=trailing-comma + +# Maximum number of lines in a module +max-module-lines=1250 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +# This does something silly/broken... +#indent-after-paren=4 + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis +ignored-modules=pkg_resources,confargparse,argparse,six.moves,six.moves.urllib +# import errors ignored only in 1.4.4 +# https://bitbucket.org/logilab/pylint/commits/cd000904c9e2 + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject + +# When zope mode is activated, add a predefined set of Zope acquired attributes +# to generated-members. +zope=yes + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defined in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by,implementedBy,providedBy + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=6 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=12 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/README.md b/README.md index bf0416817..35950b18c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ # letstest simple aws testfarm scripts for letsencrypt client testing + +- Configures (canned) boulder server +- Launches EC2 instances with a given list of AMIs for different distros +- Copies letsencrypt repo and puts it on the instances +- Runs letsencrypt tests (bash scripts) on all of these +- Logs execution and success/fail for debugging + +## Notes + - Some AWS images, e.g. official CentOS and FreeBSD images + require acceptance of user terms on the AWS marketplace + website. This can't be automated. + - AWS EC2 has a default limit of 20 t2/t1 instances, if more + are needed, they need to be requested via online webform. + +## Usage + - Requires AWS IAM secrets to be set up with aws cli + - Requires an AWS associated keyfile .pem + +``` +>aws configure --profile HappyHacker +[interactive: enter secrets for IAM role] +>aws ec2 create-key-pair --profile HappyHacker --key-name MyKeyPair --query 'KeyMaterial' --output text > MyKeyPair.pem +``` +then: +``` +>python multitester.py targets.yaml MyKeyPair.pem HappyHacker scripts/test_letsencrypt_auto_venv_only.sh +``` + +see: + https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html + https://docs.aws.amazon.com/cli/latest/userguide/cli-ec2-keypairs.html + +https://github.com/letsencrypt/boulder +https://github.com/letsencrypt/letsencrypt \ No newline at end of file diff --git a/apache2_targets.yaml b/apache2_targets.yaml new file mode 100644 index 000000000..e707b8636 --- /dev/null +++ b/apache2_targets.yaml @@ -0,0 +1,57 @@ +targets: + #----------------------------------------------------------------------------- + # Apache 2.4 + - ami: ami-26d5af4c + name: ubuntu15.10 + type: ubuntu + virt: hvm + user: ubuntu + - ami: ami-d92e6bb3 + name: ubuntu15.04LTS + type: ubuntu + virt: hvm + user: ubuntu + - ami: ami-7b89cc11 + name: ubuntu14.04LTS + type: ubuntu + virt: hvm + user: ubuntu + - ami: ami-9295d0f8 + name: ubuntu14.04LTS_32bit + type: ubuntu + virt: pv + user: ubuntu + - ami: ami-116d857a + name: debian8.1 + type: debian + virt: hvm + user: admin + userdata: | + #cloud-init + runcmd: + - [ apt-get, install, -y, curl ] + #----------------------------------------------------------------------------- + # Apache 2.2 + # - ami: ami-0611546c + # name: ubuntu12.04LTS + # type: ubuntu + # virt: hvm + # user: ubuntu + # - ami: ami-e0efab88 + # name: debian7.8.aws.1 + # type: debian + # virt: hvm + # user: admin + # userdata: | + # #cloud-init + # runcmd: + # - [ apt-get, install, -y, curl ] + # - ami: ami-e6eeaa8e + # name: debian7.8.aws.1_32bit + # type: debian + # virt: pv + # user: admin + # userdata: | + # #cloud-init + # runcmd: + # - [ apt-get, install, -y, curl ] \ No newline at end of file diff --git a/multitester.py b/multitester.py new file mode 100644 index 000000000..116d5b5a8 --- /dev/null +++ b/multitester.py @@ -0,0 +1,482 @@ +""" +Letsencrypt Integration Test Tool + +- Configures (canned) boulder server +- Launches EC2 instances with a given list of AMIs for different distros +- Copies letsencrypt repo and puts it on the instances +- Runs letsencrypt tests (bash scripts) on all of these +- Logs execution and success/fail for debugging + +Notes: + - Some AWS images, e.g. official CentOS and FreeBSD images + require acceptance of user terms on the AWS marketplace + website. This can't be automated. + - AWS EC2 has a default limit of 20 t2/t1 instances, if more + are needed, they need to be requested via online webform. + +Usage: + - Requires AWS IAM secrets to be set up with aws cli + - Requires an AWS associated keyfile .pem + +>aws configure --profile HappyHacker +[interactive: enter secrets for IAM role] +>aws ec2 create-key-pair --profile HappyHacker --key-name MyKeyPair \ + --query 'KeyMaterial' --output text > MyKeyPair.pem +then: +>python multitester.py targets.yaml MyKeyPair.pem HappyHacker test_letsencrypt_auto_venv_only.sh +see: + https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html + https://docs.aws.amazon.com/cli/latest/userguide/cli-ec2-keypairs.html +""" + +from __future__ import print_function +from __future__ import with_statement + +import sys, os, time, argparse, socket +import multiprocessing as mp +from multiprocessing import Manager +import urllib2 +import yaml +import boto3 +import fabric +from fabric.api import run, execute, local, env, sudo, cd, lcd +from fabric.operations import get, put +from fabric.context_managers import shell_env + +# Command line parser +#------------------------------------------------------------------------------- +parser = argparse.ArgumentParser(description='Builds EC2 cluster for testing.') +parser.add_argument('config_file', + help='yaml configuration file for AWS server cluster') +parser.add_argument('key_file', + help='key file (.pem) for AWS') +parser.add_argument('aws_profile', + help='profile for AWS (i.e. as in ~/.aws/certificates)') +parser.add_argument('test_script', + default='test_letsencrypt_auto_certonly_standalone.sh', + help='name of bash script in /scripts to deploy and run') +#parser.add_argument('--script_args', +# nargs='+', +# help='space-delimited list of arguments to pass to the bash test script', +# required=False) +parser.add_argument('--repo', + default='https://github.com/letsencrypt/letsencrypt.git', + help='letsencrypt git repo to use') +parser.add_argument('--branch', + default='~', + help='letsencrypt git branch to trial') +parser.add_argument('--pull_request', + default='~', + help='letsencrypt/letsencrypt pull request to trial') +parser.add_argument('--merge_master', + action='store_true', + help="if set merges PR into master branch of letsencrypt/letsencrypt") +parser.add_argument('--saveinstances', + action='store_true', + help="don't kill EC2 instances after run, useful for debugging") +cl_args = parser.parse_args() + +# Credential Variables +#------------------------------------------------------------------------------- +# assumes naming: = .pem +KEYFILE = cl_args.key_file +KEYNAME = os.path.split(cl_args.key_file)[1].split('.pem')[0] +PROFILE = cl_args.aws_profile + +# Globals +#------------------------------------------------------------------------------- +BOULDER_AMI = 'ami-5f490b35' # premade shared boulder AMI 14.04LTS us-east-1 +LOGDIR = "" #points to logging / working directory +# boto3/AWS api globals +AWS_SESSION = None +EC2 = None + +# Boto3/AWS automation functions +#------------------------------------------------------------------------------- +def make_security_group(): + # will fail if security group of GroupName already exists + # cannot have duplicate SGs of the same name + mysg = EC2.create_security_group(GroupName="letsencrypt_test", + Description='security group for automated testing') + mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=22, ToPort=22) + mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=80, ToPort=80) + mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=443, ToPort=443) + # for boulder wfe (http) server + mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=4000, ToPort=4000) + # for mosh + mysg.authorize_ingress(IpProtocol="udp", CidrIp="0.0.0.0/0", FromPort=60000, ToPort=61000) + return mysg + +def make_instance(instance_name, + ami_id, + keyname, + machine_type='t2.micro', + security_groups=['letsencrypt_test'], + userdata=""): #userdata contains bash or cloud-init script + + new_instance = EC2.create_instances( + ImageId=ami_id, + SecurityGroups=security_groups, + KeyName=keyname, + MinCount=1, + MaxCount=1, + UserData=userdata, + InstanceType=machine_type)[0] + + # brief pause to prevent rare EC2 error + time.sleep(0.5) + + # give instance a name + new_instance.create_tags(Tags=[{'Key': 'Name', 'Value': instance_name}]) + return new_instance + +def terminate_and_clean(instances): + """ + Some AMIs specify EBS stores that won't delete on instance termination. + These must be manually deleted after shutdown. + """ + volumes_to_delete = [] + for instance in instances: + for bdmap in instance.block_device_mappings: + if 'Ebs' in bdmap.keys(): + if not bdmap['Ebs']['DeleteOnTermination']: + volumes_to_delete.append(bdmap['Ebs']['VolumeId']) + + for instance in instances: + instance.terminate() + + # can't delete volumes until all attaching instances are terminated + _ids = [instance.id for instance in instances] + all_terminated = False + while not all_terminated: + all_terminated = True + for _id in _ids: + # necessary to reinit object for boto3 to get true state + inst = EC2.Instance(id=_id) + if inst.state['Name'] != 'terminated': + all_terminated = False + time.sleep(5) + + for vol_id in volumes_to_delete: + volume = EC2.Volume(id=vol_id) + volume.delete() + + return volumes_to_delete + + +# Helper Routines +#------------------------------------------------------------------------------- +def block_until_http_ready(urlstring, wait_time=10, timeout=240): + "Blocks until server at urlstring can respond to http requests" + server_ready = False + t_elapsed = 0 + while not server_ready and t_elapsed < timeout: + try: + sys.stdout.write('.') + sys.stdout.flush() + req = urllib2.Request(urlstring) + response = urllib2.urlopen(req) + #if response.code == 200: + server_ready = True + except urllib2.URLError: + pass + time.sleep(wait_time) + t_elapsed += wait_time + +def block_until_ssh_open(ipstring, wait_time=10, timeout=120): + "Blocks until server at ipstring has an open port 22" + reached = False + t_elapsed = 0 + while not reached and t_elapsed < timeout: + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((ipstring, 22)) + reached = True + except socket.error as err: + time.sleep(wait_time) + t_elapsed += wait_time + sock.close() + +def block_until_instance_ready(booting_instance, wait_time=5, extra_wait_time=20): + "Blocks booting_instance until AWS EC2 instance is ready to accept SSH connections" + # the reinstantiation from id is necessary to force boto3 + # to correctly update the 'state' variable during init + _id = booting_instance.id + _instance = EC2.Instance(id=_id) + _state = _instance.state['Name'] + _ip = _instance.public_ip_address + while _state != 'running' or _ip is None: + time.sleep(wait_time) + _instance = EC2.Instance(id=_id) + _state = _instance.state['Name'] + _ip = _instance.public_ip_address + block_until_ssh_open(_ip) + time.sleep(extra_wait_time) + return _instance + + +# Fabric Routines +#------------------------------------------------------------------------------- +def local_git_clone(repo_url): + "clones master of repo_url" + with lcd(LOGDIR): + local('if [ -d letsencrypt ]; then rm -rf letsencrypt; fi') + local('git clone %s'% repo_url) + local('tar czf le.tar.gz letsencrypt') + +def local_git_branch(repo_url, branch_name): + "clones branch of repo_url" + with lcd(LOGDIR): + local('if [ -d letsencrypt ]; then rm -rf letsencrypt; fi') + local('git clone %s --branch %s --single-branch'%(repo_url, branch_name)) + local('tar czf le.tar.gz letsencrypt') + +def local_git_PR(repo_url, PRnumstr, merge_master=True): + "clones specified pull request from repo_url and optionally merges into master" + with lcd(LOGDIR): + local('if [ -d letsencrypt ]; then rm -rf letsencrypt; fi') + local('git clone %s'% repo_url) + local('cd letsencrypt && git fetch origin pull/%s/head:lePRtest'%PRnumstr) + local('cd letsencrypt && git co lePRtest') + if merge_master: + local('cd letsencrypt && git remote update origin') + local('cd letsencrypt && git merge origin/master -m "testmerge"') + local('tar czf le.tar.gz letsencrypt') + +def local_repo_to_remote(): + "copies local tarball of repo to remote" + with lcd(LOGDIR): + put(local_path='le.tar.gz', remote_path='') + run('tar xzf le.tar.gz') + +def local_repo_clean(): + "delete tarball" + with lcd(LOGDIR): + local('rm le.tar.gz') + +def deploy_script(scriptname, *args): + "copies to remote and executes local script" + with lcd('scripts'): + put(local_path=scriptname, remote_path='', mirror_local_mode=True) + args_str = ' '.join(args) + run('./'+scriptname+' '+args_str) + +def run_boulder(): + with cd('$GOPATH/src/github.com/letsencrypt/boulder'): + run('go run cmd/rabbitmq-setup/main.go -server amqp://localhost') + run('nohup ./start.py >& /dev/null < /dev/null &') + +def config_and_launch_boulder(instance): + execute(deploy_script, 'boulder_config.sh') + execute(run_boulder) + +def install_and_launch_letsencrypt(instance, boulder_url): + execute(local_repo_to_remote) + with shell_env(BOULDER_URL=boulder_url): + execute(deploy_script, cl_args.test_script) + +def grab_letsencrypt_log(): + "grabs letsencrypt.log via cat into logged stdout" + sudo('if [ -f /var/log/letsencrypt/letsencrypt.log ]; then \ + cat /var/log/letsencrypt/letsencrypt.log; else echo "[novarlog]"; fi') + # fallback file if /var/log is unwriteable...? correct? + sudo('if [ -f ./letsencrypt.log ]; then \ + cat ./letsencrypt.log; else echo "[nolocallog]"; fi') + +#------------------------------------------------------------------------------- +# SCRIPT BEGINS +#------------------------------------------------------------------------------- + +# Set up local copy of git repo +#------------------------------------------------------------------------------- +LOGDIR = "letest-%d"%int(time.time()) +print("Making local dir for test repo and logs: %s"%LOGDIR) +local('mkdir %s'%LOGDIR) + +# figure out what git object to test and locally create it in LOGDIR +print("Making local git repo") +try: + if cl_args.pull_request != '~': + print('Testing PR %s '%cl_args.pull_request, + "MERGING into master" if cl_args.merge_master else "") + execute(local_git_PR, cl_args.repo, cl_args.pull_request, cl_args.merge_master) + elif cl_args.branch != '~': + print('Testing branch %s of %s'%(cl_args.branch, cl_args.repo)) + execute(local_git_branch, cl_args.repo, cl_args.branch) + else: + print('Testing master of %s'%cl_args.repo) + execute(local_git_clone, cl_args.repo) +except FabricException: + print("FAIL: trouble with git repo") + exit() + + +# Set up EC2 instances +#------------------------------------------------------------------------------- +configdata = yaml.load(open(cl_args.config_file, 'r')) +targetlist = configdata['targets'] +print('Testing against these images: [%d total]'%len(targetlist)) +for target in targetlist: + print(target['ami'], target['name']) + +print("Connecting to EC2 using\n profile %s\n keyname %s\n keyfile %s"%(PROFILE, KEYNAME, KEYFILE)) +AWS_SESSION = boto3.session.Session(profile_name=PROFILE) +EC2 = AWS_SESSION.resource('ec2') + +print("Making Security Group") +sg_exists = False +for sg in EC2.security_groups.all(): + if sg.group_name == 'letsencrypt_test': + sg_exists = True + print(" %s already exists"%'letsencrypt_test') +if not sg_exists: + make_security_group() + time.sleep(30) + +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'], + KEYNAME, + machine_type=machine_type, + userdata=userdata) ) + + +# Configure and launch boulder server +#------------------------------------------------------------------------------- +print("Waiting on Boulder Server") +boulder_server = block_until_instance_ready(boulder_server) +print(" server %s"%boulder_server) + +print("Configuring and Launching Boulder") + +# Fabric library controlled through global env parameters +env.key_filename = KEYFILE +env.shell = '/bin/bash -l -i -c' +env.connection_attempts = 5 +env.timeout = 10 +# replace default SystemExit thrown by fabric during trouble +class FabricException(Exception): + pass +env['abort_exception'] = FabricException + +# 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) + +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) + +# Install and launch client scripts in parallel +#------------------------------------------------------------------------------- +print("Running letsencrypt clients in parallel - output routed to log files.") +# (Advice: always use Manager.Queue, never regular multiprocessing.Queue +# the latter has implementation flaws that deadlock it in some circumstances) +manager = Manager() +outqueue = manager.Queue() +inqueue = manager.Queue() +SENTINEL = None #queue kill signal + +# launch as many processes as clients to test +num_processes = len(targetlist) +jobs = [] #keep a reference to current procs + +def test_client_process(inqueue, outqueue): + cur_proc = mp.current_process() + for inreq in iter(inqueue.get, SENTINEL): + ii, target = inreq + + #save all stdout to log file + sys.stdout = open(LOGDIR+'/'+'%d_%s.log'%(ii,target['name']), 'w') + + print("[%s : client %d %s %s]" % (cur_proc.name, ii, target['ami'], target['name'])) + instances[ii] = block_until_instance_ready(instances[ii]) + print("server %s at %s"%(instances[ii], instances[ii].public_ip_address)) + env.host_string = "%s@%s"%(target['user'], instances[ii].public_ip_address) + print(env.host_string) + + try: + install_and_launch_letsencrypt(instances[ii], boulder_url) + outqueue.put((ii, target, 'pass')) + print("%s - %s SUCCESS"%(target['ami'], target['name'])) + except: + outqueue.put((ii, target, 'fail')) + print("%s - %s FAIL"%(target['ami'], target['name'])) + pass + + # append server letsencrypt.log to each per-machine output log + print("\n\nletsencrypt.log\n" + "-"*80 + "\n") + try: + execute(grab_letsencrypt_log) + except: + print("log fail\n") + pass + +# initiate process execution +for i in range(num_processes): + p = mp.Process(target=test_client_process, args=(inqueue, outqueue)) + jobs.append(p) + p.daemon = True # kills subprocesses if parent is killed + p.start() + +# fill up work queue +for ii, target in enumerate(targetlist): + inqueue.put((ii, target)) + +# add SENTINELs to end client processes +for i in range(num_processes): + inqueue.put(SENTINEL) +# wait on termination of client processes +for p in jobs: + p.join() +# add SENTINEL to output queue +outqueue.put(SENTINEL) + +# clean up +execute(local_repo_clean) + +# print and save summary results +results_file = open(LOGDIR+'/results', 'w') +outputs = [outq for outq in iter(outqueue.get, SENTINEL)] +outputs.sort(key=lambda x: x[0]) +for outq in outputs: + ii, target, status = outq + print('%d %s %s'%(ii, target['name'], status)) + results_file.write('%d %s %s\n'%(ii, target['name'], status)) +results_file.close() + +if not cl_args.saveinstances: + print('Terminating EC2 Instances and Cleaning Dangling EBS Volumes') + boulder_server.terminate() + terminate_and_clean(instances) +else: + # print login information for the boxes for debugging + for ii, target in enumerate(targetlist): + print(target['name'], + target['ami'], + "%s@%s"%(target['user'],instances[ii].public_ip_address)) + +# kill any connections +fabric.network.disconnect_all() diff --git a/scripts/boulder_config.sh b/scripts/boulder_config.sh new file mode 100755 index 000000000..1ef63ca10 --- /dev/null +++ b/scripts/boulder_config.sh @@ -0,0 +1,32 @@ +#!/bin/bash -x + +# Configures and Launches Boulder Server installed on +# us-east-1 ami-5f490b35 bouldertestserver (boulder commit 8b433f54dab) + +# fetch instance data from EC2 metadata service +public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) +public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) +private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) + +# get local DNS resolver for VPC +resolver_ip=$(grep nameserver /etc/resolv.conf |cut -d" " -f2 |head -1) +resolver=$resolver_ip':53' + +# modifies integration testing boulder setup for local AWS VPC network +# connections instead of localhost +cd $GOPATH/src/github.com/letsencrypt/boulder +# configure boulder to receive outside connection on 4000 +sed -i '/listenAddress/ s/127.0.0.1:4000/'$private_ip':4000/' ./test/boulder-config.json +sed -i '/baseURL/ s/127.0.0.1:4000/'$private_ip':4000/' ./test/boulder-config.json +# change test ports to real +sed -i '/httpPort/ s/5002/80/' ./test/boulder-config.json +sed -i '/httpsPort/ s/5001/443/' ./test/boulder-config.json +sed -i '/tlsPort/ s/5001/443/' ./test/boulder-config.json +# set local dns resolver +sed -i '/dnsResolver/ s/127.0.0.1:8053/'$resolver'/' ./test/boulder-config.json + +# start rabbitMQ +#go run cmd/rabbitmq-setup/main.go -server amqp://localhost +# start acme services +#nohup ./start.py >& /dev/null < /dev/null & +#./start.py diff --git a/scripts/boulder_install.sh b/scripts/boulder_install.sh new file mode 100755 index 000000000..b5ddf2c5b --- /dev/null +++ b/scripts/boulder_install.sh @@ -0,0 +1,28 @@ +#!/bin/bash -x + +# >>>> only tested on Ubuntu 14.04LTS <<<< + +# non-interactive install of mariadb and other dependencies +export DEBIAN_FRONTEND=noninteractive +sudo debconf-set-selections <<< 'mariadb-server mysql-server/root_password password PASS' +sudo debconf-set-selections <<< 'mariadb-server mysql-server/root_password_again password PASS' +apt-get -y --no-upgrade install git make libltdl3-dev mariadb-server rabbitmq-server +sudo mysql -uroot -pPASS -e "SET PASSWORD = PASSWORD(\'\');" + +# install go +wget https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz +tar xzvf go1.5.1.linux-amd64.tar.gz +mkdir gocode +echo "export GOROOT=/home/ubuntu/go \n\ + export GOPATH=/home/ubuntu/gocode\n\ + export PATH=/home/ubuntu/go/bin:/home/ubuntu/gocode/bin:$PATH" >> .bashrc + +# install boulder and its go dependencies +go get -d github.com/letsencrypt/boulder/... +cd $GOPATH/src/github.com/letsencrypt/boulder +wget https://github.com/jsha/boulder-tools/raw/master/goose.gz +mkdir $GOPATH/bin +zcat goose.gz > $GOPATH/bin/goose +chmod +x $GOPATH/bin/goose +./test/create_db.sh +go get github.com/jsha/listenbuddy diff --git a/scripts/test_letsencrypt_auto_apache2.sh b/scripts/test_letsencrypt_auto_apache2.sh new file mode 100755 index 000000000..24980361a --- /dev/null +++ b/scripts/test_letsencrypt_auto_apache2.sh @@ -0,0 +1,24 @@ +#!/bin/bash -x + +#install apache2 on apt systems +# debian doesn't come with curl +sudo apt-get update +sudo apt-get -y --no-upgrade install apache2 curl + +# $BOULDER_URL is dynamically set at execution +# fetch instance data from EC2 metadata service +public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) +public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) +private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) + +# For apache 2.4, set up ServerName +sudo sed -i '/ServerName/ s/#ServerName/ServerName/' \ + /etc/apache2/sites-available/000-default.conf +sudo sed -i '/ServerName/ s/www.example.com/'$public_host'/' \ + /etc/apache2/sites-available/000-default.conf + +# run letsencrypt-apache2 via letsencrypt-auto +cd letsencrypt +./letsencrypt-auto -v --debug --text --agree-dev-preview --agree-tos \ + --renew-by-default --redirect --register-unsafely-without-email \ + --domain $public_host --server $BOULDER_URL diff --git a/scripts/test_letsencrypt_auto_certonly_standalone.sh b/scripts/test_letsencrypt_auto_certonly_standalone.sh new file mode 100755 index 000000000..e82c81bd4 --- /dev/null +++ b/scripts/test_letsencrypt_auto_certonly_standalone.sh @@ -0,0 +1,14 @@ +#!/bin/bash -x + +# $BOULDER_URL is dynamically set at execution +# fetch instance data from EC2 metadata service +public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) +public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) +private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) + +cd letsencrypt +./letsencrypt-auto certonly -v --standalone --debug \ + --text --agree-dev-preview --agree-tos \ + --renew-by-default --redirect \ + --register-unsafely-without-email \ + --domain $public_host --server $BOULDER_URL diff --git a/scripts/test_letsencrypt_auto_venv_only.sh b/scripts/test_letsencrypt_auto_venv_only.sh new file mode 100755 index 000000000..e6b7aed8d --- /dev/null +++ b/scripts/test_letsencrypt_auto_venv_only.sh @@ -0,0 +1,7 @@ +#!/bin/bash -x + +# $BOULDER_URL is dynamically set at execution + +cd letsencrypt +# help installs virtualenv and does nothing else +./letsencrypt-auto -v --help all diff --git a/targets.yaml b/targets.yaml new file mode 100644 index 000000000..4547366b3 --- /dev/null +++ b/targets.yaml @@ -0,0 +1,99 @@ +targets: + #----------------------------------------------------------------------------- + #Ubuntu + - ami: ami-26d5af4c + name: ubuntu15.10 + type: ubuntu + virt: hvm + user: ubuntu + - ami: ami-d92e6bb3 + name: ubuntu15.04LTS + type: ubuntu + virt: hvm + user: ubuntu + - ami: ami-7b89cc11 + name: ubuntu14.04LTS + type: ubuntu + virt: hvm + user: ubuntu + - ami: ami-9295d0f8 + name: ubuntu14.04LTS_32bit + type: ubuntu + virt: pv + user: ubuntu + - ami: ami-0611546c + name: ubuntu12.04LTS + type: ubuntu + virt: hvm + user: ubuntu + #----------------------------------------------------------------------------- + # Debian + - ami: ami-116d857a + name: debian8.1 + type: debian + virt: hvm + user: admin + userdata: | + #cloud-init + runcmd: + - [ apt-get, install, -y, curl ] + - ami: ami-e0efab88 + name: debian7.8.aws.1 + type: debian + virt: hvm + user: admin + userdata: | + #cloud-init + runcmd: + - [ apt-get, install, -y, curl ] + - ami: ami-e6eeaa8e + name: debian7.8.aws.1_32bit + type: debian + virt: pv + user: admin + userdata: | + cloud-init + runcmd: + - [ apt-get, install, -y, curl ] + #----------------------------------------------------------------------------- + # Other Redhat Distros + - ami: ami-60b6c60a + name: amazonlinux-2015.09.1 + type: centos + virt: hvm + user: ec2-user + - ami: ami-0d4cfd66 + name: amazonlinux-2015.03.1 + type: centos + virt: hvm + user: ec2-user + - ami: ami-a8d369c0 + name: RHEL7 + type: redhat + virt: hvm + user: ec2-user + - ami: ami-518bfb3b + name: fedora23 + type: fedora + virt: hvm + user: fedora + #----------------------------------------------------------------------------- + # CentOS + # These Marketplace AMIs must, irritatingly, have their terms manually + # agreed to on the AWS marketplace site for any new AWS account using them... + # - ami: ami-61bbf104 + # name: centos7 + # type: centos + # virt: hvm + # user: centos + # # centos6 requires EPEL repo added + # - ami: ami-57cd8732 + # name: centos6 + # type: centos + # virt: hvm + # user: centos + # userdata: | + # #cloud-config + # runcmd: + # - [ yum, install, -y, epel-release ] + # - [ iptables, -F ] From 372578ca92b56fc5a7e55b3ae6779cb1b776d1ce Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Fri, 4 Dec 2015 14:35:57 -0800 Subject: [PATCH 013/159] passing in instance data as environment variables --- multitester.py | 5 ++++- scripts/test_letsencrypt_auto_apache2.sh | 14 +++++++------- .../test_letsencrypt_auto_certonly_standalone.sh | 13 +++++++------ scripts/test_letsencrypt_auto_venv_only.sh | 2 +- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/multitester.py b/multitester.py index 116d5b5a8..dbf91143b 100644 --- a/multitester.py +++ b/multitester.py @@ -272,7 +272,10 @@ def config_and_launch_boulder(instance): def install_and_launch_letsencrypt(instance, boulder_url): execute(local_repo_to_remote) - with shell_env(BOULDER_URL=boulder_url): + with shell_env(BOULDER_URL=boulder_url, + PUBLIC_IP=instance.public_ip_address, + PRIVATE_IP=instance.private_ip_address, + PUBLIC_HOSTNAME=instance.public_dns_name): execute(deploy_script, cl_args.test_script) def grab_letsencrypt_log(): diff --git a/scripts/test_letsencrypt_auto_apache2.sh b/scripts/test_letsencrypt_auto_apache2.sh index 24980361a..087a2eb13 100755 --- a/scripts/test_letsencrypt_auto_apache2.sh +++ b/scripts/test_letsencrypt_auto_apache2.sh @@ -3,22 +3,22 @@ #install apache2 on apt systems # debian doesn't come with curl sudo apt-get update -sudo apt-get -y --no-upgrade install apache2 curl +sudo apt-get -y --no-upgrade install apache2 #curl -# $BOULDER_URL is dynamically set at execution +# $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL are dynamically set at execution # fetch instance data from EC2 metadata service -public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) -public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) -private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) +#public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) +#public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) +#private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) # For apache 2.4, set up ServerName sudo sed -i '/ServerName/ s/#ServerName/ServerName/' \ /etc/apache2/sites-available/000-default.conf -sudo sed -i '/ServerName/ s/www.example.com/'$public_host'/' \ +sudo sed -i '/ServerName/ s/www.example.com/'$PUBLIC_HOSTNAME'/' \ /etc/apache2/sites-available/000-default.conf # run letsencrypt-apache2 via letsencrypt-auto cd letsencrypt ./letsencrypt-auto -v --debug --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ - --domain $public_host --server $BOULDER_URL + --domain $PUBLIC_HOSTNAME --server $BOULDER_URL diff --git a/scripts/test_letsencrypt_auto_certonly_standalone.sh b/scripts/test_letsencrypt_auto_certonly_standalone.sh index e82c81bd4..10d7c3b5e 100755 --- a/scripts/test_letsencrypt_auto_certonly_standalone.sh +++ b/scripts/test_letsencrypt_auto_certonly_standalone.sh @@ -1,14 +1,15 @@ #!/bin/bash -x -# $BOULDER_URL is dynamically set at execution -# fetch instance data from EC2 metadata service -public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) -public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) -private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) +# $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL are dynamically set at execution + +# with curl, instance metadata available from EC2 metadata service: +#public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) +#public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) +#private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) cd letsencrypt ./letsencrypt-auto certonly -v --standalone --debug \ --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect \ --register-unsafely-without-email \ - --domain $public_host --server $BOULDER_URL + --domain $PUBLIC_HOSTNAME --server $BOULDER_URL diff --git a/scripts/test_letsencrypt_auto_venv_only.sh b/scripts/test_letsencrypt_auto_venv_only.sh index e6b7aed8d..476ad8bde 100755 --- a/scripts/test_letsencrypt_auto_venv_only.sh +++ b/scripts/test_letsencrypt_auto_venv_only.sh @@ -1,6 +1,6 @@ #!/bin/bash -x -# $BOULDER_URL is dynamically set at execution +# $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL are dynamically set at execution cd letsencrypt # help installs virtualenv and does nothing else From 261c421b2552e331939a05fbda4d8658292b9a36 Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Fri, 4 Dec 2015 14:41:50 -0800 Subject: [PATCH 014/159] minor cleanup --- .pylintrc | 335 ------------------------------------------------- multitester.py | 6 +- 2 files changed, 3 insertions(+), 338 deletions(-) delete mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 4f978cdd2..000000000 --- a/.pylintrc +++ /dev/null @@ -1,335 +0,0 @@ -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Profiled execution. -profile=no - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Pickle collected data for later comparisons. -persistent=yes - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -#load-plugins=linter_plugin - - -[MESSAGES CONTROL] - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable=fixme,locally-disabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes -# abstract-class-not-used cannot be disabled locally (at least in -# pylint 1.4.1), same for abstract-class-little-used - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (RP0004). -comment=no - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input,file - -# Good variable names which should always be accepted, separated by a comma -good-names=f,i,j,k,ex,Run,_,fd,logger - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Regular expression matching correct function names -function-rgx=[a-z_][a-z0-9_]{2,40}$ - -# Naming hint for function names -function-name-hint=[a-z_][a-z0-9_]{2,40}$ - -# Regular expression matching correct variable names -variable-rgx=[a-z_][a-z0-9_]{1,30}$ - -# Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression matching correct attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for attribute names -attr-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for argument names -argument-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression matching correct method names -method-rgx=[a-z_][a-z0-9_]{2,50}$ - -# Naming hint for method names -method-name-hint=[a-z_][a-z0-9_]{2,50}$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=(__.*__)|(test_[A-Za-z0-9_]*)|(_.*)|(.*Test$) - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging,logger - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=(unused)?_.*|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=6 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=yes - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=100 - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - -# List of optional constructs for which whitespace checking is disabled -no-space-check=trailing-comma - -# Maximum number of lines in a module -max-module-lines=1250 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -# This does something silly/broken... -#indent-after-paren=4 - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis -ignored-modules=pkg_resources,confargparse,argparse,six.moves,six.moves.urllib -# import errors ignored only in 1.4.4 -# https://bitbucket.org/logilab/pylint/commits/cd000904c9e2 - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject - -# When zope mode is activated, add a predefined set of Zope acquired attributes -# to generated-members. -zope=yes - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E0201 when accessed. Python regular -# expressions are accepted. -generated-members=REQUEST,acl_users,aq_parent - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defined in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by,implementedBy,providedBy - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=6 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=12 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/multitester.py b/multitester.py index dbf91143b..02645eae4 100644 --- a/multitester.py +++ b/multitester.py @@ -354,11 +354,11 @@ for target in targetlist: userdata = target['userdata'] else: userdata = '' - instances.append( make_instance('le-%s'%target['name'], + instances.append(make_instance('le-%s'%target['name'], target['ami'], KEYNAME, machine_type=machine_type, - userdata=userdata) ) + userdata=userdata)) # Configure and launch boulder server @@ -479,7 +479,7 @@ else: for ii, target in enumerate(targetlist): print(target['name'], target['ami'], - "%s@%s"%(target['user'],instances[ii].public_ip_address)) + "%s@%s"%(target['user'], instances[ii].public_ip_address)) # kill any connections fabric.network.disconnect_all() From 547b9b9244e07c1a8dba4061e2081a41a4b1110e Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Fri, 4 Dec 2015 15:18:51 -0800 Subject: [PATCH 015/159] specify test script by path --- multitester.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/multitester.py b/multitester.py index 02645eae4..daf6d49e5 100644 --- a/multitester.py +++ b/multitester.py @@ -23,7 +23,7 @@ Usage: >aws ec2 create-key-pair --profile HappyHacker --key-name MyKeyPair \ --query 'KeyMaterial' --output text > MyKeyPair.pem then: ->python multitester.py targets.yaml MyKeyPair.pem HappyHacker test_letsencrypt_auto_venv_only.sh +>python multitester.py targets.yaml MyKeyPair.pem HappyHacker scripts/test_letsencrypt_auto_venv_only.sh see: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html https://docs.aws.amazon.com/cli/latest/userguide/cli-ec2-keypairs.html @@ -54,7 +54,7 @@ parser.add_argument('aws_profile', help='profile for AWS (i.e. as in ~/.aws/certificates)') parser.add_argument('test_script', default='test_letsencrypt_auto_certonly_standalone.sh', - help='name of bash script in /scripts to deploy and run') + help='path of bash script in to deploy and run') #parser.add_argument('--script_args', # nargs='+', # help='space-delimited list of arguments to pass to the bash test script', @@ -254,12 +254,13 @@ def local_repo_clean(): with lcd(LOGDIR): local('rm le.tar.gz') -def deploy_script(scriptname, *args): +def deploy_script(scriptpath, *args): "copies to remote and executes local script" - with lcd('scripts'): - put(local_path=scriptname, remote_path='', mirror_local_mode=True) + #with lcd('scripts'): + put(local_path=scriptpath, remote_path='', mirror_local_mode=True) + scriptfile = os.path.split(scriptpath)[1] args_str = ' '.join(args) - run('./'+scriptname+' '+args_str) + run('./'+scriptfile+' '+args_str) def run_boulder(): with cd('$GOPATH/src/github.com/letsencrypt/boulder'): @@ -267,7 +268,7 @@ def run_boulder(): run('nohup ./start.py >& /dev/null < /dev/null &') def config_and_launch_boulder(instance): - execute(deploy_script, 'boulder_config.sh') + execute(deploy_script, 'scripts/boulder_config.sh') execute(run_boulder) def install_and_launch_letsencrypt(instance, boulder_url): @@ -290,6 +291,16 @@ def grab_letsencrypt_log(): # SCRIPT BEGINS #------------------------------------------------------------------------------- +# Fabric library controlled through global env parameters +env.key_filename = KEYFILE +env.shell = '/bin/bash -l -i -c' +env.connection_attempts = 5 +env.timeout = 10 +# replace default SystemExit thrown by fabric during trouble +class FabricException(Exception): + pass +env['abort_exception'] = FabricException + # Set up local copy of git repo #------------------------------------------------------------------------------- LOGDIR = "letest-%d"%int(time.time()) @@ -369,16 +380,6 @@ print(" server %s"%boulder_server) print("Configuring and Launching Boulder") -# Fabric library controlled through global env parameters -env.key_filename = KEYFILE -env.shell = '/bin/bash -l -i -c' -env.connection_attempts = 5 -env.timeout = 10 -# replace default SystemExit thrown by fabric during trouble -class FabricException(Exception): - pass -env['abort_exception'] = FabricException - # 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) From 50232f3fec359522bfbb34d74eecbdbe7d20491e Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Fri, 4 Dec 2015 15:21:58 -0800 Subject: [PATCH 016/159] report script used and logging dir --- multitester.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/multitester.py b/multitester.py index daf6d49e5..7c7bd3e2b 100644 --- a/multitester.py +++ b/multitester.py @@ -395,7 +395,8 @@ print("Boulder Server at (EC2 private ip): %s"%boulder_url) # Install and launch client scripts in parallel #------------------------------------------------------------------------------- -print("Running letsencrypt clients in parallel - output routed to log files.") +print("Uploading and running test script in parallel: %s"%cl_args.test_script) +print("Output routed to log files in %s"%LOGDIR) # (Advice: always use Manager.Queue, never regular multiprocessing.Queue # the latter has implementation flaws that deadlock it in some circumstances) manager = Manager() From 6c905056d23be6c75598c59511bdcf32cdef05db Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sat, 5 Dec 2015 18:41:57 +0200 Subject: [PATCH 017/159] Added option to disable apache module handling per OS basis --- letsencrypt-apache/letsencrypt_apache/configurator.py | 5 +++-- letsencrypt-apache/letsencrypt_apache/constants.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index ba3bdcd7d..1ca7e13dc 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -540,8 +540,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str port: Port to listen on """ - if "ssl_module" not in self.parser.modules: - self.enable_mod("ssl", temp=temp) + if constants.os_constant("handle_mods"): + if "ssl_module" not in self.parser.modules: + self.enable_mod("ssl", temp=temp) # Check for Listen # Note: This could be made to also look for ip:443 combo diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index e302a29fe..ee45cac70 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -9,6 +9,7 @@ CLI_DEFAULTS_DEBIAN = dict( enmod="a2enmod", dismod="a2dismod", le_vhost_ext="-le-ssl.conf", + handle_mods=True ) CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", @@ -16,6 +17,7 @@ CLI_DEFAULTS_CENTOS = dict( enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", + handle_mods=False ) CLI_DEFAULTS = { "debian": CLI_DEFAULTS_DEBIAN, From da5b980674c898457e2ff722738eaf67e5134961 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sat, 5 Dec 2015 18:53:13 +0200 Subject: [PATCH 018/159] Handle exe checks for distros missing some of the helper scripts --- letsencrypt-apache/letsencrypt_apache/configurator.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 1ca7e13dc..34512be72 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -138,8 +138,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ # Verify Apache is installed for exe in (self.conf("ctl"), self.conf("enmod"), self.conf("dismod")): - if not le_util.exe_exists(exe): - raise errors.NoInstallationError + if exe != None: + if not le_util.exe_exists(exe): + raise errors.NoInstallationError # Make sure configuration is valid self.config_test() From b856fc58af56672bfc7fea0b45b6d2f791cf1d45 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sat, 5 Dec 2015 19:10:40 +0200 Subject: [PATCH 019/159] Added correct os_info string for CentOS --- letsencrypt-apache/letsencrypt_apache/constants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index ee45cac70..658fcc70f 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -22,7 +22,8 @@ CLI_DEFAULTS_CENTOS = dict( CLI_DEFAULTS = { "debian": CLI_DEFAULTS_DEBIAN, "ubuntu": CLI_DEFAULTS_DEBIAN, - "centos": CLI_DEFAULTS_CENTOS + "centos": CLI_DEFAULTS_CENTOS, + "centos linux": CLI_DEFAULTS_CENTOS } """CLI defaults.""" From b723de431d2f419b83271f3c5c736ccf5e482716 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 6 Dec 2015 11:33:57 +0200 Subject: [PATCH 020/159] Config parameter for configuration location that != server root --- letsencrypt-apache/letsencrypt_apache/configurator.py | 4 +++- letsencrypt-apache/letsencrypt_apache/constants.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 34512be72..73af842d9 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -98,6 +98,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): help="SSL vhost configuration extension.") add("server-root", default=constants.os_constant("server_root"), help="Apache server root directory.") + add("config-root", default=constants.os_constant("config_root"), + help="Apache server configuration root") le_util.add_deprecated_argument(add, "init-script", 1) def __init__(self, *args, **kwargs): @@ -146,7 +148,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.config_test() self.parser = parser.ApacheParser( - self.aug, self.conf("server-root"), self.conf("ctl")) + self.aug, self.conf("config-root"), self.conf("ctl")) # Check for errors in parsing files with Augeas self.check_parsing_errors("httpd.aug") diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 658fcc70f..09fb8ba59 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -5,6 +5,7 @@ from letsencrypt import le_util CLI_DEFAULTS_DEBIAN = dict( server_root="/etc/apache2", + config_root="/etc/apache2", ctl="apache2ctl", enmod="a2enmod", dismod="a2dismod", @@ -13,6 +14,7 @@ CLI_DEFAULTS_DEBIAN = dict( ) CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", + config_root="/etc/httpd/conf.d", ctl="apachectl", enmod=None, dismod=None, From e9c389f125e4a7e51a80133403cad4a981156162 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 6 Dec 2015 22:40:51 +0200 Subject: [PATCH 021/159] New constant for VirtualHost - configuration directory and changing the configurator to use it --- .../letsencrypt_apache/configurator.py | 15 +++++++-------- .../letsencrypt_apache/constants.py | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 73af842d9..353e6c68a 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -98,8 +98,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): help="SSL vhost configuration extension.") add("server-root", default=constants.os_constant("server_root"), help="Apache server root directory.") - add("config-root", default=constants.os_constant("config_root"), - help="Apache server configuration root") + add("vhost-root", default=constants.os_constant("vhost_root"), + help="Apache server VirtualHost configuration root") le_util.add_deprecated_argument(add, "init-script", 1) def __init__(self, *args, **kwargs): @@ -148,7 +148,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.config_test() self.parser = parser.ApacheParser( - self.aug, self.conf("config-root"), self.conf("ctl")) + self.aug, self.conf("server-root"), self.conf("ctl")) # Check for errors in parsing files with Augeas self.check_parsing_errors("httpd.aug") @@ -481,10 +481,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :rtype: list """ - # Search sites-available, httpd.conf for possible virtual hosts + # Search vhost-root, httpd.conf for possible virtual hosts paths = self.aug.match( - ("/files%s/sites-available//*[label()=~regexp('%s')]" % - (self.parser.root, parser.case_i("VirtualHost")))) + ("/files%s//*[label()=~regexp('%s')]" % + (self.conf("vhost-root"), parser.case_i("VirtualHost")))) vhs = [] @@ -997,8 +997,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if len(ssl_vhost.name) < (255 - (len(redirect_filename) + 1)): redirect_filename = "le-redirect-%s.conf" % ssl_vhost.name - redirect_filepath = os.path.join( - self.parser.root, "sites-available", redirect_filename) + redirect_filepath = os.path.join(self.conf("vhost-root"), redirect_filename) # Register the new file that will be created # Note: always register the creation before writing to ensure file will diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 09fb8ba59..cf6351f22 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -5,7 +5,7 @@ from letsencrypt import le_util CLI_DEFAULTS_DEBIAN = dict( server_root="/etc/apache2", - config_root="/etc/apache2", + vhost_root="/etc/apache2/sites-available", ctl="apache2ctl", enmod="a2enmod", dismod="a2dismod", @@ -14,7 +14,7 @@ CLI_DEFAULTS_DEBIAN = dict( ) CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", - config_root="/etc/httpd/conf.d", + vhost_root="/etc/httpd/conf.d", ctl="apachectl", enmod=None, dismod=None, From db9bf90cf9aaa779cddd186f2f9cf234b1239e06 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 6 Dec 2015 22:42:51 +0200 Subject: [PATCH 022/159] Change apachectl parameters to work with other distros too --- letsencrypt-apache/letsencrypt_apache/configurator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 353e6c68a..7ad9f0ff8 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -1083,6 +1083,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :rtype: bool """ + # Always return true for distros without enabled / available + if self.conf("enmod") == None: + return True enabled_dir = os.path.join(self.parser.root, "sites-enabled") for entry in os.listdir(enabled_dir): try: @@ -1209,7 +1212,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ try: - le_util.run_script([self.conf("ctl"), "-k", "graceful"]) + le_util.run_script([self.conf("ctl"), "graceful"]) except errors.SubprocessError as err: raise errors.MisconfigurationError(str(err)) From 103454e4a304a1a3062dc51dddddcdb69b947cfe Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 6 Dec 2015 22:46:28 +0200 Subject: [PATCH 023/159] Support CentOS configuration layout in parser --- letsencrypt-apache/letsencrypt_apache/parser.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index ec5211ae4..c47dbd99f 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -546,8 +546,7 @@ class ApacheParser(object): def _find_config_root(self): """Find the Apache Configuration Root file.""" - location = ["apache2.conf", "httpd.conf"] - + location = ["apache2.conf", "httpd.conf", "conf/httpd.conf"] for name in location: if os.path.isfile(os.path.join(self.root, name)): return os.path.join(self.root, name) From b11f091023d7e1fc63b3cce66347540008ac0da9 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 6 Dec 2015 23:06:56 +0200 Subject: [PATCH 024/159] Cleanup --- letsencrypt-apache/letsencrypt_apache/configurator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 7ad9f0ff8..ac7ce76d4 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -140,7 +140,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ # Verify Apache is installed for exe in (self.conf("ctl"), self.conf("enmod"), self.conf("dismod")): - if exe != None: + if exe is not None: if not le_util.exe_exists(exe): raise errors.NoInstallationError @@ -472,7 +472,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self._add_servernames(vhost) return vhost - # TODO: make "sites-available" a configurable directory def get_virtual_hosts(self): """Returns list of virtual hosts found in the Apache configuration. From e58c0a530fa180438392adb7aa9e205a1931cbdb Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 01:15:29 +0200 Subject: [PATCH 025/159] Call paper with customizable vhost root parameter --- .../letsencrypt_apache/configurator.py | 2 +- letsencrypt-apache/letsencrypt_apache/parser.py | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index ac7ce76d4..ce0714668 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -148,7 +148,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.config_test() self.parser = parser.ApacheParser( - self.aug, self.conf("server-root"), self.conf("ctl")) + self.aug, self.conf("server-root"), self.conf("vhost-root"), self.conf("ctl")) # Check for errors in parsing files with Augeas self.check_parsing_errors("httpd.aug") diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index c47dbd99f..2cd258a1b 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -28,7 +28,7 @@ class ApacheParser(object): arg_var_interpreter = re.compile(r"\$\{[^ \}]*}") fnmatch_chars = set(["*", "?", "\\", "[", "]"]) - def __init__(self, aug, root, ctl): + def __init__(self, aug, root, vhostroot, ctl): # Note: Order is important here. # This uses the binary, so it can be done first. @@ -44,6 +44,8 @@ class ApacheParser(object): self.loc = {"root": self._find_config_root()} self._parse_file(self.loc["root"]) + self.vhostroot = os.path.abspath(vhostroot) + # This problem has been fixed in Augeas 1.0 self.standardize_excl() @@ -56,9 +58,12 @@ class ApacheParser(object): # Set up rest of locations self.loc.update(self._set_locations()) - # Must also attempt to parse sites-available or equivalent - # Sites-available is not included naturally in configuration - self._parse_file(os.path.join(self.root, "sites-available") + "/*") + # Take the CentOS layout into account, httpd.conf not in httpd root + self._parse_file(os.path.join(self.root, "conf") + "/httpd.conf") + + # Must also attempt to parse virtual host root + self._parse_file(self.vhostroot + "/*") + def init_modules(self): """Iterates on the configuration until no new modules are loaded. From 651c8702cbd81a35fcfab081cf4051e7062b1e3b Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 01:16:07 +0200 Subject: [PATCH 026/159] Commented out TLS SNI apache extra config file logging options, because of distro specific paths --- .../letsencrypt_apache/options-ssl-apache.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/options-ssl-apache.conf b/letsencrypt-apache/letsencrypt_apache/options-ssl-apache.conf index 2a724d7ec..ec07a4ba3 100644 --- a/letsencrypt-apache/letsencrypt_apache/options-ssl-apache.conf +++ b/letsencrypt-apache/letsencrypt_apache/options-ssl-apache.conf @@ -14,9 +14,9 @@ SSLOptions +StrictRequire LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common -CustomLog /var/log/apache2/access.log vhost_combined -LogLevel warn -ErrorLog /var/log/apache2/error.log +#CustomLog /var/log/apache2/access.log vhost_combined +#LogLevel warn +#ErrorLog /var/log/apache2/error.log # Always ensure Cookies have "Secure" set (JAH 2012/1) #Header edit Set-Cookie (?i)^(.*)(;\s*secure)??((\s*;)?(.*)) "$1; Secure$3$4" From cd0ae93ddc93fa02acde43d7f91ff65d5659c12b Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 11:06:32 +0200 Subject: [PATCH 027/159] Be more explicit in the configuration file parsing in vhost directory, augeas will silently fail if it encounters something funny --- letsencrypt-apache/letsencrypt_apache/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 2cd258a1b..abdf6e449 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -62,7 +62,7 @@ class ApacheParser(object): self._parse_file(os.path.join(self.root, "conf") + "/httpd.conf") # Must also attempt to parse virtual host root - self._parse_file(self.vhostroot + "/*") + self._parse_file(self.vhostroot + "/*.conf") def init_modules(self): From 6e3da9e0438d26722623fdfb838b8e00cfeb36dc Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 11:07:31 +0200 Subject: [PATCH 028/159] Configurable directory path for challenge configuration file --- letsencrypt-apache/letsencrypt_apache/configurator.py | 3 +++ letsencrypt-apache/letsencrypt_apache/constants.py | 6 ++++-- letsencrypt-apache/letsencrypt_apache/tls_sni_01.py | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index ce0714668..642a46696 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -100,6 +100,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): help="Apache server root directory.") add("vhost-root", default=constants.os_constant("vhost_root"), help="Apache server VirtualHost configuration root") + add("challenge-location", + default=constants.os_constant("challenge_location"), + help="Directory path for challenge configuration.") le_util.add_deprecated_argument(add, "init-script", 1) def __init__(self, *args, **kwargs): diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index cf6351f22..0fd9a25dc 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -10,7 +10,8 @@ CLI_DEFAULTS_DEBIAN = dict( enmod="a2enmod", dismod="a2dismod", le_vhost_ext="-le-ssl.conf", - handle_mods=True + handle_mods=True, + challenge_location="/etc/apache2" ) CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", @@ -19,7 +20,8 @@ CLI_DEFAULTS_CENTOS = dict( enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", - handle_mods=False + handle_mods=False, + challenge_location="/etc/httpd/conf.d" ) CLI_DEFAULTS = { "debian": CLI_DEFAULTS_DEBIAN, diff --git a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py b/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py index 4284e240c..2049eb574 100644 --- a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py +++ b/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py @@ -50,7 +50,7 @@ class ApacheTlsSni01(common.TLSSNI01): super(ApacheTlsSni01, self).__init__(*args, **kwargs) self.challenge_conf = os.path.join( - self.configurator.conf("server-root"), + self.configurator.conf("challenge-location"), "le_tls_sni_01_cert_challenge.conf") def perform(self): From 3d9f8c9748edf6c36a8a40cac8be45ec933ced44 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 12:01:35 +0200 Subject: [PATCH 029/159] Cleanup & pydoc --- letsencrypt-apache/letsencrypt_apache/configurator.py | 2 +- letsencrypt-apache/letsencrypt_apache/constants.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 642a46696..c8b42bd5f 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -1087,7 +1087,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ # Always return true for distros without enabled / available if self.conf("enmod") == None: - return True + return True enabled_dir = os.path.join(self.parser.root, "sites-enabled") for entry in os.listdir(enabled_dir): try: diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 0fd9a25dc..24cf7373a 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -59,6 +59,10 @@ HEADER_ARGS = {"Strict-Transport-Security": HSTS_ARGS, "Upgrade-Insecure-Requests": UIR_ARGS} def os_constant(key): + """Get a constant value for operating system + :param key: name of cli constant + :return: value of constant for active os + """ os_info = le_util.get_os_info() try: constants = CLI_DEFAULTS[os_info[0].lower()] From 3701560a88279e435e9a49faac538deaa8cc9ced Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 12:03:54 +0200 Subject: [PATCH 030/159] Fix existing tests --- .../tests/configurator_test.py | 8 ++++---- .../letsencrypt_apache/tests/parser_test.py | 10 +++++++--- .../letsencrypt_apache/tests/tls_sni_01_test.py | 2 +- .../letsencrypt_apache/tests/util.py | 17 +++++++++++------ 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index fcccfaae2..50b23b815 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -27,7 +27,7 @@ class TwoVhost80Test(util.ApacheTest): super(TwoVhost80Test, self).setUp() self.config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir) + self.config_path, self.vhost_path, self.config_dir, self.work_dir) self.vh_truth = util.get_vh_truth( self.temp_dir, "debian_apache_2_4/two_vhost_80") @@ -244,7 +244,7 @@ class TwoVhost80Test(util.ApacheTest): def test_deploy_cert_newssl(self): self.config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir, version=(2, 4, 16)) + self.config_path, self.vhost_path, self.config_dir, self.work_dir, version=(2, 4, 16)) self.config.parser.modules.add("ssl_module") self.config.parser.modules.add("mod_ssl.c") @@ -276,7 +276,7 @@ class TwoVhost80Test(util.ApacheTest): def test_deploy_cert_newssl_no_fullchain(self): self.config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir, version=(2, 4, 16)) + self.config_path, self.vhost_path, self.config_dir, self.work_dir, version=(2, 4, 16)) self.config.parser.modules.add("ssl_module") self.config.parser.modules.add("mod_ssl.c") @@ -289,7 +289,7 @@ class TwoVhost80Test(util.ApacheTest): def test_deploy_cert_old_apache_no_chain(self): self.config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir, version=(2, 4, 7)) + self.config_path, self.vhost_path, self.config_dir, self.work_dir, version=(2, 4, 7)) self.config.parser.modules.add("ssl_module") self.config.parser.modules.add("mod_ssl.c") diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py index bc1f316f9..ca51c6fd8 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py @@ -193,7 +193,9 @@ class ParserInitTest(util.ApacheTest): path = os.path.join( self.temp_dir, "debian_apache_2_4/////two_vhost_80/../two_vhost_80/apache2") - parser = ApacheParser(self.aug, path, "dummy_ctl") + + parser = ApacheParser(self.aug, path, + "/dummy/vhostpath", "dummy_ctl") self.assertEqual(parser.root, self.config_path) @@ -202,7 +204,8 @@ class ParserInitTest(util.ApacheTest): with mock.patch("letsencrypt_apache.parser.ApacheParser." "update_runtime_variables"): parser = ApacheParser( - self.aug, os.path.relpath(self.config_path), "dummy_ctl") + self.aug, os.path.relpath(self.config_path), + "/dummy/vhostpath", "dummy_ctl") self.assertEqual(parser.root, self.config_path) @@ -211,7 +214,8 @@ class ParserInitTest(util.ApacheTest): with mock.patch("letsencrypt_apache.parser.ApacheParser." "update_runtime_variables"): parser = ApacheParser( - self.aug, self.config_path + os.path.sep, "dummy_ctl") + self.aug, self.config_path + os.path.sep, + "/dummy/vhostpath", "dummy_ctl") self.assertEqual(parser.root, self.config_path) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py b/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py index f4dff7734..6caca2520 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py @@ -20,7 +20,7 @@ class TlsSniPerformTest(util.ApacheTest): super(TlsSniPerformTest, self).setUp() config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir) + self.config_path, self.vhost_path, self.config_dir, self.work_dir) config.config.tls_sni_01_port = 443 from letsencrypt_apache import tls_sni_01 diff --git a/letsencrypt-apache/letsencrypt_apache/tests/util.py b/letsencrypt-apache/letsencrypt_apache/tests/util.py index 0c60373f2..95c95e6a9 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/util.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/util.py @@ -23,7 +23,8 @@ from letsencrypt_apache import obj class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods def setUp(self, test_dir="debian_apache_2_4/two_vhost_80", - config_root="debian_apache_2_4/two_vhost_80/apache2"): + config_root="debian_apache_2_4/two_vhost_80/apache2", + vhost_root="debian_apache_2_4/two_vhost_80/apache2/sites-available"): # pylint: disable=arguments-differ super(ApacheTest, self).setUp() @@ -36,6 +37,7 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods constants.MOD_SSL_CONF_DEST) self.config_path = os.path.join(self.temp_dir, config_root) + self.vhost_path = os.path.join(self.temp_dir, vhost_root) self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector( "rsa512_key.pem")) @@ -44,8 +46,9 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods def setUp(self, test_dir="debian_apache_2_4/two_vhost_80", - config_root="debian_apache_2_4/two_vhost_80/apache2"): - super(ParserTest, self).setUp(test_dir, config_root) + config_root="debian_apache_2_4/two_vhost_80/apache2", + vhost_root="debian_apache_2_4/two_vhost_80/apache2/sites-available"): + super(ParserTest, self).setUp(test_dir, config_root, vhost_root) zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) @@ -55,11 +58,11 @@ class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods with mock.patch("letsencrypt_apache.parser.ApacheParser." "update_runtime_variables"): self.parser = ApacheParser( - self.aug, self.config_path, "dummy_ctl_path") + self.aug, self.config_path, self.vhost_path, "dummy_ctl_path") def get_apache_configurator( - config_path, config_dir, work_dir, version=(2, 4, 7), conf=None): + config_path, vhost_path, config_dir, work_dir, version=(2, 4, 7), conf=None): """Create an Apache Configurator with the specified options. :param conf: Function that returns binary paths. self.conf in Configurator @@ -68,7 +71,9 @@ def get_apache_configurator( backups = os.path.join(work_dir, "backups") mock_le_config = mock.MagicMock( apache_server_root=config_path, - apache_le_vhost_ext=constants.CLI_DEFAULTS["le_vhost_ext"], + apache_vhost_root=vhost_path, + apache_le_vhost_ext=constants.os_constant("le_vhost_ext"), + apache_challenge_location=config_path, backup_dir=backups, config_dir=config_dir, temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"), From c9f14e04618f10e8240c5fbbd39ade1c8699c2de Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 12:13:24 +0200 Subject: [PATCH 031/159] Augeas test fix --- .../letsencrypt_apache/tests/augeas_configurator_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py index 815e6fc44..b70e1c7f1 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py @@ -17,7 +17,7 @@ class AugeasConfiguratorTest(util.ApacheTest): super(AugeasConfiguratorTest, self).setUp() self.config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir) + self.config_path, self.vhost_path, self.config_dir, self.work_dir) self.vh_truth = util.get_vh_truth( self.temp_dir, "debian_apache_2_4/two_vhost_80") From 9e0bcf9f3c4593656f4a305ee7e55c439c686fa8 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 12:38:52 +0200 Subject: [PATCH 032/159] Add cli parameter for handle_mods --- letsencrypt-apache/letsencrypt_apache/configurator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index c8b42bd5f..9acc5cad6 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -103,6 +103,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): add("challenge-location", default=constants.os_constant("challenge_location"), help="Directory path for challenge configuration.") + add("handle-modules", default=constants.os_constant("handle_mods"), + help="Let installer handle enabling required modules for you."+ + "(Only Ubuntu/Debian currently)") le_util.add_deprecated_argument(add, "init-script", 1) def __init__(self, *args, **kwargs): @@ -545,7 +548,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str port: Port to listen on """ - if constants.os_constant("handle_mods"): + if self.conf("handle_mods"): if "ssl_module" not in self.parser.modules: self.enable_mod("ssl", temp=temp) From b02a881c6e07ae289564be4dc94b976e629504e4 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 12:42:40 +0200 Subject: [PATCH 033/159] Constant value per OS basis if installer should handle enabling / disabling sites --- letsencrypt-apache/letsencrypt_apache/configurator.py | 2 +- letsencrypt-apache/letsencrypt_apache/constants.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 9acc5cad6..30be653a1 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -1089,7 +1089,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ # Always return true for distros without enabled / available - if self.conf("enmod") == None: + if not constants.os_constant("handle_sites"): return True enabled_dir = os.path.join(self.parser.root, "sites-enabled") for entry in os.listdir(enabled_dir): diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 24cf7373a..70d0beabb 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -11,6 +11,7 @@ CLI_DEFAULTS_DEBIAN = dict( dismod="a2dismod", le_vhost_ext="-le-ssl.conf", handle_mods=True, + handle_sites=True, challenge_location="/etc/apache2" ) CLI_DEFAULTS_CENTOS = dict( @@ -21,6 +22,7 @@ CLI_DEFAULTS_CENTOS = dict( dismod=None, le_vhost_ext="-le-ssl.conf", handle_mods=False, + handle_sites=False, challenge_location="/etc/httpd/conf.d" ) CLI_DEFAULTS = { From 43473827802a2756dadcce2ce15228db5e876c48 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 12:46:56 +0200 Subject: [PATCH 034/159] Moved enable_site check to correct place --- letsencrypt-apache/letsencrypt_apache/configurator.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 30be653a1..c4ec3c2e7 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -245,9 +245,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if chain_path is not None: self.save_notes += "\tSSLCertificateChainFile %s\n" % chain_path - # Make sure vhost is enabled - if not vhost.enabled: - self.enable_site(vhost) + # Make sure vhost is enabled if distro with enabled / available + if constants.os_constant("handle_sites"): + if not vhost.enabled: + self.enable_site(vhost) def choose_vhost(self, target_name, temp=False): """Chooses a virtual host based on the given domain name. @@ -1088,9 +1089,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :rtype: bool """ - # Always return true for distros without enabled / available - if not constants.os_constant("handle_sites"): - return True + enabled_dir = os.path.join(self.parser.root, "sites-enabled") for entry in os.listdir(enabled_dir): try: From 718a6481f1c2f72139d78ec5fa63a1c817f52bb7 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 13:16:48 +0200 Subject: [PATCH 035/159] Tests for the lone function in constants --- .../tests/constants_test.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 letsencrypt-apache/letsencrypt_apache/tests/constants_test.py diff --git a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py b/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py new file mode 100644 index 000000000..478debb59 --- /dev/null +++ b/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py @@ -0,0 +1,22 @@ +import mock +import unittest + +from letsencrypt_apache import constants + + +class ConstantsTest(unittest.TestCase): + + @mock.patch("letsencrypt.le_util.get_os_info") + def test_get_debian_value(self, os_info): + os_info.return_value = ('Debian','','') + self.assertEqual(constants.os_constant("ctl"), "apache2ctl") + + @mock.patch("letsencrypt.le_util.get_os_info") + def test_get_centos_value(self, os_info): + os_info.return_value = ('CentOS Linux','','') + self.assertEqual(constants.os_constant("ctl"), "apachectl") + + @mock.patch("letsencrypt.le_util.get_os_info") + def test_get_default_value(self, os_info): + os_info.return_value = ('Nonexistent Linux','','') + self.assertEqual(constants.os_constant("ctl"), "apache2ctl") From 47eb7d76cdd25405641b8d7db3349a33c6f5beb8 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 13:37:58 +0200 Subject: [PATCH 036/159] PEP8 & linter love --- letsencrypt-apache/letsencrypt_apache/configurator.py | 2 +- letsencrypt-apache/letsencrypt_apache/constants.py | 1 + letsencrypt-apache/letsencrypt_apache/parser.py | 1 - .../letsencrypt_apache/tests/constants_test.py | 8 +++++--- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index c4ec3c2e7..549a19188 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -104,7 +104,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): default=constants.os_constant("challenge_location"), help="Directory path for challenge configuration.") add("handle-modules", default=constants.os_constant("handle_mods"), - help="Let installer handle enabling required modules for you."+ + help="Let installer handle enabling required modules for you." + "(Only Ubuntu/Debian currently)") le_util.add_deprecated_argument(add, "init-script", 1) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 70d0beabb..773ceb266 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -60,6 +60,7 @@ UIR_ARGS = ["always", "set", "Content-Security-Policy", HEADER_ARGS = {"Strict-Transport-Security": HSTS_ARGS, "Upgrade-Insecure-Requests": UIR_ARGS} + def os_constant(key): """Get a constant value for operating system :param key: name of cli constant diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index abdf6e449..4ab2b82a0 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -64,7 +64,6 @@ class ApacheParser(object): # Must also attempt to parse virtual host root self._parse_file(self.vhostroot + "/*.conf") - def init_modules(self): """Iterates on the configuration until no new modules are loaded. diff --git a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py b/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py index 478debb59..63eb5c783 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py @@ -1,3 +1,5 @@ +"""Test for letsencrypt_apache.configurator.""" + import mock import unittest @@ -8,15 +10,15 @@ class ConstantsTest(unittest.TestCase): @mock.patch("letsencrypt.le_util.get_os_info") def test_get_debian_value(self, os_info): - os_info.return_value = ('Debian','','') + os_info.return_value = ('Debian', '', '') self.assertEqual(constants.os_constant("ctl"), "apache2ctl") @mock.patch("letsencrypt.le_util.get_os_info") def test_get_centos_value(self, os_info): - os_info.return_value = ('CentOS Linux','','') + os_info.return_value = ('CentOS Linux', '', '') self.assertEqual(constants.os_constant("ctl"), "apachectl") @mock.patch("letsencrypt.le_util.get_os_info") def test_get_default_value(self, os_info): - os_info.return_value = ('Nonexistent Linux','','') + os_info.return_value = ('Nonexistent Linux', '', '') self.assertEqual(constants.os_constant("ctl"), "apache2ctl") From f479a9b894d1a55f6319ef98b837a442188d1f91 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 14:22:56 +0200 Subject: [PATCH 037/159] Added fedora layout --- letsencrypt-apache/letsencrypt_apache/constants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 773ceb266..87c3e6e66 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -29,7 +29,8 @@ CLI_DEFAULTS = { "debian": CLI_DEFAULTS_DEBIAN, "ubuntu": CLI_DEFAULTS_DEBIAN, "centos": CLI_DEFAULTS_CENTOS, - "centos linux": CLI_DEFAULTS_CENTOS + "centos linux": CLI_DEFAULTS_CENTOS, + "fedora": CLI_DEFAULTS_CENTOS } """CLI defaults.""" From 5dd3ecfb5324ac424b17663ded13830a48b70f06 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 14:42:14 +0200 Subject: [PATCH 038/159] Respect tool setting in rpm bootstrap --- bootstrap/_rpm_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/_rpm_common.sh b/bootstrap/_rpm_common.sh index b80a9555b..57b28240e 100755 --- a/bootstrap/_rpm_common.sh +++ b/bootstrap/_rpm_common.sh @@ -49,7 +49,7 @@ then fi -if yum list installed "httpd" >/dev/null 2>&1; then +if $tool list installed "httpd" >/dev/null 2>&1; then if ! $tool install -y mod_ssl then echo "Apache found, but mod_ssl could not be installed." From 0ce4fa4c6732a0d5ca38a80b47c070ac00b85885 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 17:04:22 +0200 Subject: [PATCH 039/159] Was using constant name instead of conf name --- letsencrypt-apache/letsencrypt_apache/configurator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 549a19188..e99f5cbe0 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -549,7 +549,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str port: Port to listen on """ - if self.conf("handle_mods"): + if self.conf("handle-modules"): if "ssl_module" not in self.parser.modules: self.enable_mod("ssl", temp=temp) From d4337f3936031169b4a8afeed29aa99d59a05841 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 17:12:42 +0200 Subject: [PATCH 040/159] Give user command line control on using enable site helper script --- letsencrypt-apache/letsencrypt_apache/configurator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index e99f5cbe0..52635ca11 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -106,6 +106,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): add("handle-modules", default=constants.os_constant("handle_mods"), help="Let installer handle enabling required modules for you." + "(Only Ubuntu/Debian currently)") + add("handle-sites", default=constants.os_constant("handle_sites"), + help="Let installer handle enabling sites for you." + + "(Only Ubuntu/Debian currently)") le_util.add_deprecated_argument(add, "init-script", 1) def __init__(self, *args, **kwargs): @@ -246,7 +249,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.save_notes += "\tSSLCertificateChainFile %s\n" % chain_path # Make sure vhost is enabled if distro with enabled / available - if constants.os_constant("handle_sites"): + if self.conf("handle-sites"): if not vhost.enabled: self.enable_site(vhost) From 73878f2457a7eebb36a2deed31006785e702d357 Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Tue, 8 Dec 2015 11:30:13 +0200 Subject: [PATCH 041/159] Abort when no Pythons are found It seems ill-advised to continue without setting the LE_PYTHON variable, when the very next command tries to use it. --- letsencrypt-auto | 1 + 1 file changed, 1 insertion(+) diff --git a/letsencrypt-auto b/letsencrypt-auto index 44c71883c..5ad4abd76 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -97,6 +97,7 @@ DeterminePythonVersion() { export LE_PYTHON=${LE_PYTHON:-python} else echo "Cannot find any Pythons... please install one!" + exit 1 fi PYVER=`$LE_PYTHON --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` From 55cac0dc9f9cf3db175bcf79666963e148b11871 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Wed, 9 Dec 2015 17:05:31 +0200 Subject: [PATCH 042/159] Fixed a2dismod help text --- letsencrypt-apache/letsencrypt_apache/configurator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 3ad6032f1..be9825304 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -93,7 +93,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): add("enmod", default=constants.os_constant("enmod"), help="Path to the Apache 'a2enmod' binary.") add("dismod", default=constants.os_constant("dismod"), - help="Path to the Apache 'a2enmod' binary.") + help="Path to the Apache 'a2dismod' binary.") add("le-vhost-ext", default=constants.os_constant("le_vhost_ext"), help="SSL vhost configuration extension.") add("server-root", default=constants.os_constant("server_root"), From 37c02927d59e247ed9b9da7496c79aa2ae1ac883 Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Wed, 9 Dec 2015 16:52:02 -0800 Subject: [PATCH 043/159] current nonworking progress at scripting install of httpd on centos-like systems --- README.md | 9 ++-- multitester.py | 7 +-- scripts/test_apache2.sh | 50 +++++++++++++++++ scripts/test_letsencrypt_auto_apache2.sh | 24 --------- targets.yaml | 68 ++++++++++++++++-------- 5 files changed, 106 insertions(+), 52 deletions(-) create mode 100755 scripts/test_apache2.sh delete mode 100755 scripts/test_letsencrypt_auto_apache2.sh diff --git a/README.md b/README.md index 35950b18c..a5d365c8f 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,9 @@ then: ``` see: - https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html - https://docs.aws.amazon.com/cli/latest/userguide/cli-ec2-keypairs.html +- https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html +- https://docs.aws.amazon.com/cli/latest/userguide/cli-ec2-keypairs.html -https://github.com/letsencrypt/boulder -https://github.com/letsencrypt/letsencrypt \ No newline at end of file +main repos: +- https://github.com/letsencrypt/boulder +- https://github.com/letsencrypt/letsencrypt diff --git a/multitester.py b/multitester.py index 7c7bd3e2b..37e6a479c 100644 --- a/multitester.py +++ b/multitester.py @@ -271,12 +271,13 @@ def config_and_launch_boulder(instance): execute(deploy_script, 'scripts/boulder_config.sh') execute(run_boulder) -def install_and_launch_letsencrypt(instance, boulder_url): +def install_and_launch_letsencrypt(instance, boulder_url, target): execute(local_repo_to_remote) with shell_env(BOULDER_URL=boulder_url, PUBLIC_IP=instance.public_ip_address, PRIVATE_IP=instance.private_ip_address, - PUBLIC_HOSTNAME=instance.public_dns_name): + PUBLIC_HOSTNAME=instance.public_dns_name, + OS_TYPE=target['type']): execute(deploy_script, cl_args.test_script) def grab_letsencrypt_log(): @@ -423,7 +424,7 @@ def test_client_process(inqueue, outqueue): print(env.host_string) try: - install_and_launch_letsencrypt(instances[ii], boulder_url) + install_and_launch_letsencrypt(instances[ii], boulder_url, target) outqueue.put((ii, target, 'pass')) print("%s - %s SUCCESS"%(target['ami'], target['name'])) except: diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh new file mode 100755 index 000000000..fe8b094fa --- /dev/null +++ b/scripts/test_apache2.sh @@ -0,0 +1,50 @@ +#!/bin/bash -x + +#install apache2 on apt systems +# debian doesn't come with curl +#sudo apt-get update +#sudo apt-get -y --no-upgrade install apache2 #curl + +# $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL are dynamically set at execution +# fetch instance data from EC2 metadata service +#public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) +#public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) +#private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) + +if [ $OS_TYPE = "ubuntu" ] +then + CONFFILE=/etc/apache2/sites-available/000-default.conf + sudo apt-get update + sudo apt-get -y --no-upgrade install apache2 #curl + # For apache 2.4, set up ServerName + sudo sed -i '/ServerName/ s/#ServerName/ServerName/' $CONFFILE + sudo sed -i '/ServerName/ s/www.example.com/'$PUBLIC_HOSTNAME'/' $CONFFILE +elif [ $OS_TYPE = "centos" ] +then + CONFFILE=/etc/httpd/conf/httpd.conf + sudo yum -y install httpd + sudo service httpd start + sudo mkdir -p /var/www/$PUBLIC_HOSTNAME/public_html + sudo chmod -R 777 /var/www + sudo echo 'foo\nbar' > /var/www/$PUBLIC_HOSTNAME/public_html/index.html + sudo mkdir /etc/httpd/sites-available + sudo mkdir /etc/httpd/sites-enabled + sudo echo "IncludeOptional sites-enabled/*.conf" >> /etc/httpd/conf/httpd.conf + sudo echo """ + + ServerName $PUBLIC_HOSTNAME + DocumentRoot /var/www/$PUBLIC_HOSTNAME/public_html + ErrorLog /var/www/$PUBLIC_HOSTNAME/error.log + CustomLog /var/www/$PUBLIC_HOSTNAME/requests.log combined +""" >> /etc/httpd/sites-available/$PUBLIC_HOSTNAME.conf + sudo cp /etc/httpd/sites-available/$PUBLIC_HOSTNAME.conf /etc/httpd/sites-enabled/ +fi + +# run letsencrypt-apache2 via letsencrypt-auto +cd letsencrypt +./bootstrap/install-deps.sh +./bootstrap/dev/venv.sh +source ./venv/bin/activate +sudo ./venv/bin/letsencrypt -v --debug --text --agree-dev-preview --agree-tos \ + --renew-by-default --redirect --register-unsafely-without-email \ + --domain $PUBLIC_HOSTNAME --server $BOULDER_URL diff --git a/scripts/test_letsencrypt_auto_apache2.sh b/scripts/test_letsencrypt_auto_apache2.sh deleted file mode 100755 index 087a2eb13..000000000 --- a/scripts/test_letsencrypt_auto_apache2.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -x - -#install apache2 on apt systems -# debian doesn't come with curl -sudo apt-get update -sudo apt-get -y --no-upgrade install apache2 #curl - -# $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL are dynamically set at execution -# fetch instance data from EC2 metadata service -#public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) -#public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) -#private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) - -# For apache 2.4, set up ServerName -sudo sed -i '/ServerName/ s/#ServerName/ServerName/' \ - /etc/apache2/sites-available/000-default.conf -sudo sed -i '/ServerName/ s/www.example.com/'$PUBLIC_HOSTNAME'/' \ - /etc/apache2/sites-available/000-default.conf - -# run letsencrypt-apache2 via letsencrypt-auto -cd letsencrypt -./letsencrypt-auto -v --debug --text --agree-dev-preview --agree-tos \ - --renew-by-default --redirect --register-unsafely-without-email \ - --domain $PUBLIC_HOSTNAME --server $BOULDER_URL diff --git a/targets.yaml b/targets.yaml index 4547366b3..384f82df6 100644 --- a/targets.yaml +++ b/targets.yaml @@ -30,7 +30,7 @@ targets: # Debian - ami: ami-116d857a name: debian8.1 - type: debian + type: ubuntu virt: hvm user: admin userdata: | @@ -39,7 +39,7 @@ targets: - [ apt-get, install, -y, curl ] - ami: ami-e0efab88 name: debian7.8.aws.1 - type: debian + type: ubuntu virt: hvm user: admin userdata: | @@ -48,7 +48,7 @@ targets: - [ apt-get, install, -y, curl ] - ami: ami-e6eeaa8e name: debian7.8.aws.1_32bit - type: debian + type: ubuntu virt: pv user: admin userdata: | @@ -62,38 +62,64 @@ targets: type: centos virt: hvm user: ec2-user + userdata: | + #cloud-config + runcmd: + - yum -y install httpd + - service httpd start - ami: ami-0d4cfd66 name: amazonlinux-2015.03.1 type: centos virt: hvm user: ec2-user + userdata: | + #cloud-config + runcmd: + - yum -y install httpd + - service httpd start - ami: ami-a8d369c0 name: RHEL7 - type: redhat + type: centos virt: hvm user: ec2-user + userdata: | + #cloud-config + runcmd: + - yum -y install httpd + - service httpd start - ami: ami-518bfb3b name: fedora23 - type: fedora + type: centos virt: hvm user: fedora + userdata: | + #cloud-config + runcmd: + - yum -y install httpd + - service httpd start #----------------------------------------------------------------------------- # CentOS # These Marketplace AMIs must, irritatingly, have their terms manually # agreed to on the AWS marketplace site for any new AWS account using them... - # - ami: ami-61bbf104 - # name: centos7 - # type: centos - # virt: hvm - # user: centos - # # centos6 requires EPEL repo added - # - ami: ami-57cd8732 - # name: centos6 - # type: centos - # virt: hvm - # user: centos - # userdata: | - # #cloud-config - # runcmd: - # - [ yum, install, -y, epel-release ] - # - [ iptables, -F ] + - ami: ami-61bbf104 + name: centos7 + type: centos + virt: hvm + user: centos + userdata: | + #cloud-config + runcmd: + - yum -y install httpd + - service httpd start + # centos6 requires EPEL repo added + - ami: ami-57cd8732 + name: centos6 + type: centos + virt: hvm + user: centos + userdata: | + #cloud-config + runcmd: + - yum install -y epel-release httpd + - service httpd start + - iptables -F From 0a1b9c2bf0d4f9018bf87b08361b102325b06d3e Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Wed, 9 Dec 2015 17:05:38 -0800 Subject: [PATCH 044/159] fixed failing tests for changes that allow apache22 --- letsencrypt-apache/letsencrypt_apache/parser.py | 1 - .../letsencrypt_apache/tests/configurator_test.py | 13 ++++++++++++- .../letsencrypt_apache/tests/parser_test.py | 6 +++--- .../letsencrypt_apache/tests/tls_sni_01_test.py | 3 ++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 4ed83e652..8f15ab10c 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -107,7 +107,6 @@ class ApacheParser(object): except ValueError: self.unparsable = True return - #raise errors.PluginError("Unable to parse runtime variables") for match in matches: if match.count("=") > 1: diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index 0b6170e1d..4e166dfc8 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -28,10 +28,18 @@ class TwoVhost80Test(util.ApacheTest): self.config = util.get_apache_configurator( self.config_path, self.config_dir, self.work_dir) - + self.config = self.mock_deploy_cert(self.config) self.vh_truth = util.get_vh_truth( self.temp_dir, "debian_apache_2_4/two_vhost_80") + def mock_deploy_cert(self, config): + self.config.real_deploy_cert = self.config.deploy_cert + def mocked_deploy_cert(*args, **kwargs): + with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod") as mock_enable: + config.real_deploy_cert(*args, **kwargs) + self.config.deploy_cert = mocked_deploy_cert + return self.config + def tearDown(self): shutil.rmtree(self.temp_dir) shutil.rmtree(self.config_dir) @@ -245,6 +253,7 @@ class TwoVhost80Test(util.ApacheTest): # Get the default 443 vhost self.config.assoc["random.demo"] = self.vh_truth[1] + self.config = self.mock_deploy_cert(self.config) self.config.deploy_cert( "random.demo", "example/cert.pem", "example/key.pem", "example/cert_chain.pem", "example/fullchain.pem") @@ -271,6 +280,7 @@ class TwoVhost80Test(util.ApacheTest): def test_deploy_cert_newssl_no_fullchain(self): self.config = util.get_apache_configurator( self.config_path, self.config_dir, self.work_dir, version=(2, 4, 16)) + self.config = self.mock_deploy_cert(self.config) self.config.parser.modules.add("ssl_module") self.config.parser.modules.add("mod_ssl.c") @@ -284,6 +294,7 @@ class TwoVhost80Test(util.ApacheTest): def test_deploy_cert_old_apache_no_chain(self): self.config = util.get_apache_configurator( self.config_path, self.config_dir, self.work_dir, version=(2, 4, 7)) + self.config = self.mock_deploy_cert(self.config) self.config.parser.modules.add("ssl_module") self.config.parser.modules.add("mod_ssl.c") diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py index bc1f316f9..121c2ceb2 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py @@ -150,9 +150,9 @@ class BasicParserTest(util.ParserTest): @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") def test_update_runtime_vars_bad_output(self, mock_cfg): - mock_cfg.return_value = "Define: TLS=443=24" - self.assertRaises( - errors.PluginError, self.parser.update_runtime_variables, "ctl") + #mock_cfg.return_value = "Define: TLS=443=24" + #self.assertRaises( + # errors.PluginError, self.parser.update_runtime_variables, "ctl") mock_cfg.return_value = "Define: DUMP_RUN_CFG\nDefine: TLS=443=24" self.assertRaises( diff --git a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py b/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py index f4dff7734..6f10555f8 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py @@ -78,7 +78,8 @@ class TlsSniPerformTest(util.ApacheTest): # pylint: disable=protected-access self.sni._setup_challenge_cert = mock_setup_cert - sni_responses = self.sni.perform() + with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod") as mock_enable: + sni_responses = self.sni.perform() self.assertEqual(mock_setup_cert.call_count, 2) From d761df90d4313a3a161ab0868a63fcc1152d8020 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Wed, 9 Dec 2015 18:51:16 -0800 Subject: [PATCH 045/159] added coverage tests --- .../letsencrypt_apache/parser.py | 3 ++- .../letsencrypt_apache/tests/parser_test.py | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 8f15ab10c..418e0ec39 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -59,7 +59,8 @@ class ApacheParser(object): # Must also attempt to parse sites-available or equivalent # Sites-available is not included naturally in configuration self._parse_file(os.path.join(self.root, "sites-available") + "/*") - #TODO check to see if there were unparsed define statements + + #check to see if there were unparsed define statements if self.unparsable: if self.find_dir("Define", exclude=False): raise errors.PluginError("Error parsing runtime variables") diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py index 121c2ceb2..57a75bcec 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py @@ -150,9 +150,9 @@ class BasicParserTest(util.ParserTest): @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") def test_update_runtime_vars_bad_output(self, mock_cfg): - #mock_cfg.return_value = "Define: TLS=443=24" - #self.assertRaises( - # errors.PluginError, self.parser.update_runtime_variables, "ctl") + mock_cfg.return_value = "Define: TLS=443=24" + self.parser.update_runtime_variables("ctl") + self.assertTrue( self.parser.unparsable) mock_cfg.return_value = "Define: DUMP_RUN_CFG\nDefine: TLS=443=24" self.assertRaises( @@ -185,6 +185,19 @@ class ParserInitTest(util.ApacheTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) + @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") + def test_unparsable(self, mock_cfg): + from letsencrypt_apache.parser import ApacheParser + def unparsable_true(self, arg): + self.unparsable = True + with mock.patch.object(ApacheParser, 'update_runtime_variables', autospec=True) as urv: + urv.side_effect = unparsable_true + mock_cfg.return_value = ('Define: TEST') + self.assertRaises( + errors.PluginError, + ApacheParser, self.aug, os.path.relpath(self.config_path), "ctl") + self.assertEquals(1,1) + def test_root_normalized(self): from letsencrypt_apache.parser import ApacheParser From 9ea3dc313697f52889a6bf3d89edefb9132a618c Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 30 Nov 2015 22:12:02 -0800 Subject: [PATCH 046/159] Hackishly add wheezy backports libaugeas0 where required --- bootstrap/_deb_common.sh | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index 4c6b91a33..cd9036581 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -32,6 +32,26 @@ if apt-cache show python-virtualenv > /dev/null ; then virtualenv="$virtualenv python-virtualenv" fi +augeas_pkg=libaugeas0 +AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + +if dpkg --compare-version 1.0 gt "$AUGVERSION" ; then + if lsb_release -a | grep -q wheezy ; then + if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q wheezy-backports ; then + # XXX ask for permission before doing this? + echo Installing augeas from wheezy-backports... + echo deb http://http.debian.net/debian wheezy-backports main >> /etc/apt/sources.list + apt-get update + apt-get install -y --no-install-recommends -t wheezy-backports libaugeas0 + fi + augeas_pkg= + else + echo "No libaugeas0 version is available that's new enough to run the" + echo "Let's Encrypt apache plugin..." + fi + # XXX add a case for ubuntu PPAs +fi + apt-get install -y --no-install-recommends \ git \ python \ @@ -39,11 +59,13 @@ apt-get install -y --no-install-recommends \ $virtualenv \ gcc \ dialog \ - libaugeas0 \ + $augeas_pkg \ libssl-dev \ libffi-dev \ ca-certificates \ + + if ! command -v virtualenv > /dev/null ; then echo Failed to install a working \"virtualenv\" command, exiting exit 1 From 822e2025ac6d29d1dd9527e7a6b6f65a3146923b Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Thu, 10 Dec 2015 06:19:32 -0800 Subject: [PATCH 047/159] fixed apache2 script, tested on centos7 --- multitester.py | 4 ++-- scripts/test_apache2.sh | 28 +++++++++------------- targets.yaml | 52 +++++++++++------------------------------ 3 files changed, 26 insertions(+), 58 deletions(-) diff --git a/multitester.py b/multitester.py index 37e6a479c..aaba1ed07 100644 --- a/multitester.py +++ b/multitester.py @@ -123,8 +123,8 @@ def make_instance(instance_name, UserData=userdata, InstanceType=machine_type)[0] - # brief pause to prevent rare EC2 error - time.sleep(0.5) + # brief pause to prevent rare error on EC2 delay, should block until ready instead + time.sleep(1.0) # give instance a name new_instance.create_tags(Tags=[{'Key': 'Name', 'Value': instance_name}]) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index fe8b094fa..772300589 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -1,15 +1,7 @@ #!/bin/bash -x -#install apache2 on apt systems -# debian doesn't come with curl -#sudo apt-get update -#sudo apt-get -y --no-upgrade install apache2 #curl - -# $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL are dynamically set at execution -# fetch instance data from EC2 metadata service -#public_host=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-hostname) -#public_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/public-ipv4) -#private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) +# $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL +# are dynamically set at execution if [ $OS_TYPE = "ubuntu" ] then @@ -22,22 +14,24 @@ then elif [ $OS_TYPE = "centos" ] then CONFFILE=/etc/httpd/conf/httpd.conf + sudo setenforce 0 || true #disable selinux sudo yum -y install httpd sudo service httpd start sudo mkdir -p /var/www/$PUBLIC_HOSTNAME/public_html - sudo chmod -R 777 /var/www - sudo echo 'foo\nbar' > /var/www/$PUBLIC_HOSTNAME/public_html/index.html - sudo mkdir /etc/httpd/sites-available - sudo mkdir /etc/httpd/sites-enabled - sudo echo "IncludeOptional sites-enabled/*.conf" >> /etc/httpd/conf/httpd.conf + sudo chmod -R oug+rwx /var/www + sudo chmod -R oug+rw /etc/httpd + sudo echo 'foobar' > /var/www/$PUBLIC_HOSTNAME/public_html/index.html + sudo mkdir /etc/httpd/sites-available #letsencrypt requires this... + sudo mkdir /etc/httpd/sites-enabled #letsencrypt requires this... + #sudo echo "IncludeOptional sites-enabled/*.conf" >> /etc/httpd/conf/httpd.conf sudo echo """ ServerName $PUBLIC_HOSTNAME DocumentRoot /var/www/$PUBLIC_HOSTNAME/public_html ErrorLog /var/www/$PUBLIC_HOSTNAME/error.log CustomLog /var/www/$PUBLIC_HOSTNAME/requests.log combined -""" >> /etc/httpd/sites-available/$PUBLIC_HOSTNAME.conf - sudo cp /etc/httpd/sites-available/$PUBLIC_HOSTNAME.conf /etc/httpd/sites-enabled/ +""" >> /etc/httpd/conf.d/$PUBLIC_HOSTNAME.conf + #sudo cp /etc/httpd/sites-available/$PUBLIC_HOSTNAME.conf /etc/httpd/sites-enabled/ fi # run letsencrypt-apache2 via letsencrypt-auto diff --git a/targets.yaml b/targets.yaml index 384f82df6..506225f86 100644 --- a/targets.yaml +++ b/targets.yaml @@ -33,28 +33,28 @@ targets: type: ubuntu virt: hvm user: admin - userdata: | - #cloud-init - runcmd: - - [ apt-get, install, -y, curl ] + # userdata: | + # #cloud-init + # runcmd: + # - [ apt-get, install, -y, curl ] - ami: ami-e0efab88 name: debian7.8.aws.1 type: ubuntu virt: hvm user: admin - userdata: | - #cloud-init - runcmd: - - [ apt-get, install, -y, curl ] + # userdata: | + # #cloud-init + # runcmd: + # - [ apt-get, install, -y, curl ] - ami: ami-e6eeaa8e name: debian7.8.aws.1_32bit type: ubuntu virt: pv user: admin - userdata: | - cloud-init - runcmd: - - [ apt-get, install, -y, curl ] + # userdata: | + # #cloud-init + # runcmd: + # - [ apt-get, install, -y, curl ] #----------------------------------------------------------------------------- # Other Redhat Distros - ami: ami-60b6c60a @@ -62,41 +62,21 @@ targets: type: centos virt: hvm user: ec2-user - userdata: | - #cloud-config - runcmd: - - yum -y install httpd - - service httpd start - ami: ami-0d4cfd66 name: amazonlinux-2015.03.1 type: centos virt: hvm user: ec2-user - userdata: | - #cloud-config - runcmd: - - yum -y install httpd - - service httpd start - ami: ami-a8d369c0 name: RHEL7 type: centos virt: hvm user: ec2-user - userdata: | - #cloud-config - runcmd: - - yum -y install httpd - - service httpd start - ami: ami-518bfb3b name: fedora23 type: centos virt: hvm user: fedora - userdata: | - #cloud-config - runcmd: - - yum -y install httpd - - service httpd start #----------------------------------------------------------------------------- # CentOS # These Marketplace AMIs must, irritatingly, have their terms manually @@ -106,11 +86,6 @@ targets: type: centos virt: hvm user: centos - userdata: | - #cloud-config - runcmd: - - yum -y install httpd - - service httpd start # centos6 requires EPEL repo added - ami: ami-57cd8732 name: centos6 @@ -120,6 +95,5 @@ targets: userdata: | #cloud-config runcmd: - - yum install -y epel-release httpd - - service httpd start + - yum install -y epel-release - iptables -F From ba400771192321c45abe55bb7451a0d366192b1a Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Thu, 10 Dec 2015 06:40:20 -0800 Subject: [PATCH 048/159] readme update --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a5d365c8f..0d8506a3f 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ then: >python multitester.py targets.yaml MyKeyPair.pem HappyHacker scripts/test_letsencrypt_auto_venv_only.sh ``` +example scripts are in the 'scripts' directory, these are just bash scripts that have a few parameters passed +to them at runtime via environment variables. test_apache2.sh is a useful reference. + see: - https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html - https://docs.aws.amazon.com/cli/latest/userguide/cli-ec2-keypairs.html From a944603f430f0857f9423d95f712e8e68437488d Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Thu, 10 Dec 2015 06:44:07 -0800 Subject: [PATCH 049/159] readme fix --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d8506a3f..a085e9d91 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,16 @@ simple aws testfarm scripts for letsencrypt client testing ``` then: ``` ->python multitester.py targets.yaml MyKeyPair.pem HappyHacker scripts/test_letsencrypt_auto_venv_only.sh +>python multitester.py targets.yaml MyKeyPair.pem HappyHacker scripts/test_apache2.sh ``` +## Scripts example scripts are in the 'scripts' directory, these are just bash scripts that have a few parameters passed to them at runtime via environment variables. test_apache2.sh is a useful reference. +Note that the
test_letsencrypt_auto_*
scripts pull code from PyPI using the letsencrypt-auto script, +__not__ the local python code. test_apache2 runs the dev venv and does local tests. + see: - https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html - https://docs.aws.amazon.com/cli/latest/userguide/cli-ec2-keypairs.html From 54127eef5a6286959fa9fe424a776f1faa5b61fb Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Thu, 10 Dec 2015 18:12:07 +0200 Subject: [PATCH 050/159] Don't call site enable methods when handle-sites is false for the operating system --- letsencrypt-apache/letsencrypt_apache/configurator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index be9825304..3e6881739 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -471,7 +471,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): is_ssl = True filename = get_file_path(path) - is_enabled = self.is_site_enabled(filename) + if self.conf("handle-sites"): + is_enabled = self.is_site_enabled(filename) + else: + is_enabled = True macro = False if "/macro/" in path.lower(): From 509c192ab8e3515e3733a91769e2ec42b2b7acc0 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Thu, 10 Dec 2015 18:17:12 +0200 Subject: [PATCH 051/159] Add RedHat Enterprise defaults to constants --- letsencrypt-apache/letsencrypt_apache/constants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 87c3e6e66..049ddce4d 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -30,7 +30,8 @@ CLI_DEFAULTS = { "ubuntu": CLI_DEFAULTS_DEBIAN, "centos": CLI_DEFAULTS_CENTOS, "centos linux": CLI_DEFAULTS_CENTOS, - "fedora": CLI_DEFAULTS_CENTOS + "fedora": CLI_DEFAULTS_CENTOS, + "red hat enterprise linux server": CLI_DEFAULTS_CENTOS } """CLI defaults.""" From 7bf8842ba7107a86318c7a954e7ed00b3fc904f5 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 11 Dec 2015 00:03:00 +0200 Subject: [PATCH 052/159] Added missing test for full coverage --- .../tests/configurator_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index b93034cd9..e16dff173 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -116,6 +116,24 @@ class TwoVhost80Test(util.ApacheTest): self.assertEqual(found, 6) + # Handle case of non-debian layout get_virtual_hosts + orig_conf = self.config.conf + with mock.patch( + "letsencrypt_apache.configurator.ApacheConfigurator.conf" + ) as mock_conf: + def conf_sideeffect(key): + """Handle calls to configurator.conf() + :param key: configuration key + :return: configuration value + """ + if key == "handle-sites": + return False + else: + return orig_conf(key) + mock_conf.side_effect = conf_sideeffect + vhs = self.config.get_virtual_hosts() + self.assertEqual(len(vhs), 6) + @mock.patch("letsencrypt_apache.display_ops.select_vhost") def test_choose_vhost_none_avail(self, mock_select): mock_select.return_value = None From 48b1240451b3dafd9228baa400633317df784654 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 11 Dec 2015 09:18:00 +0200 Subject: [PATCH 053/159] Removed redundant config parsing directive. --- letsencrypt-apache/letsencrypt_apache/parser.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 4ab2b82a0..0289c57d8 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -58,9 +58,6 @@ class ApacheParser(object): # Set up rest of locations self.loc.update(self._set_locations()) - # Take the CentOS layout into account, httpd.conf not in httpd root - self._parse_file(os.path.join(self.root, "conf") + "/httpd.conf") - # Must also attempt to parse virtual host root self._parse_file(self.vhostroot + "/*.conf") From 4d31a8cb390778a35207bb0133b21a46c91de2dc Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 11 Dec 2015 12:41:21 +0200 Subject: [PATCH 054/159] Sane error message in case user tries to use apache-handle-sites functionality in non-supported environment. --- letsencrypt-apache/letsencrypt_apache/configurator.py | 6 ++++++ .../letsencrypt_apache/tests/configurator_test.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 3e6881739..d67e7bc18 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -1119,6 +1119,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ enabled_dir = os.path.join(self.parser.root, "sites-enabled") + if not os.path.isdir(enabled_dir): + error_msg = ("Directory '{0}' does not exist. Please ensure " + "that the values for --apache-handle-sites and " + "--apache-server-root are correct for your " + "environment.".format(enabled_dir)) + raise errors.ConfigurationError(error_msg) for entry in os.listdir(enabled_dir): try: if filecmp.cmp(avail_fp, os.path.join(enabled_dir, entry)): diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index e16dff173..064111d4a 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -219,6 +219,10 @@ class TwoVhost80Test(util.ApacheTest): self.assertFalse(self.config.is_site_enabled(self.vh_truth[1].filep)) self.assertTrue(self.config.is_site_enabled(self.vh_truth[2].filep)) self.assertTrue(self.config.is_site_enabled(self.vh_truth[3].filep)) + with mock.patch("os.path.isdir") as mock_isdir: + mock_isdir.return_value = False + with (self.assertRaises(errors.ConfigurationError)): + self.config.is_site_enabled("irrelevant") @mock.patch("letsencrypt.le_util.run_script") @mock.patch("letsencrypt.le_util.exe_exists") From 0805b08162ca777062b26c136bebd2e48cea322e Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 11 Dec 2015 13:17:21 +0200 Subject: [PATCH 055/159] Make python 2.6 happy with the assertRaises --- .../letsencrypt_apache/tests/configurator_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index 064111d4a..9ff6456be 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -221,8 +221,9 @@ class TwoVhost80Test(util.ApacheTest): self.assertTrue(self.config.is_site_enabled(self.vh_truth[3].filep)) with mock.patch("os.path.isdir") as mock_isdir: mock_isdir.return_value = False - with (self.assertRaises(errors.ConfigurationError)): - self.config.is_site_enabled("irrelevant") + self.assertRaises(errors.ConfigurationError, + self.config.is_site_enabled, + "irrelevant") @mock.patch("letsencrypt.le_util.run_script") @mock.patch("letsencrypt.le_util.exe_exists") From 77c67f1fa9ab7a6e3fd8519b252f6abb9d54e89c Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 14 Dec 2015 09:27:16 +0200 Subject: [PATCH 056/159] Gentoo constants, still missing gentoo fingerprint though --- letsencrypt-apache/letsencrypt_apache/constants.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 049ddce4d..989106e39 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -25,6 +25,17 @@ CLI_DEFAULTS_CENTOS = dict( handle_sites=False, challenge_location="/etc/httpd/conf.d" ) +CLI_DEFAULTS_GENTOO = dict( + server_root="/etc/apache2", + vhost_root="/etc/apache2/vhosts.d", + ctl="apache2ctl", + enmod=None, + dismod=None, + le_vhost_ext="-le-ssl.conf", + handle_mods=False, + handle_sites=False, + challenge_location="/etc/apache2/vhosts.d" +) CLI_DEFAULTS = { "debian": CLI_DEFAULTS_DEBIAN, "ubuntu": CLI_DEFAULTS_DEBIAN, From 9beb855618b03ade596c2d0abb42815f21990e75 Mon Sep 17 00:00:00 2001 From: Antoine Jacoutot Date: Mon, 14 Dec 2015 13:57:52 +0100 Subject: [PATCH 057/159] Mention that OpenBSD has a native letsencrypt package now. --- docs/using.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 687901191..80d429773 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -59,8 +59,8 @@ or for full help, type: ``letsencrypt-auto`` is the recommended method of running the Let's Encrypt client beta releases on systems that don't have a packaged version. Debian -experimental, Arch linux and FreeBSD now have native packages, so on those -systems you can just install ``letsencrypt`` (and perhaps +experimental, Arch linux, FreeBSD and OpenBSD now have native packages, so on +those systems you can just install ``letsencrypt`` (and perhaps ``letsencrypt-apache``). If you'd like to run the latest copy from Git, or run your own locally modified copy of the client, follow the instructions in the :doc:`contributing`. Some `other methods of installation`_ are discussed @@ -346,6 +346,11 @@ Operating System Packages * Port: ``cd /usr/ports/security/py-letsencrypt && make install clean`` * Package: ``pkg install py27-letsencrypt`` +**OpenBSD** + + * Port: ``cd /usr/ports/security/letsencrypt/client && make install clean`` + * Package: ``pkg_add letsencrypt`` + **Arch Linux** .. code-block:: shell From 3d20950fb89143e40b3fe1b22bf9a44840354725 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Tue, 15 Dec 2015 10:59:13 -0800 Subject: [PATCH 058/159] modified to allow pip variable --- multitester.py | 4 ++++ scripts/test_apache2.sh | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/multitester.py b/multitester.py index aaba1ed07..ab8a9b29e 100644 --- a/multitester.py +++ b/multitester.py @@ -74,6 +74,9 @@ parser.add_argument('--merge_master', parser.add_argument('--saveinstances', action='store_true', help="don't kill EC2 instances after run, useful for debugging") +parser.add_argument('--alt_pip', + default='https://certainly.isnot.org', + help="server from which to pull candidate release packages") cl_args = parser.parse_args() # Credential Variables @@ -277,6 +280,7 @@ def install_and_launch_letsencrypt(instance, boulder_url, target): PUBLIC_IP=instance.public_ip_address, PRIVATE_IP=instance.private_ip_address, PUBLIC_HOSTNAME=instance.public_dns_name, + PIP_EXTRA_INDEX_URL=cl_args.alt_pip, OS_TYPE=target['type']): execute(deploy_script, cl_args.test_script) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index 772300589..803385e43 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -36,9 +36,6 @@ fi # run letsencrypt-apache2 via letsencrypt-auto cd letsencrypt -./bootstrap/install-deps.sh -./bootstrap/dev/venv.sh -source ./venv/bin/activate -sudo ./venv/bin/letsencrypt -v --debug --text --agree-dev-preview --agree-tos \ +letsencrypt-auto -v --debug --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL From 22ca6a070a76e10f53fd411c68d285bc63fc45e7 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Tue, 15 Dec 2015 11:52:17 -0800 Subject: [PATCH 059/159] added directory notation --- scripts/test_apache2.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index 803385e43..65f514d65 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -36,6 +36,6 @@ fi # run letsencrypt-apache2 via letsencrypt-auto cd letsencrypt -letsencrypt-auto -v --debug --text --agree-dev-preview --agree-tos \ +./letsencrypt-auto -v --debug --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL From 535782c6d5c010d90fc1738fa513471d9e310210 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Tue, 15 Dec 2015 12:15:17 -0800 Subject: [PATCH 060/159] fix default url --- multitester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multitester.py b/multitester.py index ab8a9b29e..fb29ab9eb 100644 --- a/multitester.py +++ b/multitester.py @@ -75,7 +75,7 @@ parser.add_argument('--saveinstances', action='store_true', help="don't kill EC2 instances after run, useful for debugging") parser.add_argument('--alt_pip', - default='https://certainly.isnot.org', + default='https://certainly.isnot.org/pip/', help="server from which to pull candidate release packages") cl_args = parser.parse_args() From cad254926e98bc27c607f989161de7019396e38a Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 15 Dec 2015 12:47:01 -0800 Subject: [PATCH 061/159] A letsencrypt-auto upgrade test! - should be run with --branch v0.1.0 --- scripts/test_leauto_upgrades.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100755 scripts/test_leauto_upgrades.sh diff --git a/scripts/test_leauto_upgrades.sh b/scripts/test_leauto_upgrades.sh new file mode 100755 index 000000000..70f8a2293 --- /dev/null +++ b/scripts/test_leauto_upgrades.sh @@ -0,0 +1,18 @@ +#!/bin/bash -xe + +# $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL +# are dynamically set at execution + +cd letsencrypt +#git checkout v0.1.0 use --branch instead +SAVE="$PIP_EXTRA_INDEX_URL" +unset PIP_EXTRA_INDEX_URL +./letsencrypt-auto -v --debug --version + +export PIP_EXTRA_INDEX_URL="$SAVE" + +if ! ./letsencrypt-auto -v --debug --version | grep 0.1.1 ; then + echo upgrade appeared to fail + exit 1 +fi +echo upgrade appeared to be successful From 38821f244b3ce434c1c02a18ba6b8ac7a17af245 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 15 Dec 2015 17:06:58 -0800 Subject: [PATCH 062/159] Remove git as dependency --- bootstrap/_arch_common.sh | 1 - bootstrap/_deb_common.sh | 1 - bootstrap/_gentoo_common.sh | 3 +-- bootstrap/_rpm_common.sh | 2 -- bootstrap/_suse_common.sh | 3 +-- bootstrap/freebsd.sh | 1 - 6 files changed, 2 insertions(+), 9 deletions(-) diff --git a/bootstrap/_arch_common.sh b/bootstrap/_arch_common.sh index f66067ffb..2b512792f 100755 --- a/bootstrap/_arch_common.sh +++ b/bootstrap/_arch_common.sh @@ -8,7 +8,6 @@ # ./bootstrap/dev/_common_venv.sh deps=" - git python2 python-virtualenv gcc diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index 4c6b91a33..d8b03075c 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -33,7 +33,6 @@ if apt-cache show python-virtualenv > /dev/null ; then fi apt-get install -y --no-install-recommends \ - git \ python \ python-dev \ $virtualenv \ diff --git a/bootstrap/_gentoo_common.sh b/bootstrap/_gentoo_common.sh index a718db7ff..a9bc6acd7 100755 --- a/bootstrap/_gentoo_common.sh +++ b/bootstrap/_gentoo_common.sh @@ -1,7 +1,6 @@ #!/bin/sh -PACKAGES="dev-vcs/git - dev-lang/python:2.7 +PACKAGES="dev-lang/python:2.7 dev-python/virtualenv dev-util/dialog app-admin/augeas diff --git a/bootstrap/_rpm_common.sh b/bootstrap/_rpm_common.sh index b975da444..6edea8eb1 100755 --- a/bootstrap/_rpm_common.sh +++ b/bootstrap/_rpm_common.sh @@ -33,9 +33,7 @@ then fi fi -# "git-core" seems to be an alias for "git" in CentOS 7 (yum search fails) if ! $tool install -y \ - git-core \ gcc \ dialog \ augeas-libs \ diff --git a/bootstrap/_suse_common.sh b/bootstrap/_suse_common.sh index 46f9d693b..701849e4b 100755 --- a/bootstrap/_suse_common.sh +++ b/bootstrap/_suse_common.sh @@ -2,8 +2,7 @@ # SLE12 don't have python-virtualenv -zypper -nq in -l git-core \ - python \ +zypper -nq in -l python \ python-devel \ python-virtualenv \ gcc \ diff --git a/bootstrap/freebsd.sh b/bootstrap/freebsd.sh index 180ee21b4..4482c35cd 100755 --- a/bootstrap/freebsd.sh +++ b/bootstrap/freebsd.sh @@ -1,7 +1,6 @@ #!/bin/sh -xe pkg install -Ay \ - git \ python \ py27-virtualenv \ augeas \ From 69ea4662c3d167efcc9cf1da93ef46b5092e5c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20HUBSCHER?= Date: Wed, 16 Dec 2015 15:25:31 +0100 Subject: [PATCH 063/159] Guarantee a true SSLContext object with Python 2 --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index 40c6ac16c..e94891802 100644 --- a/setup.py +++ b/setup.py @@ -55,6 +55,10 @@ if sys.version_info < (2, 7): 'argparse', 'mock<1.1.0', ]) +elif sys.version_info < (2, 8): + # For secure SSL connexion with Python 2.7 (InsecurePlatformWarning) + install_requires.append('ndg-httpsclient') + install_requires.append('pyasn1') else: install_requires.append('mock') From eca5e7ae27928a8f2232ca9efb99e095120ab01b Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 16 Dec 2015 12:45:15 -0800 Subject: [PATCH 064/159] Put every package on its own line --- bootstrap/_gentoo_common.sh | 3 ++- bootstrap/_suse_common.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bootstrap/_gentoo_common.sh b/bootstrap/_gentoo_common.sh index a9bc6acd7..f49dc00f0 100755 --- a/bootstrap/_gentoo_common.sh +++ b/bootstrap/_gentoo_common.sh @@ -1,6 +1,7 @@ #!/bin/sh -PACKAGES="dev-lang/python:2.7 +PACKAGES=" + dev-lang/python:2.7 dev-python/virtualenv dev-util/dialog app-admin/augeas diff --git a/bootstrap/_suse_common.sh b/bootstrap/_suse_common.sh index 701849e4b..efeebe4f8 100755 --- a/bootstrap/_suse_common.sh +++ b/bootstrap/_suse_common.sh @@ -2,7 +2,8 @@ # SLE12 don't have python-virtualenv -zypper -nq in -l python \ +zypper -nq in -l \ + python \ python-devel \ python-virtualenv \ gcc \ From e7226d28041a03a8f2d6d61ac84e765e8235a180 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 16:43:24 -0800 Subject: [PATCH 065/159] Automate testing with the apache-conf-library --- tests/apache-conf-files/hackish-apache-test | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/apache-conf-files/hackish-apache-test b/tests/apache-conf-files/hackish-apache-test index c6663551e..8efe65e42 100755 --- a/tests/apache-conf-files/hackish-apache-test +++ b/tests/apache-conf-files/hackish-apache-test @@ -18,11 +18,25 @@ function CleanupExit() { exit 1 } +FAILS=0 trap CleanupExit INT for f in *.conf ; do - echo testing "$f" + echo -n testing "$f"... sudo cp "$f" "$EA"/sites-available/ sudo ln -s "$EA/sites-available/$f" "$EA/sites-enabled/$f" - sudo "$LEROOT"/venv/bin/letsencrypt --apache certonly -t + RESULT=`echo c | sudo "$LEROOT"/venv/bin/letsencrypt --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` + if echo $RESULT | grep -Eq \("Please specify --domains"\|"mod_macro is not yet"\) ; then + echo passed + else + echo failed + echo $RESULT + echo + echo + FAILS=`expr $FAILS + 1` + fi sudo rm /etc/apache2/sites-{enabled,available}/"$f" done +if [ "$FAILS" -ne 0 ] ; then + return 1 +fi +return 0 From 52705107ff76f1e713b9b903837e7c38c230f411 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 17:01:36 -0800 Subject: [PATCH 066/159] let the environment determine how letsencrypt is run --- tests/apache-conf-files/hackish-apache-test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/apache-conf-files/hackish-apache-test b/tests/apache-conf-files/hackish-apache-test index 8efe65e42..e6f25b15f 100755 --- a/tests/apache-conf-files/hackish-apache-test +++ b/tests/apache-conf-files/hackish-apache-test @@ -9,6 +9,7 @@ export EA=/etc/apache2/ TESTDIR="`dirname $0`" LEROOT="`realpath \"$TESTDIR/../../\"`" cd $TESTDIR/passing +LETSENCRYPT="${LETSENCRYPT:-$LEROOT/venv/bin/letsencrypt}" function CleanupExit() { echo control c, exiting tests... @@ -24,7 +25,7 @@ for f in *.conf ; do echo -n testing "$f"... sudo cp "$f" "$EA"/sites-available/ sudo ln -s "$EA/sites-available/$f" "$EA/sites-enabled/$f" - RESULT=`echo c | sudo "$LEROOT"/venv/bin/letsencrypt --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` + RESULT=`echo c | sudo "$LETSENCRYPT" --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Please specify --domains"\|"mod_macro is not yet"\) ; then echo passed else From 3c6af7094c67b37e8b815309fe0a492e232fccbf Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 17:21:47 -0800 Subject: [PATCH 067/159] Bugfix, and use --staging --- tests/apache-conf-files/hackish-apache-test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/apache-conf-files/hackish-apache-test b/tests/apache-conf-files/hackish-apache-test index e6f25b15f..c4df319ed 100755 --- a/tests/apache-conf-files/hackish-apache-test +++ b/tests/apache-conf-files/hackish-apache-test @@ -25,7 +25,7 @@ for f in *.conf ; do echo -n testing "$f"... sudo cp "$f" "$EA"/sites-available/ sudo ln -s "$EA/sites-available/$f" "$EA/sites-enabled/$f" - RESULT=`echo c | sudo "$LETSENCRYPT" --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` + RESULT=`echo c | sudo "$LETSENCRYPT" --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Please specify --domains"\|"mod_macro is not yet"\) ; then echo passed else @@ -38,6 +38,6 @@ for f in *.conf ; do sudo rm /etc/apache2/sites-{enabled,available}/"$f" done if [ "$FAILS" -ne 0 ] ; then - return 1 + exit 1 fi -return 0 +exit 0 From bf764e4852dcf061e586789455afcb9785f39e3f Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 18:01:49 -0800 Subject: [PATCH 068/159] Support appending to non-Debianish Apache setups --- tests/apache-conf-files/hackish-apache-test | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/apache-conf-files/hackish-apache-test b/tests/apache-conf-files/hackish-apache-test index c4df319ed..99fa123f7 100755 --- a/tests/apache-conf-files/hackish-apache-test +++ b/tests/apache-conf-files/hackish-apache-test @@ -23,8 +23,14 @@ FAILS=0 trap CleanupExit INT for f in *.conf ; do echo -n testing "$f"... - sudo cp "$f" "$EA"/sites-available/ - sudo ln -s "$EA/sites-available/$f" "$EA/sites-enabled/$f" + if [ "$APPEND_APACHECONF" = "" ] ; then + sudo cp "$f" "$EA"/sites-available/ + sudo ln -sf "$EA/sites-available/$f" "$EA/sites-enabled/$f" + else + TMP="/tmp/`basename \"$APPEND_APACHECONF\"`.$$" + sudo cp -a "$APPEND_APACHECONF" "$TMP" + sudo bash -c "cat \"$f\" >> \"$APPEND_APACHECONF\"" + fi RESULT=`echo c | sudo "$LETSENCRYPT" --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Please specify --domains"\|"mod_macro is not yet"\) ; then echo passed @@ -35,7 +41,11 @@ for f in *.conf ; do echo FAILS=`expr $FAILS + 1` fi - sudo rm /etc/apache2/sites-{enabled,available}/"$f" + if [ "$APPEND_APACHECONF" = "" ] ; then + sudo rm /etc/apache2/sites-{enabled,available}/"$f" + else + sudo mv "$TMP" "$APPEND_APACHECONF" + fi done if [ "$FAILS" -ne 0 ] ; then exit 1 From 15386fd0decc5dfddbc57efab1b376d1d0b7fce7 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Wed, 16 Dec 2015 18:55:39 -0800 Subject: [PATCH 069/159] fix issue with parsing renewal confs --- letsencrypt/renewer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/renewer.py b/letsencrypt/renewer.py index 0a490d447..8f7f38c90 100644 --- a/letsencrypt/renewer.py +++ b/letsencrypt/renewer.py @@ -179,7 +179,9 @@ def main(cli_args=sys.argv[1:]): # RenewableCert object for this cert at all, which could # dramatically improve performance for large deployments # where autorenewal is widely turned off. - cert = storage.RenewableCert(renewal_file, cli_config) + cert = storage.RenewableCert( + os.path.join(cli_config.renewal_configs_dir, renewal_file), + cli_config) except errors.CertStorageError: # This indicates an invalid renewal configuration file, such # as one missing a required parameter (in the future, perhaps From 9013fecc9cea8bfca8e29789520772c3bd96cd51 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 19:41:35 -0800 Subject: [PATCH 070/159] Prep for testfarming. --- tests/apache-conf-files/hackish-apache-test | 16 ++++++++++------ tests/apache-conf-files/passing/README.modules | 3 +-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/apache-conf-files/hackish-apache-test b/tests/apache-conf-files/hackish-apache-test index 99fa123f7..3d4336579 100755 --- a/tests/apache-conf-files/hackish-apache-test +++ b/tests/apache-conf-files/hackish-apache-test @@ -14,11 +14,19 @@ LETSENCRYPT="${LETSENCRYPT:-$LEROOT/venv/bin/letsencrypt}" function CleanupExit() { echo control c, exiting tests... if [ "$f" != "" ] ; then - sudo rm /etc/apache2/sites-{enabled,available}/"$f" + Cleanup fi exit 1 } +function Cleanup() { + if [ "$APPEND_APACHECONF" = "" ] ; then + sudo rm /etc/apache2/sites-{enabled,available}/"$f" + else + sudo mv "$TMP" "$APPEND_APACHECONF" + fi +} + FAILS=0 trap CleanupExit INT for f in *.conf ; do @@ -41,11 +49,7 @@ for f in *.conf ; do echo FAILS=`expr $FAILS + 1` fi - if [ "$APPEND_APACHECONF" = "" ] ; then - sudo rm /etc/apache2/sites-{enabled,available}/"$f" - else - sudo mv "$TMP" "$APPEND_APACHECONF" - fi + Cleanup done if [ "$FAILS" -ne 0 ] ; then exit 1 diff --git a/tests/apache-conf-files/passing/README.modules b/tests/apache-conf-files/passing/README.modules index 7edbd3e84..32c3ef019 100644 --- a/tests/apache-conf-files/passing/README.modules +++ b/tests/apache-conf-files/passing/README.modules @@ -1,5 +1,4 @@ -Modules required to parse these conf files: - +# Modules required to parse these conf files: ssl rewrite macro From babb33991bffb51fd82acc75afa87ce4774168c4 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 19:51:45 -0800 Subject: [PATCH 071/159] Neaten things with a Setup() function --- tests/apache-conf-files/hackish-apache-test | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/apache-conf-files/hackish-apache-test b/tests/apache-conf-files/hackish-apache-test index 3d4336579..b8caaadc0 100755 --- a/tests/apache-conf-files/hackish-apache-test +++ b/tests/apache-conf-files/hackish-apache-test @@ -19,6 +19,17 @@ function CleanupExit() { exit 1 } +function Setup() { + if [ "$APPEND_APACHECONF" = "" ] ; then + sudo cp "$f" "$EA"/sites-available/ + sudo ln -sf "$EA/sites-available/$f" "$EA/sites-enabled/$f" + else + TMP="/tmp/`basename \"$APPEND_APACHECONF\"`.$$" + sudo cp -a "$APPEND_APACHECONF" "$TMP" + sudo bash -c "cat \"$f\" >> \"$APPEND_APACHECONF\"" + fi +} + function Cleanup() { if [ "$APPEND_APACHECONF" = "" ] ; then sudo rm /etc/apache2/sites-{enabled,available}/"$f" @@ -31,14 +42,7 @@ FAILS=0 trap CleanupExit INT for f in *.conf ; do echo -n testing "$f"... - if [ "$APPEND_APACHECONF" = "" ] ; then - sudo cp "$f" "$EA"/sites-available/ - sudo ln -sf "$EA/sites-available/$f" "$EA/sites-enabled/$f" - else - TMP="/tmp/`basename \"$APPEND_APACHECONF\"`.$$" - sudo cp -a "$APPEND_APACHECONF" "$TMP" - sudo bash -c "cat \"$f\" >> \"$APPEND_APACHECONF\"" - fi + Setup RESULT=`echo c | sudo "$LETSENCRYPT" --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Please specify --domains"\|"mod_macro is not yet"\) ; then echo passed From 03572a8d8e25163af85100ec7c08d99af43b32a3 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 20:49:55 -0800 Subject: [PATCH 072/159] Add hackish-apache-tests to test_apache2.sh --- scripts/test_apache2.sh | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index 65f514d65..db29dedd3 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -3,7 +3,7 @@ # $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL # are dynamically set at execution -if [ $OS_TYPE = "ubuntu" ] +if [ "$OS_TYPE" = "ubuntu" ] then CONFFILE=/etc/apache2/sites-available/000-default.conf sudo apt-get update @@ -11,7 +11,7 @@ then # For apache 2.4, set up ServerName sudo sed -i '/ServerName/ s/#ServerName/ServerName/' $CONFFILE sudo sed -i '/ServerName/ s/www.example.com/'$PUBLIC_HOSTNAME'/' $CONFFILE -elif [ $OS_TYPE = "centos" ] +elif [ "$OS_TYPE" = "centos" ] then CONFFILE=/etc/httpd/conf/httpd.conf sudo setenforce 0 || true #disable selinux @@ -39,3 +39,25 @@ cd letsencrypt ./letsencrypt-auto -v --debug --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL +if [ $? -ne 0 ] ; then + FAIL=1 +fi + +if [ "$OS_TYPE" = "ubuntu" ] ; then + export LETSENCRYPT="$HOME/.local/share/letsencrypt/bin/letsencrypt" + for mod in `grep -v '^#' tests/apache-conf-files/passing/README.modules` ; do + sudo a2enmod $mod + done + tests/apache-conf-files/hackish-apache-test +else + echo Not running hackish apache tests on $OS_TYPE +fi + +if [ $? -ne 0 ] ; then + FAIL=1 +fi + +# return error if any of the subtests failed +if [ "$FAIL" = 1 ] ; then + return 1 +fi From 04aefcffac0ce885dcc79d5a79098018ac108ed6 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 20:53:40 -0800 Subject: [PATCH 073/159] install modules needed for tests --- scripts/test_apache2.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index db29dedd3..20dc10a71 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -45,6 +45,7 @@ fi if [ "$OS_TYPE" = "ubuntu" ] ; then export LETSENCRYPT="$HOME/.local/share/letsencrypt/bin/letsencrypt" + sudo apt-get install -y libapache2-mod-wsgi for mod in `grep -v '^#' tests/apache-conf-files/passing/README.modules` ; do sudo a2enmod $mod done From dfd666fd3d8bf56aacc5d6909cc1d0f7f2b008e2 Mon Sep 17 00:00:00 2001 From: Philippe Langlois Date: Thu, 17 Dec 2015 07:40:36 +0100 Subject: [PATCH 074/159] Root prompt explanation + minor typos --- letsencrypt-auto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt-auto b/letsencrypt-auto index 44c71883c..aec1e81de 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -47,13 +47,13 @@ if test "`id -u`" -ne "0" ; then args="" # This `while` loop iterates over all parameters given to this function. # For each parameter, all `'` will be replace by `'"'"'`, and the escaped string - # will be wrap in a pair of `'`, then append to `$args` string + # will be wrapped in a pair of `'`, then appended to `$args` string # For example, `echo "It's only 1\$\!"` will be escaped to: # 'echo' 'It'"'"'s only 1$!' # │ │└┼┘│ # │ │ │ └── `'s only 1$!'` the literal string # │ │ └── `\"'\"` is a single quote (as a string) - # │ └── `'It'`, to be concatenated with the strings followed it + # │ └── `'It'`, to be concatenated with the strings following it # └── `echo` wrapped in a pair of `'`, it's totally fine for the shell command itself while [ $# -ne 0 ]; do args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' " @@ -201,5 +201,5 @@ fi # Explain what's about to happen, for the benefit of those getting sudo # password prompts... -echo "Running with virtualenv:" $SUDO $VENV_BIN/letsencrypt "$@" +echo "Requesting root privileges to run with virtualenv:" $SUDO $VENV_BIN/letsencrypt "$@" $SUDO $VENV_BIN/letsencrypt "$@" From 6958710030909f73b367d28a2031b19986740da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20HUBSCHER?= Date: Thu, 17 Dec 2015 10:13:09 +0100 Subject: [PATCH 075/159] @pde review. --- acme/setup.py | 6 ++++-- setup.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index e35b40d6e..e75d77efd 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -10,8 +10,6 @@ install_requires = [ # load_pem_private/public_key (>=0.6) # rsa_recover_prime_factors (>=0.8) 'cryptography>=0.8', - 'ndg-httpsclient', # urllib3 InsecurePlatformWarning (#304) - 'pyasn1', # urllib3 InsecurePlatformWarning (#304) # Connection.set_tlsext_host_name (>=0.13), X509Req.get_extensions (>=0.15) 'PyOpenSSL>=0.15', 'pyrfc3339', @@ -29,6 +27,10 @@ if sys.version_info < (2, 7): 'argparse', 'mock<1.1.0', ]) +elif sys.version_info < (2, 7, 9): + # For secure SSL connexion with Python 2.7 (InsecurePlatformWarning) + install_requires.append('ndg-httpsclient') + install_requires.append('pyasn1') else: install_requires.append('mock') diff --git a/setup.py b/setup.py index e94891802..0341e400b 100644 --- a/setup.py +++ b/setup.py @@ -55,7 +55,7 @@ if sys.version_info < (2, 7): 'argparse', 'mock<1.1.0', ]) -elif sys.version_info < (2, 8): +elif sys.version_info < (2, 7, 9): # For secure SSL connexion with Python 2.7 (InsecurePlatformWarning) install_requires.append('ndg-httpsclient') install_requires.append('pyasn1') From 2ce7d5cbd636b5976f5e1aa00464982d73daf6dc Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 17 Dec 2015 12:22:09 -0800 Subject: [PATCH 076/159] add support for verbose count setting logger level --- letsencrypt/storage.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 3b2b548b0..9614f091a 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -116,6 +116,8 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # read further defaults from the systemwide renewal configuration # file at this stage? self.configuration = config_with_defaults(self.configfile) + logger_level = self.configuration['renewalparams']['verbose_count'] + set_logger_level(logger_level) if not all(x in self.configuration for x in ALL_FOUR): raise errors.CertStorageError( @@ -129,6 +131,21 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes self._fix_symlinks() + def set_logger_level(logger_level): + levels_dict = {"0" : 0, + "-1" : 10, + "-2" : 20, + "-3" : 30, + "-4" : 40, + "-5" : 50} + if logger_level in levels_dict: + new_level = levels_dict[logger_level] + else: + new_level = 30 + root_logger = logger.parent + root_logger.setLevel(new_level) + return + def _consistent(self): """Are the files associated with this lineage self-consistent? From 44a9d3d2907a3dc87c2536e2620947d9711ad879 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 17 Dec 2015 12:29:28 -0800 Subject: [PATCH 077/159] fixed self issue --- letsencrypt/storage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 9614f091a..c79903039 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -117,7 +117,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # file at this stage? self.configuration = config_with_defaults(self.configfile) logger_level = self.configuration['renewalparams']['verbose_count'] - set_logger_level(logger_level) + self.set_logger_level(logger_level) if not all(x in self.configuration for x in ALL_FOUR): raise errors.CertStorageError( @@ -131,7 +131,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes self._fix_symlinks() - def set_logger_level(logger_level): + def set_logger_level(self, logger_level): levels_dict = {"0" : 0, "-1" : 10, "-2" : 20, From 253cc3dc8f0b30a2aaa1d5b2ae29ea635ccf4b59 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 17 Dec 2015 14:36:53 -0800 Subject: [PATCH 078/159] have the handler actually set the level of the logger --- letsencrypt/renewer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/letsencrypt/renewer.py b/letsencrypt/renewer.py index 8cb5d1c3d..2f36e7e91 100644 --- a/letsencrypt/renewer.py +++ b/letsencrypt/renewer.py @@ -116,6 +116,7 @@ def renew(cert, old_version): def _cli_log_handler(args, level, fmt): # pylint: disable=unused-argument handler = colored_logging.StreamHandler() handler.setFormatter(logging.Formatter(fmt)) + handler.setLevel(level) return handler From e463fca34d88b9d28c874c97dccbd10c118dfe0a Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 17 Dec 2015 16:01:21 -0800 Subject: [PATCH 079/159] fix broken test --- letsencrypt/storage.py | 2 +- letsencrypt/tests/renewer_test.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 7e2802b14..5186cd945 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -260,7 +260,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes :returns: The path to the current version of the specified member. - :rtype: str + :rtype: str or None """ if kind not in ALL_FOUR: diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index daec9678f..d583e8645 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -764,6 +764,8 @@ class RenewableCertTests(BaseRenewableCertTest): def test_bad_config_file(self): from letsencrypt import renewer + os.unlink(os.path.join(self.cli_config.renewal_configs_dir, + "example.org.conf")) with open(os.path.join(self.cli_config.renewal_configs_dir, "bad.conf"), "w") as f: f.write("incomplete = configfile\n") From 79432fddc3cc97d2ca7ca7f525eac9ec76441b30 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 17 Dec 2015 16:40:56 -0800 Subject: [PATCH 080/159] undo previous logger changes --- letsencrypt/storage.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index b9587b909..c2992bb47 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -116,8 +116,6 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # read further defaults from the systemwide renewal configuration # file at this stage? self.configuration = config_with_defaults(self.configfile) - logger_level = self.configuration['renewalparams']['verbose_count'] - self.set_logger_level(logger_level) if not all(x in self.configuration for x in ALL_FOUR): raise errors.CertStorageError( @@ -131,21 +129,6 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes self._fix_symlinks() - def set_logger_level(self, logger_level): - levels_dict = {"0" : 0, - "-1" : 10, - "-2" : 20, - "-3" : 30, - "-4" : 40, - "-5" : 50} - if logger_level in levels_dict: - new_level = levels_dict[logger_level] - else: - new_level = 30 - root_logger = logger.parent - root_logger.setLevel(new_level) - return - def _consistent(self): """Are the files associated with this lineage self-consistent? From 3e7072e131b288322383628a075775670e880b4e Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Fri, 18 Dec 2015 08:08:52 +0000 Subject: [PATCH 081/159] Add failing test from ticket #1934 Augeas fails to parse a directive argument with a quote inside (expecting either fully quoted or unquoted values). --- .../failing/graphite-quote-1934.conf | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/apache-conf-files/failing/graphite-quote-1934.conf diff --git a/tests/apache-conf-files/failing/graphite-quote-1934.conf b/tests/apache-conf-files/failing/graphite-quote-1934.conf new file mode 100644 index 000000000..2a8734b43 --- /dev/null +++ b/tests/apache-conf-files/failing/graphite-quote-1934.conf @@ -0,0 +1,21 @@ + + + WSGIDaemonProcess _graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120 user=_graphite group=_graphite + WSGIProcessGroup _graphite + WSGIImportScript /usr/share/graphite-web/graphite.wsgi process-group=_graphite application-group=%{GLOBAL} + WSGIScriptAlias / /usr/share/graphite-web/graphite.wsgi + + Alias /content/ /usr/share/graphite-web/static/ + + SetHandler None + + + ErrorLog ${APACHE_LOG_DIR}/graphite-web_error.log + + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn + + CustomLog ${APACHE_LOG_DIR}/graphite-web_access.log combined + + From a72e498c97c4a0f77e0f2996e6fd1251122bcffb Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Fri, 18 Dec 2015 08:09:47 +0000 Subject: [PATCH 082/159] Merge Augeas lens fix for quotes in directive arguments From https://github.com/hercules-team/augeas/commit/d4d7ea97718c09c5968277aba08d5e47b971b2ac Closes: #1934 --- letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug | 2 +- .../{failing => passing}/graphite-quote-1934.conf | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/apache-conf-files/{failing => passing}/graphite-quote-1934.conf (100%) diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug index d665ea7a7..0f2cb7b45 100644 --- a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug +++ b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug @@ -59,7 +59,7 @@ let empty = Util.empty_dos let indent = Util.indent (* borrowed from shellvars.aug *) -let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ '"\t\r\n])|\\\\"|\\\\'/ +let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'/ let char_arg_sec = /[^ '"\t\r\n>]|\\\\"|\\\\'/ let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/ diff --git a/tests/apache-conf-files/failing/graphite-quote-1934.conf b/tests/apache-conf-files/passing/graphite-quote-1934.conf similarity index 100% rename from tests/apache-conf-files/failing/graphite-quote-1934.conf rename to tests/apache-conf-files/passing/graphite-quote-1934.conf From e885536f9d1fd9dd8374ebb8a1038a33b69793b0 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 18 Dec 2015 10:13:15 -0800 Subject: [PATCH 083/159] Move module installation inside the test script --- scripts/test_apache2.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index 20dc10a71..a048a1ad0 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -45,11 +45,7 @@ fi if [ "$OS_TYPE" = "ubuntu" ] ; then export LETSENCRYPT="$HOME/.local/share/letsencrypt/bin/letsencrypt" - sudo apt-get install -y libapache2-mod-wsgi - for mod in `grep -v '^#' tests/apache-conf-files/passing/README.modules` ; do - sudo a2enmod $mod - done - tests/apache-conf-files/hackish-apache-test + tests/apache-conf-files/hackish-apache-test --debian-modules else echo Not running hackish apache tests on $OS_TYPE fi From 9f02f264c5d2c2762cd470019845442d32bfbfc2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 18 Dec 2015 11:07:39 -0800 Subject: [PATCH 084/159] test_tox : run unit tests through tox --- scripts/test_tox.sh | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100755 scripts/test_tox.sh diff --git a/scripts/test_tox.sh b/scripts/test_tox.sh new file mode 100755 index 000000000..f7f325d5c --- /dev/null +++ b/scripts/test_tox.sh @@ -0,0 +1,80 @@ +#!/bin/bash -x +XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} +VENV_NAME="venv" +# The path to the letsencrypt-auto script. Everything that uses these might +# at some point be inlined... +LEA_PATH=./letsencrypt/ +VENV_PATH=${LEA_PATH/$VENV_NAME} +VENV_BIN=${VENV_PATH}/bin +BOOTSTRAP=${LEA_PATH}/bootstrap + +SUDO=sudo + +ExperimentalBootstrap() { + # Arguments: Platform name, boostrap script name, SUDO command (iff needed) + if [ "$2" != "" ] ; then + echo "Bootstrapping dependencies for $1..." + if [ "$3" != "" ] ; then + "$3" "$BOOTSTRAP/$2" + else + "$BOOTSTRAP/$2" + fi + fi +} + +# virtualenv call is not idempotent: it overwrites pip upgraded in +# later steps, causing "ImportError: cannot import name unpack_url" +if [ ! -f $BOOTSTRAP/debian.sh ] ; then + echo "Cannot find the letsencrypt bootstrap scripts in $BOOTSTRAP" + exit 1 +fi + +if [ -f /etc/debian_version ] ; then + echo "Bootstrapping dependencies for Debian-based OSes..." + $SUDO $BOOTSTRAP/_deb_common.sh +elif [ -f /etc/redhat-release ] ; then + echo "Bootstrapping dependencies for RedHat-based OSes..." + $SUDO $BOOTSTRAP/_rpm_common.sh +elif `grep -q openSUSE /etc/os-release` ; then + echo "Bootstrapping dependencies for openSUSE-based OSes..." + $SUDO $BOOTSTRAP/_suse_common.sh +elif [ -f /etc/arch-release ] ; then + if [ "$DEBUG" = 1 ] ; then + echo "Bootstrapping dependencies for Archlinux..." + $SUDO $BOOTSTRAP/archlinux.sh + else + echo "Please use pacman to install letsencrypt packages:" + echo "# pacman -S letsencrypt letsencrypt-apache" + echo + echo "If you would like to use the virtualenv way, please run the script again with the" + echo "--debug flag." + exit 1 + fi +elif [ -f /etc/manjaro-release ] ; then + ExperimentalBootstrap "Manjaro Linux" manjaro.sh "$SUDO" +elif [ -f /etc/gentoo-release ] ; then + ExperimentalBootstrap "Gentoo" _gentoo_common.sh "$SUDO" +elif uname | grep -iq FreeBSD ; then + ExperimentalBootstrap "FreeBSD" freebsd.sh "$SUDO" +elif uname | grep -iq Darwin ; then + ExperimentalBootstrap "Mac OS X" mac.sh # homebrew doesn't normally run as root +elif grep -iq "Amazon Linux" /etc/issue ; then + ExperimentalBootstrap "Amazon Linux" _rpm_common.sh "$SUDO" +else + echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" + echo + echo "You will need to bootstrap, configure virtualenv, and run a pip install manually" + echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" + echo "for more info" +fi +echo "Bootstrapped!" + +cd letsencrypt +./bootstrap/dev/venv.sh +PYVER=`python --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` + +if [ $PYVER -eq 26 ] ; then + venv/bin/tox -e py26 +else + venv/bin/tox -e py27 +fi From 483ab16f574df34c339d457bfa39cd7c62191bae Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Fri, 18 Dec 2015 20:34:35 -0500 Subject: [PATCH 085/159] fix typo in using.rst --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 115688c93..5da13f02c 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -371,7 +371,7 @@ If you run Debian Stretch or Debian Sid, you can install letsencrypt packages. sudo apt-get update sudo apt-get install letsencrypt python-letsencrypt-apache -If you don't want to use the Apache plugin, you can ommit the +If you don't want to use the Apache plugin, you can omit the ``python-letsencrypt-apache`` package. Packages for Debian Jessie are coming in the next few weeks. From 55d1f68c77cfa1cd1e7cb9843d6cab8541d86bf7 Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Fri, 18 Dec 2015 21:17:05 -0500 Subject: [PATCH 086/159] Suppress spurious output Suppress spurious output while testing for the presence of the virtualenv or python-virtualenv package. --- bootstrap/_deb_common.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index 4c6b91a33..e82fa7271 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -24,11 +24,11 @@ apt-get update # distro version (#346) virtualenv= -if apt-cache show virtualenv > /dev/null ; then +if apt-cache show virtualenv > /dev/null 2>&1; then virtualenv="virtualenv" fi -if apt-cache show python-virtualenv > /dev/null ; then +if apt-cache show python-virtualenv > /dev/null 2>&1; then virtualenv="$virtualenv python-virtualenv" fi From cd7051323f008d5dc1a687f402fddc725d8049f5 Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Fri, 18 Dec 2015 21:27:24 -0500 Subject: [PATCH 087/159] Fix typo in comment --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 29519d430..aba9116f9 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1189,7 +1189,7 @@ def _plugins_parsing(helpful, plugins): # These would normally be a flag within the webroot plugin, but because # they are parsed in conjunction with --domains, they live here for - # legibiility. helpful.add_plugin_ags must be called first to add the + # legibility. helpful.add_plugin_ags must be called first to add the # "webroot" topic helpful.add("webroot", "-w", "--webroot-path", action=WebrootPathProcessor, help="public_html / webroot path. This can be specified multiple times to " From 0822906c297856b6d745fd020ca55233e80393c4 Mon Sep 17 00:00:00 2001 From: Daniel Convissor Date: Sat, 19 Dec 2015 09:41:37 -0500 Subject: [PATCH 088/159] Keep storage.names() from passing None to open() Fixes exiting abnormally with: TypeError: coercing to Unicode: need string or buffer, NoneType found --- letsencrypt/storage.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index c2992bb47..ac71bd9fe 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -450,12 +450,15 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes :param int version: the desired version number :returns: the subject names :rtype: `list` of `str` + :raises .CertStorageError: if could not find cert file. """ if version is None: target = self.current_target("cert") else: target = self.version("cert", version) + if target is None: + raise errors.CertStorageError("could not find cert file") with open(target) as f: return crypto_util.get_sans_from_cert(f.read()) From 212f04fd922f2976d86fa8cf96a2b22e113e8b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20HUBSCHER?= Date: Sun, 20 Dec 2015 16:02:32 +0100 Subject: [PATCH 089/159] @kuba review --- acme/setup.py | 7 ++++--- setup.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index e75d77efd..ba2c88394 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -27,12 +27,13 @@ if sys.version_info < (2, 7): 'argparse', 'mock<1.1.0', ]) -elif sys.version_info < (2, 7, 9): +else: + install_requires.append('mock') + +if sys.version_info < (2, 7, 9): # For secure SSL connexion with Python 2.7 (InsecurePlatformWarning) install_requires.append('ndg-httpsclient') install_requires.append('pyasn1') -else: - install_requires.append('mock') docs_extras = [ 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags diff --git a/setup.py b/setup.py index 0341e400b..3d1acae39 100644 --- a/setup.py +++ b/setup.py @@ -55,12 +55,13 @@ if sys.version_info < (2, 7): 'argparse', 'mock<1.1.0', ]) -elif sys.version_info < (2, 7, 9): +else: + install_requires.append('mock') + +if sys.version_info < (2, 7, 9): # For secure SSL connexion with Python 2.7 (InsecurePlatformWarning) install_requires.append('ndg-httpsclient') install_requires.append('pyasn1') -else: - install_requires.append('mock') dev_extras = [ # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 From 87dfe8c2b23a9205d77a453c2ff459fd3cc73ddc Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 21 Dec 2015 11:12:01 -0800 Subject: [PATCH 090/159] Move everything into tests/letstest --- README.md => tests/letstest/README.md | 0 apache2_targets.yaml => tests/letstest/apache2_targets.yaml | 0 multitester.py => tests/letstest/multitester.py | 0 {scripts => tests/letstest/scripts}/boulder_config.sh | 0 {scripts => tests/letstest/scripts}/boulder_install.sh | 0 {scripts => tests/letstest/scripts}/test_apache2.sh | 0 {scripts => tests/letstest/scripts}/test_leauto_upgrades.sh | 0 .../scripts}/test_letsencrypt_auto_certonly_standalone.sh | 0 .../letstest/scripts}/test_letsencrypt_auto_venv_only.sh | 0 {scripts => tests/letstest/scripts}/test_tox.sh | 0 targets.yaml => tests/letstest/targets.yaml | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename README.md => tests/letstest/README.md (100%) rename apache2_targets.yaml => tests/letstest/apache2_targets.yaml (100%) rename multitester.py => tests/letstest/multitester.py (100%) rename {scripts => tests/letstest/scripts}/boulder_config.sh (100%) rename {scripts => tests/letstest/scripts}/boulder_install.sh (100%) rename {scripts => tests/letstest/scripts}/test_apache2.sh (100%) rename {scripts => tests/letstest/scripts}/test_leauto_upgrades.sh (100%) rename {scripts => tests/letstest/scripts}/test_letsencrypt_auto_certonly_standalone.sh (100%) rename {scripts => tests/letstest/scripts}/test_letsencrypt_auto_venv_only.sh (100%) rename {scripts => tests/letstest/scripts}/test_tox.sh (100%) rename targets.yaml => tests/letstest/targets.yaml (100%) diff --git a/README.md b/tests/letstest/README.md similarity index 100% rename from README.md rename to tests/letstest/README.md diff --git a/apache2_targets.yaml b/tests/letstest/apache2_targets.yaml similarity index 100% rename from apache2_targets.yaml rename to tests/letstest/apache2_targets.yaml diff --git a/multitester.py b/tests/letstest/multitester.py similarity index 100% rename from multitester.py rename to tests/letstest/multitester.py diff --git a/scripts/boulder_config.sh b/tests/letstest/scripts/boulder_config.sh similarity index 100% rename from scripts/boulder_config.sh rename to tests/letstest/scripts/boulder_config.sh diff --git a/scripts/boulder_install.sh b/tests/letstest/scripts/boulder_install.sh similarity index 100% rename from scripts/boulder_install.sh rename to tests/letstest/scripts/boulder_install.sh diff --git a/scripts/test_apache2.sh b/tests/letstest/scripts/test_apache2.sh similarity index 100% rename from scripts/test_apache2.sh rename to tests/letstest/scripts/test_apache2.sh diff --git a/scripts/test_leauto_upgrades.sh b/tests/letstest/scripts/test_leauto_upgrades.sh similarity index 100% rename from scripts/test_leauto_upgrades.sh rename to tests/letstest/scripts/test_leauto_upgrades.sh diff --git a/scripts/test_letsencrypt_auto_certonly_standalone.sh b/tests/letstest/scripts/test_letsencrypt_auto_certonly_standalone.sh similarity index 100% rename from scripts/test_letsencrypt_auto_certonly_standalone.sh rename to tests/letstest/scripts/test_letsencrypt_auto_certonly_standalone.sh diff --git a/scripts/test_letsencrypt_auto_venv_only.sh b/tests/letstest/scripts/test_letsencrypt_auto_venv_only.sh similarity index 100% rename from scripts/test_letsencrypt_auto_venv_only.sh rename to tests/letstest/scripts/test_letsencrypt_auto_venv_only.sh diff --git a/scripts/test_tox.sh b/tests/letstest/scripts/test_tox.sh similarity index 100% rename from scripts/test_tox.sh rename to tests/letstest/scripts/test_tox.sh diff --git a/targets.yaml b/tests/letstest/targets.yaml similarity index 100% rename from targets.yaml rename to tests/letstest/targets.yaml From 9e8f3cc644e7deb388361dc1f3e7430880a0609a Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 21 Dec 2015 22:52:32 +0200 Subject: [PATCH 091/159] Add gentoo fingerprint --- letsencrypt-apache/letsencrypt_apache/constants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 989106e39..8f3c6a3f6 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -42,7 +42,8 @@ CLI_DEFAULTS = { "centos": CLI_DEFAULTS_CENTOS, "centos linux": CLI_DEFAULTS_CENTOS, "fedora": CLI_DEFAULTS_CENTOS, - "red hat enterprise linux server": CLI_DEFAULTS_CENTOS + "red hat enterprise linux server": CLI_DEFAULTS_CENTOS, + "gentoo base system": CLI_DEFAULTS_GENTOO } """CLI defaults.""" From 52167475dfb8f92371fd564f7c35d51651180d17 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 21 Dec 2015 23:51:22 +0200 Subject: [PATCH 092/159] Get pass version to parser and ignore CLI checks for define for 2.2 as it returns empty results anyway --- .../letsencrypt_apache/configurator.py | 14 ++++++++------ letsencrypt-apache/letsencrypt_apache/constants.py | 3 +++ letsencrypt-apache/letsencrypt_apache/parser.py | 5 +++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index d67e7bc18..5c71ee1fe 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -156,11 +156,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Make sure configuration is valid self.config_test() - self.parser = parser.ApacheParser( - self.aug, self.conf("server-root"), self.conf("vhost-root"), self.conf("ctl")) - # Check for errors in parsing files with Augeas - self.check_parsing_errors("httpd.aug") - # Set Version if self.version is None: self.version = self.get_version() @@ -168,6 +163,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): raise errors.NotSupportedError( "Apache Version %s not supported.", str(self.version)) + self.parser = parser.ApacheParser( + self.aug, self.conf("server-root"), self.conf("vhost-root"), + self.conf("ctl"), self.version) + # Check for errors in parsing files with Augeas + self.check_parsing_errors("httpd.aug") + # Get all of the available vhosts self.vhosts = self.get_virtual_hosts() @@ -1277,7 +1278,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ try: - stdout, _ = le_util.run_script([self.conf("ctl"), "-v"]) + stdout, _ = le_util.run_script( + constants.os_constant("version_cmd").split(" ")) except errors.SubprocessError: raise errors.PluginError( "Unable to run %s -v" % self.conf("ctl")) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 8f3c6a3f6..410d9adb3 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -7,6 +7,7 @@ CLI_DEFAULTS_DEBIAN = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/sites-available", ctl="apache2ctl", + version_cmd='apache2ctl -v', enmod="a2enmod", dismod="a2dismod", le_vhost_ext="-le-ssl.conf", @@ -18,6 +19,7 @@ CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", vhost_root="/etc/httpd/conf.d", ctl="apachectl", + version_cmd="apachectl -v", enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", @@ -29,6 +31,7 @@ CLI_DEFAULTS_GENTOO = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/vhosts.d", ctl="apache2ctl", + version_cmd="/usr/sbin/apache2 -v", enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 0289c57d8..c9ab438ca 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -28,7 +28,7 @@ class ApacheParser(object): arg_var_interpreter = re.compile(r"\$\{[^ \}]*}") fnmatch_chars = set(["*", "?", "\\", "[", "]"]) - def __init__(self, aug, root, vhostroot, ctl): + def __init__(self, aug, root, vhostroot, ctl, version=(2, 4)): # Note: Order is important here. # This uses the binary, so it can be done first. @@ -36,7 +36,8 @@ class ApacheParser(object): # https://httpd.apache.org/docs/2.4/mod/core.html#ifdefine # This only handles invocation parameters and Define directives! self.variables = {} - self.update_runtime_variables(ctl) + if version >= (2, 4): + self.update_runtime_variables(ctl) self.aug = aug # Find configuration root and make sure augeas can parse it. From ca39b0d12597621d840555f5b29a1b03e37f7ad0 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Mon, 21 Dec 2015 14:39:14 -0800 Subject: [PATCH 093/159] fixed linting problems --- .../letsencrypt_apache/tests/configurator_test.py | 5 ++++- letsencrypt-apache/letsencrypt_apache/tests/parser_test.py | 6 ++++-- .../letsencrypt_apache/tests/tls_sni_01_test.py | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index 2d57de668..d7bc04f20 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -33,9 +33,12 @@ class TwoVhost80Test(util.ApacheTest): self.temp_dir, "debian_apache_2_4/two_vhost_80") def mock_deploy_cert(self, config): + """A test for a mock deploy cert""" self.config.real_deploy_cert = self.config.deploy_cert def mocked_deploy_cert(*args, **kwargs): - with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod") as mock_enable: + """a helper to mock a deployed cert""" + with mock.patch( + "letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"): config.real_deploy_cert(*args, **kwargs) self.config.deploy_cert = mocked_deploy_cert return self.config diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py index 57a75bcec..352c2fcf4 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py @@ -152,7 +152,7 @@ class BasicParserTest(util.ParserTest): def test_update_runtime_vars_bad_output(self, mock_cfg): mock_cfg.return_value = "Define: TLS=443=24" self.parser.update_runtime_variables("ctl") - self.assertTrue( self.parser.unparsable) + self.assertTrue(self.parser.unparsable) mock_cfg.return_value = "Define: DUMP_RUN_CFG\nDefine: TLS=443=24" self.assertRaises( @@ -189,6 +189,8 @@ class ParserInitTest(util.ApacheTest): def test_unparsable(self, mock_cfg): from letsencrypt_apache.parser import ApacheParser def unparsable_true(self, arg): + """a helper to set the self unparsabale to true""" + print "side effect has passed in arg: %s", arg self.unparsable = True with mock.patch.object(ApacheParser, 'update_runtime_variables', autospec=True) as urv: urv.side_effect = unparsable_true @@ -196,7 +198,7 @@ class ParserInitTest(util.ApacheTest): self.assertRaises( errors.PluginError, ApacheParser, self.aug, os.path.relpath(self.config_path), "ctl") - self.assertEquals(1,1) + self.assertEquals(1, 1) def test_root_normalized(self): from letsencrypt_apache.parser import ApacheParser diff --git a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py b/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py index 6f10555f8..7db4eee6f 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py @@ -78,7 +78,8 @@ class TlsSniPerformTest(util.ApacheTest): # pylint: disable=protected-access self.sni._setup_challenge_cert = mock_setup_cert - with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod") as mock_enable: + with mock.patch( + "letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"): sni_responses = self.sni.perform() self.assertEqual(mock_setup_cert.call_count, 2) From 03fdd03a878ce1451c6e766f32421e604ceb4f72 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 19:52:35 -0800 Subject: [PATCH 094/159] Experimentally try travis with the hackish-apache-test --- .travis.yml | 1 + tox.ini | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8dde06ceb..2b37c1bef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ env: - TOXENV=py27 BOULDER_INTEGRATION=1 - TOXENV=lint - TOXENV=cover + - TOXENV=hackishapachetest # Only build pushes to the master branch, PRs, and branches beginning with diff --git a/tox.ini b/tox.ini index d1fafe20f..cffbc4e18 100644 --- a/tox.ini +++ b/tox.ini @@ -67,3 +67,10 @@ commands = pylint --rcfile=.pylintrc letsencrypt-nginx/letsencrypt_nginx pylint --rcfile=.pylintrc letsencrypt-compatibility-test/letsencrypt_compatibility_test pylint --rcfile=.pylintrc letshelp-letsencrypt/letshelp_letsencrypt + +[testenv:hackishapachetest] +basepython = python2.7 +setenv = + LETSENCRYPT=letsencrypt +commands = + ./tests/apache-conf-files/hackish-apache-test From 9129dcbc8797f67d85d04fa29541b5cec446e2ce Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 16 Dec 2015 20:04:27 -0800 Subject: [PATCH 095/159] Make sure there's always a domain name to prompt a question --- tests/apache-conf-files/hackish-apache-test | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/apache-conf-files/hackish-apache-test b/tests/apache-conf-files/hackish-apache-test index b8caaadc0..27ee0fdf0 100755 --- a/tests/apache-conf-files/hackish-apache-test +++ b/tests/apache-conf-files/hackish-apache-test @@ -23,6 +23,13 @@ function Setup() { if [ "$APPEND_APACHECONF" = "" ] ; then sudo cp "$f" "$EA"/sites-available/ sudo ln -sf "$EA/sites-available/$f" "$EA/sites-enabled/$f" + sudo echo """ + + ServerName example.com + DocumentRoot /tmp/ + ErrorLog /tmp/error.log + CustomLog /tmp/requests.log combined +""" >> $EA/sites-available/throwaway-example.conf else TMP="/tmp/`basename \"$APPEND_APACHECONF\"`.$$" sudo cp -a "$APPEND_APACHECONF" "$TMP" @@ -33,6 +40,7 @@ function Setup() { function Cleanup() { if [ "$APPEND_APACHECONF" = "" ] ; then sudo rm /etc/apache2/sites-{enabled,available}/"$f" + sudo rm $EA/sites-available/throwaway-example.conf else sudo mv "$TMP" "$APPEND_APACHECONF" fi From d777e7fabad3f379c266382a36f970a13e85cf12 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 17 Dec 2015 15:45:47 -0800 Subject: [PATCH 096/159] This sort of works in tox; travis is unlikely due to sudo --- .travis.yml | 1 + .../letsencrypt_apache/tests}/apache-conf-files/NEEDED.txt | 0 .../tests}/apache-conf-files/failing/ipv6-1143.conf | 0 .../tests}/apache-conf-files/failing/ipv6-1143b.conf | 0 .../apache-conf-files/failing/missing-double-quote-1724.conf | 0 .../tests}/apache-conf-files/failing/multivhost-1093.conf | 0 .../tests}/apache-conf-files/failing/multivhost-1093b.conf | 0 .../tests}/apache-conf-files/hackish-apache-test | 2 +- .../tests}/apache-conf-files/passing/1626-1531.conf | 0 .../tests}/apache-conf-files/passing/README.modules | 0 .../tests}/apache-conf-files/passing/anarcat-1531.conf | 0 .../passing/drupal-errordocument-arg-1724.conf | 0 .../tests}/apache-conf-files/passing/drupal-htaccess-1531.conf | 0 .../tests}/apache-conf-files/passing/example-1755.conf | 0 .../tests}/apache-conf-files/passing/example-ssl.conf | 0 .../tests}/apache-conf-files/passing/example.conf | 0 .../apache-conf-files/passing/finalize-1243.apache2.conf.txt | 0 .../tests}/apache-conf-files/passing/finalize-1243.conf | 0 .../tests}/apache-conf-files/passing/missing-quote-1724.conf | 0 .../tests}/apache-conf-files/passing/modmacro-1385.conf | 0 .../tests}/apache-conf-files/passing/owncloud-1264.conf | 0 .../tests}/apache-conf-files/passing/roundcube-1222.conf | 0 .../tests}/apache-conf-files/passing/semacode-1598.conf | 0 .../apache-conf-files/passing/sslrequire-wordlist-1827.htaccess | 0 .../apache-conf-files/passing/two-blocks-one-line-1693.conf | 0 tox.ini | 2 +- 26 files changed, 3 insertions(+), 2 deletions(-) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/NEEDED.txt (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/failing/ipv6-1143.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/failing/ipv6-1143b.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/failing/missing-double-quote-1724.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/failing/multivhost-1093.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/failing/multivhost-1093b.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/hackish-apache-test (97%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/1626-1531.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/README.modules (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/anarcat-1531.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/drupal-errordocument-arg-1724.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/drupal-htaccess-1531.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/example-1755.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/example-ssl.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/example.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/finalize-1243.apache2.conf.txt (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/finalize-1243.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/missing-quote-1724.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/modmacro-1385.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/owncloud-1264.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/roundcube-1222.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/semacode-1598.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/two-blocks-one-line-1693.conf (100%) diff --git a/.travis.yml b/.travis.yml index 2b37c1bef..dfbc09e2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: python services: - rabbitmq - mariadb + - apache2 # http://docs.travis-ci.com/user/ci-environment/#CI-environment-OS # gimme has to be kept in sync with Boulder's Go version setting in .travis.yml diff --git a/tests/apache-conf-files/NEEDED.txt b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt similarity index 100% rename from tests/apache-conf-files/NEEDED.txt rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt diff --git a/tests/apache-conf-files/failing/ipv6-1143.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/ipv6-1143.conf similarity index 100% rename from tests/apache-conf-files/failing/ipv6-1143.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/ipv6-1143.conf diff --git a/tests/apache-conf-files/failing/ipv6-1143b.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/ipv6-1143b.conf similarity index 100% rename from tests/apache-conf-files/failing/ipv6-1143b.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/ipv6-1143b.conf diff --git a/tests/apache-conf-files/failing/missing-double-quote-1724.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf similarity index 100% rename from tests/apache-conf-files/failing/missing-double-quote-1724.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf diff --git a/tests/apache-conf-files/failing/multivhost-1093.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093.conf similarity index 100% rename from tests/apache-conf-files/failing/multivhost-1093.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093.conf diff --git a/tests/apache-conf-files/failing/multivhost-1093b.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093b.conf similarity index 100% rename from tests/apache-conf-files/failing/multivhost-1093b.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093b.conf diff --git a/tests/apache-conf-files/hackish-apache-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test similarity index 97% rename from tests/apache-conf-files/hackish-apache-test rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test index 27ee0fdf0..9e828bf2d 100755 --- a/tests/apache-conf-files/hackish-apache-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test @@ -7,7 +7,7 @@ # assess, but it should be automated export EA=/etc/apache2/ TESTDIR="`dirname $0`" -LEROOT="`realpath \"$TESTDIR/../../\"`" +LEROOT="`realpath \"$TESTDIR/../../../../\"`" cd $TESTDIR/passing LETSENCRYPT="${LETSENCRYPT:-$LEROOT/venv/bin/letsencrypt}" diff --git a/tests/apache-conf-files/passing/1626-1531.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/1626-1531.conf similarity index 100% rename from tests/apache-conf-files/passing/1626-1531.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/1626-1531.conf diff --git a/tests/apache-conf-files/passing/README.modules b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/README.modules similarity index 100% rename from tests/apache-conf-files/passing/README.modules rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/README.modules diff --git a/tests/apache-conf-files/passing/anarcat-1531.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/anarcat-1531.conf similarity index 100% rename from tests/apache-conf-files/passing/anarcat-1531.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/anarcat-1531.conf diff --git a/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf similarity index 100% rename from tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf diff --git a/tests/apache-conf-files/passing/drupal-htaccess-1531.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf similarity index 100% rename from tests/apache-conf-files/passing/drupal-htaccess-1531.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf diff --git a/tests/apache-conf-files/passing/example-1755.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-1755.conf similarity index 100% rename from tests/apache-conf-files/passing/example-1755.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-1755.conf diff --git a/tests/apache-conf-files/passing/example-ssl.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf similarity index 100% rename from tests/apache-conf-files/passing/example-ssl.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf diff --git a/tests/apache-conf-files/passing/example.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example.conf similarity index 100% rename from tests/apache-conf-files/passing/example.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example.conf diff --git a/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt similarity index 100% rename from tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt diff --git a/tests/apache-conf-files/passing/finalize-1243.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.conf similarity index 100% rename from tests/apache-conf-files/passing/finalize-1243.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.conf diff --git a/tests/apache-conf-files/passing/missing-quote-1724.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/missing-quote-1724.conf similarity index 100% rename from tests/apache-conf-files/passing/missing-quote-1724.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/missing-quote-1724.conf diff --git a/tests/apache-conf-files/passing/modmacro-1385.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/modmacro-1385.conf similarity index 100% rename from tests/apache-conf-files/passing/modmacro-1385.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/modmacro-1385.conf diff --git a/tests/apache-conf-files/passing/owncloud-1264.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/owncloud-1264.conf similarity index 100% rename from tests/apache-conf-files/passing/owncloud-1264.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/owncloud-1264.conf diff --git a/tests/apache-conf-files/passing/roundcube-1222.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/roundcube-1222.conf similarity index 100% rename from tests/apache-conf-files/passing/roundcube-1222.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/roundcube-1222.conf diff --git a/tests/apache-conf-files/passing/semacode-1598.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/semacode-1598.conf similarity index 100% rename from tests/apache-conf-files/passing/semacode-1598.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/semacode-1598.conf diff --git a/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess similarity index 100% rename from tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess diff --git a/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf similarity index 100% rename from tests/apache-conf-files/passing/two-blocks-one-line-1693.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf diff --git a/tox.ini b/tox.ini index cffbc4e18..1d2f12e6c 100644 --- a/tox.ini +++ b/tox.ini @@ -73,4 +73,4 @@ basepython = python2.7 setenv = LETSENCRYPT=letsencrypt commands = - ./tests/apache-conf-files/hackish-apache-test + sudo ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test From a819ee146d6369ab08fe022ae3f7481e50d32ed9 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 17 Dec 2015 18:03:34 -0800 Subject: [PATCH 097/159] Experimentally try sudo in travis This may decontainerise us, so we might not want to merge even if it works... --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dfbc09e2e..a0ca6576f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,7 @@ branches: - /^test-.*$/ # container-based infrastructure -sudo: false +sudo: true addons: # make sure simplehttp simple verification works (custom /etc/hosts) From 7a16e2e2489bceb6cb88e21e0fa0d75900572250 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 18 Dec 2015 00:17:22 -0800 Subject: [PATCH 098/159] Wrangle things to actually run in travis --- .travis.yml | 11 +++++++---- .../tests/apache-conf-files/hackish-apache-test | 1 + tox.ini | 5 +++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index a0ca6576f..b1fe7f55d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,11 +19,11 @@ env: - GOPATH=/tmp/go - PATH=$GOPATH/bin:$PATH matrix: - - TOXENV=py26 BOULDER_INTEGRATION=1 - - TOXENV=py27 BOULDER_INTEGRATION=1 - - TOXENV=lint - - TOXENV=cover - TOXENV=hackishapachetest +# - TOXENV=py26 BOULDER_INTEGRATION=1 +# - TOXENV=py27 BOULDER_INTEGRATION=1 +# - TOXENV=lint +# - TOXENV=cover # Only build pushes to the master branch, PRs, and branches beginning with @@ -60,6 +60,9 @@ addons: - openssl # For Boulder integration testing - rsyslog + # for hackishapachetest + - realpath + - apache2 install: "travis_retry pip install tox coveralls" script: 'travis_retry tox && ([ "xxx$BOULDER_INTEGRATION" = "xxx" ] || ./tests/travis-integration.sh)' diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test index 9e828bf2d..664423d7a 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test @@ -51,6 +51,7 @@ trap CleanupExit INT for f in *.conf ; do echo -n testing "$f"... Setup + echo running from $PWD RESULT=`echo c | sudo "$LETSENCRYPT" --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Please specify --domains"\|"mod_macro is not yet"\) ; then echo passed diff --git a/tox.ini b/tox.ini index 1d2f12e6c..1a637777d 100644 --- a/tox.ini +++ b/tox.ini @@ -69,8 +69,9 @@ commands = pylint --rcfile=.pylintrc letshelp-letsencrypt/letshelp_letsencrypt [testenv:hackishapachetest] -basepython = python2.7 +#basepython = python2.7 setenv = - LETSENCRYPT=letsencrypt + LETSENCRYPT=/home/travis/build/letsencrypt/letsencrypt/.tox/hackishapachetest/bin/letsencrypt commands = + pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt sudo ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test From 8d71b2d6c38aa062ef20a4cb10d7e9fb4cc345c3 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 18 Dec 2015 08:48:49 -0800 Subject: [PATCH 099/159] Install Apache modules in travis --- .travis.yml | 2 ++ .../tests/apache-conf-files/hackish-apache-test | 13 +++++++++++-- tox.ini | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b1fe7f55d..6b0af69c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,6 +63,8 @@ addons: # for hackishapachetest - realpath - apache2 + - libapache2-mod-wsgi + - sudo install: "travis_retry pip install tox coveralls" script: 'travis_retry tox && ([ "xxx$BOULDER_INTEGRATION" = "xxx" ] || ./tests/travis-integration.sh)' diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test index 664423d7a..cf06b48af 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test @@ -46,13 +46,22 @@ function Cleanup() { fi } +# if our environment asks us to enable modules, do our best! +if [ "$1" = --debian-modules ] ; then + sudo apt-get install -y libapache2-mod-wsgi + + for mod in ssl rewrite macro wsgi deflate ; do + sudo a2enmod $mod + done +fi + + FAILS=0 trap CleanupExit INT for f in *.conf ; do echo -n testing "$f"... Setup - echo running from $PWD - RESULT=`echo c | sudo "$LETSENCRYPT" --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` + RESULT=`echo c | sudo "$LETSENCRYPT" -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Please specify --domains"\|"mod_macro is not yet"\) ; then echo passed else diff --git a/tox.ini b/tox.ini index 1a637777d..abb934055 100644 --- a/tox.ini +++ b/tox.ini @@ -74,4 +74,4 @@ setenv = LETSENCRYPT=/home/travis/build/letsencrypt/letsencrypt/.tox/hackishapachetest/bin/letsencrypt commands = pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt - sudo ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test + sudo ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test --debian-modules From a0e902d405e8cd4bd3b584847ec8636affc818e0 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 18 Dec 2015 11:43:20 -0800 Subject: [PATCH 100/159] More module deps! --- .../tests/apache-conf-files/hackish-apache-test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test index cf06b48af..bfe71cc51 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test @@ -48,9 +48,9 @@ function Cleanup() { # if our environment asks us to enable modules, do our best! if [ "$1" = --debian-modules ] ; then - sudo apt-get install -y libapache2-mod-wsgi + sudo apt-get install -y libapache2-mod-{wsgi,macro} - for mod in ssl rewrite macro wsgi deflate ; do + for mod in ssl rewrite macro wsgi deflate userdir version ; do sudo a2enmod $mod done fi From 05e210d42ad5fe38a4b1b66a659e6dfe3d77e736 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 18 Dec 2015 11:44:52 -0800 Subject: [PATCH 101/159] Also add dependencies to travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 6b0af69c3..ab1931be1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,6 +64,7 @@ addons: - realpath - apache2 - libapache2-mod-wsgi + - libapache2-mod-macro - sudo install: "travis_retry pip install tox coveralls" From 47f7e70b764c4b5c760256659260059afee14df2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 21 Dec 2015 13:49:46 -0800 Subject: [PATCH 102/159] This is a more correct test --- .../tests/apache-conf-files/hackish-apache-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test index bfe71cc51..c661e6435 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test @@ -62,7 +62,7 @@ for f in *.conf ; do echo -n testing "$f"... Setup RESULT=`echo c | sudo "$LETSENCRYPT" -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` - if echo $RESULT | grep -Eq \("Please specify --domains"\|"mod_macro is not yet"\) ; then + if echo $RESULT | grep -Eq \("Which names would you like"\|"mod_macro is not yet"\) ; then echo passed else echo failed From 42333536517329dcb6584bf9da9c52389ff1be27 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 21 Dec 2015 16:41:57 -0800 Subject: [PATCH 103/159] release.sh stage version changes to letsencrypt/ ! Fixes: #1966 --- tools/release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/release.sh b/tools/release.sh index eeabfd4a3..172f6fea1 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -86,7 +86,7 @@ SetVersion() { done sed -i "s/^__version.*/__version__ = '$ver'/" letsencrypt/__init__.py - git add -p $SUBPKGS # interactive user input + git add -p letsencrypt $SUBPKGS # interactive user input } SetVersion "$version" git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version" From 61816a4029717860e2940d00d7c48e51e80d6bf7 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 21 Dec 2015 18:28:05 -0800 Subject: [PATCH 104/159] Give the user some warning before enabling backports --- bootstrap/_deb_common.sh | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index cd9036581..1fc9babcc 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -38,11 +38,20 @@ AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut - if dpkg --compare-version 1.0 gt "$AUGVERSION" ; then if lsb_release -a | grep -q wheezy ; then if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q wheezy-backports ; then - # XXX ask for permission before doing this? - echo Installing augeas from wheezy-backports... - echo deb http://http.debian.net/debian wheezy-backports main >> /etc/apt/sources.list - apt-get update - apt-get install -y --no-install-recommends -t wheezy-backports libaugeas0 + # This can theoretically error if sources.list.d is empty, but in that case we don't care. + if ! grep -v -e ' *#' /etc/apt/sources.list.d/* | grep -q wheezy-backports 2>/dev/null ; then + echo -n "Installing libaugeas0 from wheezy-backports in 3 seconds..." + sleep 1s + echo -e "\e[0K\rInstalling libaugeas0 from wheezy-backports in 2 seconds..." + sleep 1s + echo -e "\e[0K\rInstalling libaugeas0 from wheezy-backports in 1 second ..." + sleep 1s + echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' + + echo deb http://http.debian.net/debian wheezy-backports main >> /etc/apt/sources.list.d/wheezy-backports.list + apt-get update + apt-get install -y --no-install-recommends -t wheezy-backports libaugeas0 + fi fi augeas_pkg= else From 527eb82e6e436662bcfed10145e7c6cfde682d39 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 21 Dec 2015 18:28:36 -0800 Subject: [PATCH 105/159] Install backports, even if they were already present --- bootstrap/_deb_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index 1fc9babcc..aadacba0a 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -50,9 +50,9 @@ if dpkg --compare-version 1.0 gt "$AUGVERSION" ; then echo deb http://http.debian.net/debian wheezy-backports main >> /etc/apt/sources.list.d/wheezy-backports.list apt-get update - apt-get install -y --no-install-recommends -t wheezy-backports libaugeas0 fi fi + apt-get install -y --no-install-recommends -t wheezy-backports libaugeas0 augeas_pkg= else echo "No libaugeas0 version is available that's new enough to run the" From aa6bf73d4ad828bb87b7f02a0b17e9f98360bb1b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 21 Dec 2015 19:57:12 -0800 Subject: [PATCH 106/159] Only test permission failures if we're not root or, more generally, if we're on a system where permissions are being enforced Closes: #1979 --- letsencrypt/plugins/webroot_test.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 9f5b6bba8..07e41e0d0 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -66,8 +66,17 @@ class AuthenticatorTest(unittest.TestCase): def test_prepare_reraises_other_errors(self): self.auth.full_path = os.path.join(self.path, "null") + permission_canary = os.path.join(self.path, "rnd") + f = open(permission_canary, "w") + f.write("thingimy") + f.close() os.chmod(self.path, 0o000) - self.assertRaises(errors.PluginError, self.auth.prepare) + try: + open(permission_canary, "r") + print("Warning, running tests as root skips permissions tests...") + except IOError: + # ok, permissions work, test away... + self.assertRaises(errors.PluginError, self.auth.prepare) os.chmod(self.path, 0o700) @mock.patch("letsencrypt.plugins.webroot.os.chown") From e41339cda8e8d091f0bc7babbdd9098c7d17a1f7 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 21 Dec 2015 20:01:28 -0800 Subject: [PATCH 107/159] Keep lint happy (But what about py3?) --- letsencrypt/plugins/webroot_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 07e41e0d0..137a2673e 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -73,7 +73,7 @@ class AuthenticatorTest(unittest.TestCase): os.chmod(self.path, 0o000) try: open(permission_canary, "r") - print("Warning, running tests as root skips permissions tests...") + print "Warning, running tests as root skips permissions tests..." except IOError: # ok, permissions work, test away... self.assertRaises(errors.PluginError, self.auth.prepare) From 7b05d573f2d08eff5f62490935ca69429b93b67f Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 22 Dec 2015 10:32:18 +0200 Subject: [PATCH 108/159] Abstracted the -D DUMP_RUN_CFG command to use os specific one, defined in constants --- letsencrypt-apache/letsencrypt_apache/constants.py | 5 ++++- letsencrypt-apache/letsencrypt_apache/parser.py | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 712e7f240..a859f57d4 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -7,7 +7,8 @@ CLI_DEFAULTS_DEBIAN = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/sites-available", ctl="apache2ctl", - version_cmd='apache2ctl -v', + version_cmd="apache2ctl -v", + define_cmd="apache2ctl -t -D DUMP_RUN_CFG", enmod="a2enmod", dismod="a2dismod", le_vhost_ext="-le-ssl.conf", @@ -20,6 +21,7 @@ CLI_DEFAULTS_CENTOS = dict( vhost_root="/etc/httpd/conf.d", ctl="apachectl", version_cmd="apachectl -v", + define_cmd="apachectl -t -D DUMP_RUN_CFG", enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", @@ -32,6 +34,7 @@ CLI_DEFAULTS_GENTOO = dict( vhost_root="/etc/apache2/vhosts.d", ctl="apache2ctl", version_cmd="/usr/sbin/apache2 -v", + define_cmd="/usr/sbin/apache2 -t -D DUMP_RUN_CFG", enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index c9ab438ca..19eb566c4 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -8,6 +8,7 @@ import subprocess from letsencrypt import errors +from letsencrypt_apache import constants logger = logging.getLogger(__name__) @@ -108,7 +109,7 @@ class ApacheParser(object): for match in matches: if match.count("=") > 1: logger.error("Unexpected number of equal signs in " - "apache2ctl -D DUMP_RUN_CFG") + "runtime config dump.") raise errors.PluginError( "Error parsing Apache runtime variables") parts = match.partition("=") @@ -124,7 +125,7 @@ class ApacheParser(object): """ try: proc = subprocess.Popen( - [ctl, "-t", "-D", "DUMP_RUN_CFG"], + constants.os_constant("define_cmd").split(" "), stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() From 67c0c454b4d7381f42bff3677b819818151094ac Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 22 Dec 2015 13:12:11 +0200 Subject: [PATCH 109/159] Fixed bug in bootstrapping script --- bootstrap/_deb_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index aadacba0a..227a2a9e3 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -35,7 +35,7 @@ fi augeas_pkg=libaugeas0 AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` -if dpkg --compare-version 1.0 gt "$AUGVERSION" ; then +if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then if lsb_release -a | grep -q wheezy ; then if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q wheezy-backports ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. From 092b906dee9d51f9762d16a4497a3beaf279b057 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 11:20:42 -0800 Subject: [PATCH 110/159] Fix the prettyprinted note --- bootstrap/_deb_common.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index 227a2a9e3..d6487381e 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -40,13 +40,13 @@ if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q wheezy-backports ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. if ! grep -v -e ' *#' /etc/apt/sources.list.d/* | grep -q wheezy-backports 2>/dev/null ; then - echo -n "Installing libaugeas0 from wheezy-backports in 3 seconds..." + /bin/echo -n "Installing augeas from wheezy-backports in 3 seconds..." sleep 1s - echo -e "\e[0K\rInstalling libaugeas0 from wheezy-backports in 2 seconds..." + /bin/echo -ne "\e[0K\rInstalling augeas from wheezy-backports in 2 seconds..." sleep 1s - echo -e "\e[0K\rInstalling libaugeas0 from wheezy-backports in 1 second ..." + /bin/echo -ne "\e[0K\rInstalling augeas from wheezy-backports in 1 second ..." sleep 1s - echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' + /bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' echo deb http://http.debian.net/debian wheezy-backports main >> /etc/apt/sources.list.d/wheezy-backports.list apt-get update From eaa6a51f0fa8e031fb6894059a877fe06884ae37 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 11:23:36 -0800 Subject: [PATCH 111/159] A different kind of silence --- bootstrap/_deb_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index d6487381e..3c33e9beb 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -39,7 +39,7 @@ if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then if lsb_release -a | grep -q wheezy ; then if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q wheezy-backports ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. - if ! grep -v -e ' *#' /etc/apt/sources.list.d/* | grep -q wheezy-backports 2>/dev/null ; then + if ! grep -v -e ' *#' /etc/apt/sources.list.d/* | grep -q wheezy-backports >/dev/null ; then /bin/echo -n "Installing augeas from wheezy-backports in 3 seconds..." sleep 1s /bin/echo -ne "\e[0K\rInstalling augeas from wheezy-backports in 2 seconds..." From 28fef227ebb25c8a08baee32ae2d18b96a935a60 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 11:26:36 -0800 Subject: [PATCH 112/159] Final tweaks And a third kind of silence --- bootstrap/_deb_common.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index 3c33e9beb..6f9d41c5d 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -39,12 +39,12 @@ if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then if lsb_release -a | grep -q wheezy ; then if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q wheezy-backports ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. - if ! grep -v -e ' *#' /etc/apt/sources.list.d/* | grep -q wheezy-backports >/dev/null ; then + if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q wheezy-backports ; then /bin/echo -n "Installing augeas from wheezy-backports in 3 seconds..." sleep 1s /bin/echo -ne "\e[0K\rInstalling augeas from wheezy-backports in 2 seconds..." sleep 1s - /bin/echo -ne "\e[0K\rInstalling augeas from wheezy-backports in 1 second ..." + /bin/echo -e "\e[0K\rInstalling augeas from wheezy-backports in 1 second ..." sleep 1s /bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' From bccff905db5b29bbe346d1669b376e750770001f Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Tue, 22 Dec 2015 22:14:53 +0000 Subject: [PATCH 113/159] Add passing test for quote inside RewriteRule Already fixed recently by commit a72e498. Closes: #1960 --- tests/apache-conf-files/passing/rewrite-quote-1960.conf | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/apache-conf-files/passing/rewrite-quote-1960.conf diff --git a/tests/apache-conf-files/passing/rewrite-quote-1960.conf b/tests/apache-conf-files/passing/rewrite-quote-1960.conf new file mode 100644 index 000000000..26214e7b0 --- /dev/null +++ b/tests/apache-conf-files/passing/rewrite-quote-1960.conf @@ -0,0 +1,7 @@ + + RewriteEngine On + RewriteCond %{REQUEST_URI} ^.*(,|;|:|<|>|">|"<|/|\\\.\.\\).* [NC,OR] + RewriteCond %{REQUEST_URI} ^.*(\=|\@|\[|\]|\^|\`|\{|\}|\~).* [NC,OR] + RewriteCond %{REQUEST_URI} ^.*(\'|%0A|%0D|%27|%3C|%3E|%00).* [NC] + RewriteRule ^(.*)$ - [F,L] + From f5cf58f42ef0704a9b4ddf122310527764d727ba Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 15:42:53 -0800 Subject: [PATCH 114/159] with .. open .. as # definitely nicer --- letsencrypt/plugins/webroot_test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 137a2673e..defe9396b 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -67,9 +67,8 @@ class AuthenticatorTest(unittest.TestCase): def test_prepare_reraises_other_errors(self): self.auth.full_path = os.path.join(self.path, "null") permission_canary = os.path.join(self.path, "rnd") - f = open(permission_canary, "w") - f.write("thingimy") - f.close() + with open(permission_canary, "w") as f: + f.write("thingimy") os.chmod(self.path, 0o000) try: open(permission_canary, "r") From e41ddd2cc7210563506bb5107841cfb76c27c6e2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 15:50:48 -0800 Subject: [PATCH 115/159] Rename hackishapachetest -> apacheconftest Reenable other travis tests as well as this one --- .travis.yml | 10 +++++----- .../{hackish-apache-test => apache-conf-test} | 0 tox.ini | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) rename letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/{hackish-apache-test => apache-conf-test} (100%) diff --git a/.travis.yml b/.travis.yml index ab1931be1..9efb95165 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,11 +19,11 @@ env: - GOPATH=/tmp/go - PATH=$GOPATH/bin:$PATH matrix: - - TOXENV=hackishapachetest -# - TOXENV=py26 BOULDER_INTEGRATION=1 -# - TOXENV=py27 BOULDER_INTEGRATION=1 -# - TOXENV=lint -# - TOXENV=cover + - TOXENV=py26 BOULDER_INTEGRATION=1 + - TOXENV=py27 BOULDER_INTEGRATION=1 + - TOXENV=lint + - TOXENV=cover + - TOXENV=apacheconftest # Only build pushes to the master branch, PRs, and branches beginning with diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test diff --git a/tox.ini b/tox.ini index abb934055..aac7c15eb 100644 --- a/tox.ini +++ b/tox.ini @@ -68,10 +68,10 @@ commands = pylint --rcfile=.pylintrc letsencrypt-compatibility-test/letsencrypt_compatibility_test pylint --rcfile=.pylintrc letshelp-letsencrypt/letshelp_letsencrypt -[testenv:hackishapachetest] +[testenv:apacheconftest] #basepython = python2.7 setenv = - LETSENCRYPT=/home/travis/build/letsencrypt/letsencrypt/.tox/hackishapachetest/bin/letsencrypt + LETSENCRYPT=/home/travis/build/letsencrypt/letsencrypt/.tox/apacheconftest/bin/letsencrypt commands = pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt - sudo ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/hackish-apache-test --debian-modules + sudo ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test --debian-modules From ef1973ae2888232a5b19d2c5dae4783506ccf83c Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 15:58:09 -0800 Subject: [PATCH 116/159] Move recently included tests to the new apache-conf-test location --- .../tests}/apache-conf-files/passing/graphite-quote-1934.conf | 0 .../tests}/apache-conf-files/passing/rewrite-quote-1960.conf | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/graphite-quote-1934.conf (100%) rename {tests => letsencrypt-apache/letsencrypt_apache/tests}/apache-conf-files/passing/rewrite-quote-1960.conf (100%) diff --git a/tests/apache-conf-files/passing/graphite-quote-1934.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf similarity index 100% rename from tests/apache-conf-files/passing/graphite-quote-1934.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf diff --git a/tests/apache-conf-files/passing/rewrite-quote-1960.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf similarity index 100% rename from tests/apache-conf-files/passing/rewrite-quote-1960.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf From d9ea151fbb2c54e5dc390b23590857de7f02cf73 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 16:04:30 -0800 Subject: [PATCH 117/159] Tweak the graphite config file so that apacheconftest passes again (At least on debian and other systems that have a www-data user...) --- .../tests/apache-conf-files/passing/graphite-quote-1934.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf index 2a8734b43..f257dd9a8 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf @@ -1,6 +1,6 @@ - WSGIDaemonProcess _graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120 user=_graphite group=_graphite + WSGIDaemonProcess _graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120 user=www-data group=www-data WSGIProcessGroup _graphite WSGIImportScript /usr/share/graphite-web/graphite.wsgi process-group=_graphite application-group=%{GLOBAL} WSGIScriptAlias / /usr/share/graphite-web/graphite.wsgi From 23e8d6c641dcb2d108583232e437f58d9cb1c6f4 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 16:57:08 -0800 Subject: [PATCH 118/159] When testing apache2, don't use letsencrypt-auto --- scripts/test_apache2.sh | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index a048a1ad0..6c8dec365 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -36,7 +36,20 @@ fi # run letsencrypt-apache2 via letsencrypt-auto cd letsencrypt -./letsencrypt-auto -v --debug --text --agree-dev-preview --agree-tos \ + +export SUDO=sudo +if [ -f /etc/debian_version ] ; then + echo "Bootstrapping dependencies for Debian-based OSes..." + $SUDO bootstrap/_deb_common.sh +elif [ -f /etc/redhat-release ] ; then + echo "Bootstrapping dependencies for RedHat-based OSes..." + $SUDO bootstrap/_rpm_common.sh +else + echo "Dont have bootstrapping for this OS!" + exit 1 +fi + +venv/bin/letsencrypt -v --debug --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL if [ $? -ne 0 ] ; then From 237472c361a49caf951de730c85612d62a76fd1b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 17:09:45 -0800 Subject: [PATCH 119/159] Do things in the correct and new-fashioned way --- scripts/test_apache2.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index 6c8dec365..7507bb47b 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -58,7 +58,7 @@ fi if [ "$OS_TYPE" = "ubuntu" ] ; then export LETSENCRYPT="$HOME/.local/share/letsencrypt/bin/letsencrypt" - tests/apache-conf-files/hackish-apache-test --debian-modules + venv/bin/tox apacheconftest else echo Not running hackish apache tests on $OS_TYPE fi @@ -69,5 +69,5 @@ fi # return error if any of the subtests failed if [ "$FAIL" = 1 ] ; then - return 1 + exit 1 fi From 068504ddbf2d4943d4254e8bf0a8904cd347f7cd Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 17:19:01 -0800 Subject: [PATCH 120/159] Correct comment --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9efb95165..4e0849e3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ addons: - openssl # For Boulder integration testing - rsyslog - # for hackishapachetest + # for apacheconftest - realpath - apache2 - libapache2-mod-wsgi From 91f53dc8dbf971bbc8624a5ae0677d164aeaa0b8 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 22 Dec 2015 18:57:37 -0800 Subject: [PATCH 121/159] Fix various bugs --- multitester.py | 2 +- scripts/test_apache2.sh | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/multitester.py b/multitester.py index fb29ab9eb..a9b766913 100644 --- a/multitester.py +++ b/multitester.py @@ -75,7 +75,7 @@ parser.add_argument('--saveinstances', action='store_true', help="don't kill EC2 instances after run, useful for debugging") parser.add_argument('--alt_pip', - default='https://certainly.isnot.org/pip/', + default='', help="server from which to pull candidate release packages") cl_args = parser.parse_args() diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index 7507bb47b..583f1f911 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -49,7 +49,8 @@ else exit 1 fi -venv/bin/letsencrypt -v --debug --text --agree-dev-preview --agree-tos \ +bootstrap/dev/venv.sh +sudo venv/bin/letsencrypt -v --debug --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL if [ $? -ne 0 ] ; then @@ -58,7 +59,7 @@ fi if [ "$OS_TYPE" = "ubuntu" ] ; then export LETSENCRYPT="$HOME/.local/share/letsencrypt/bin/letsencrypt" - venv/bin/tox apacheconftest + venv/bin/tox -e apacheconftest else echo Not running hackish apache tests on $OS_TYPE fi From ebfe1254ea11112689fa606cd6c29100a26e058d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20HUBSCHER?= Date: Sun, 20 Dec 2015 16:23:19 +0100 Subject: [PATCH 122/159] Update the ACME github repository URL. --- acme/acme/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acme/acme/__init__.py b/acme/acme/__init__.py index c38cea414..0f5f0e4bd 100644 --- a/acme/acme/__init__.py +++ b/acme/acme/__init__.py @@ -1,12 +1,12 @@ """ACME protocol implementation. This module is an implementation of the `ACME protocol`_. Latest -supported version: `v02`_. +supported version: `draft-ietf-acme-01`_. -.. _`ACME protocol`: https://github.com/letsencrypt/acme-spec +.. _`ACME protocol`: https://github.com/ietf-wg-acme/acme/ -.. _`v02`: - https://github.com/letsencrypt/acme-spec/commit/d328fea2d507deb9822793c512830d827a4150c4 +.. _`draft-ietf-acme-01`: + https://github.com/ietf-wg-acme/acme/tree/draft-ietf-acme-acme-01 """ From 4156d1ceccc3ccad2375ea1f6f9b017bfa705986 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 23 Dec 2015 12:28:57 -0500 Subject: [PATCH 123/159] Ignore log directories and key files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index ba843d9cc..1becea3b4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ letsencrypt.log # auth --cert-path --chain-path /*.pem + +# letstest +tests/letstest/letest-*/ +tests/letstest/*.pem From 2366b29755159195ed941c880d54bd6711079158 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Wed, 23 Dec 2015 20:07:15 +0200 Subject: [PATCH 124/159] Fix the flow let #1961 and #1811 coexist --- .../letsencrypt_apache/configurator.py | 20 +++++++++++++++---- .../letsencrypt_apache/parser.py | 7 +++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 221baea2b..90f60da15 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -556,10 +556,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str port: Port to listen on """ - if "ssl_module" not in self.parser.modules: - self.enable_mod("ssl", temp=temp) - if self.version >= (2, 4) and "socache_shmcb_module" not in self.parser.modules: - self.enable_mod("socache_shmcb", temp=temp) + + self.prepare_https_modules(temp) # Check for Listen # Note: This could be made to also look for ip:443 combo listens = [self.parser.get_arg(x).split()[0] for x in self.parser.find_dir("Listen")] @@ -600,6 +598,20 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): ip, port, self.parser.loc["listen"]) listens.append("%s:%s" % (ip, port)) + def prepare_https_modules(self, temp): + """Helper method for prepare_server_https, taking care of enabling + needed modules + + :param boolean temp: If the change is temporary + """ + + if self.conf("handle-modules"): + if "ssl_module" not in self.parser.modules: + self.enable_mod("ssl", temp=temp) + if self.version >= (2, 4) and ("socache_shmcb_module" not in + self.parser.modules): + self.enable_mod("socache_shmcb", temp=temp) + def make_addrs_sni_ready(self, addrs): """Checks to see if the server is ready for SNI challenges. diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index bd21b3a5b..b1d604631 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -36,8 +36,8 @@ class ApacheParser(object): # https://httpd.apache.org/docs/2.4/mod/core.html#ifdefine # This only handles invocation parameters and Define directives! self.variables = {} - self.unparsable = False - self.update_runtime_variables(ctl) + if version >= (2, 4): + self.update_runtime_variables(ctl) self.aug = aug # Find configuration root and make sure augeas can parse it. @@ -63,7 +63,7 @@ class ApacheParser(object): self._parse_file(self.vhostroot + "/*.conf") #check to see if there were unparsed define statements - if self.unparsable: + if version < (2, 4): if self.find_dir("Define", exclude=False): raise errors.PluginError("Error parsing runtime variables") @@ -108,7 +108,6 @@ class ApacheParser(object): try: matches.remove("DUMP_RUN_CFG") except ValueError: - self.unparsable = True return for match in matches: From c29c6c96ae0c2ede9b29469203ed27b6d8518187 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Wed, 23 Dec 2015 20:08:44 +0200 Subject: [PATCH 125/159] Fix tests to complete the merge --- .../tests/configurator_test.py | 6 +++--- .../letsencrypt_apache/tests/parser_test.py | 17 +++++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index 4e0eb6b86..218c085f9 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -27,7 +27,7 @@ class TwoVhost80Test(util.ApacheTest): super(TwoVhost80Test, self).setUp() self.config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir) + self.config_path, self.vhost_path, self.config_dir, self.work_dir) self.config = self.mock_deploy_cert(self.config) self.vh_truth = util.get_vh_truth( self.temp_dir, "debian_apache_2_4/two_vhost_80") @@ -311,7 +311,7 @@ class TwoVhost80Test(util.ApacheTest): def test_deploy_cert_newssl_no_fullchain(self): self.config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir, version=(2, 4, 16)) + self.config_path, self.vhost_path, self.config_dir, self.work_dir, version=(2, 4, 16)) self.config = self.mock_deploy_cert(self.config) self.config.parser.modules.add("ssl_module") @@ -325,7 +325,7 @@ class TwoVhost80Test(util.ApacheTest): def test_deploy_cert_old_apache_no_chain(self): self.config = util.get_apache_configurator( - self.config_path, self.config_dir, self.work_dir, version=(2, 4, 7)) + self.config_path, self.vhost_path, self.config_dir, self.work_dir, version=(2, 4, 7)) self.config = self.mock_deploy_cert(self.config) self.config.parser.modules.add("ssl_module") diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py index ffbae107d..023b3990a 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py @@ -152,7 +152,6 @@ class BasicParserTest(util.ParserTest): def test_update_runtime_vars_bad_output(self, mock_cfg): mock_cfg.return_value = "Define: TLS=443=24" self.parser.update_runtime_variables("ctl") - self.assertTrue(self.parser.unparsable) mock_cfg.return_value = "Define: DUMP_RUN_CFG\nDefine: TLS=443=24" self.assertRaises( @@ -188,17 +187,11 @@ class ParserInitTest(util.ApacheTest): @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") def test_unparsable(self, mock_cfg): from letsencrypt_apache.parser import ApacheParser - def unparsable_true(self, arg): - """a helper to set the self unparsabale to true""" - print "side effect has passed in arg: %s", arg - self.unparsable = True - with mock.patch.object(ApacheParser, 'update_runtime_variables', autospec=True) as urv: - urv.side_effect = unparsable_true - mock_cfg.return_value = ('Define: TEST') - self.assertRaises( - errors.PluginError, - ApacheParser, self.aug, os.path.relpath(self.config_path), "ctl") - self.assertEquals(1, 1) + mock_cfg.return_value = ('Define: TEST') + self.assertRaises( + errors.PluginError, + ApacheParser, self.aug, os.path.relpath(self.config_path), + "/dummy/vhostpath", "ctl", version=(2, 2, 22)) def test_root_normalized(self): from letsencrypt_apache.parser import ApacheParser From 70cc516ed817c02cc9fe4eef64d06acfd6735861 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Dec 2015 10:38:57 -0800 Subject: [PATCH 126/159] Avoid scrollback for investigating logs --- multitester.py | 1 + 1 file changed, 1 insertion(+) diff --git a/multitester.py b/multitester.py index a9b766913..5aca79b7a 100644 --- a/multitester.py +++ b/multitester.py @@ -478,6 +478,7 @@ for outq in outputs: results_file.close() if not cl_args.saveinstances: + print('Logs in ', LOGDIR) print('Terminating EC2 Instances and Cleaning Dangling EBS Volumes') boulder_server.terminate() terminate_and_clean(instances) From 6db40626196c790af20e5834f5b85432dd358cb8 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Dec 2015 10:45:08 -0800 Subject: [PATCH 127/159] Split module installation into substeps - This may prevent failures if one thing is uninstallable --- .../tests/apache-conf-files/apache-conf-test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test index c661e6435..38d5f05c7 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test @@ -48,7 +48,8 @@ function Cleanup() { # if our environment asks us to enable modules, do our best! if [ "$1" = --debian-modules ] ; then - sudo apt-get install -y libapache2-mod-{wsgi,macro} + sudo apt-get install -y libapache2-mod-wsgi + sudo apt-get install -y libapache2-mod-macro for mod in ssl rewrite macro wsgi deflate userdir version ; do sudo a2enmod $mod From 494e6e77110c6af028df31ad270bfa70d1658ad2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Dec 2015 10:53:13 -0800 Subject: [PATCH 128/159] Remove TODO that has been done --- .../letsencrypt_apache/tests/apache-conf-files/apache-conf-test | 2 -- 1 file changed, 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test index 38d5f05c7..ec0041534 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test @@ -3,8 +3,6 @@ # A hackish script to see if the client is behaving as expected # with each of the "passing" conf files. -# TODO presently this requires interaction and human judgement to -# assess, but it should be automated export EA=/etc/apache2/ TESTDIR="`dirname $0`" LEROOT="`realpath \"$TESTDIR/../../../../\"`" From 3dc3df4b345ee45d3b5d28a71fb1379d2e064446 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Dec 2015 10:58:28 -0800 Subject: [PATCH 129/159] Document the inclusion of apacheconftest in tox --- docs/contributing.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index c71aefeec..6c70830b8 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -65,8 +65,14 @@ Testing The following tools are there to help you: -- ``tox`` starts a full set of tests. Please make sure you run it - before submitting a new pull request. +- ``tox`` starts a full set of tests. Please note that it includes + apacheconftest, which uses the system's Apache install to test config file + parsing, so it should only be run on systems that have an + experimental, non-production Apache2 install on them. ``tox -e + apacheconftest`` can be used to run those specific Apache conf tests. + +- ``tox -e py27``, ``tox -e py26`` etc, run unit tests for specific Python + versions. - ``tox -e cover`` checks the test coverage only. Calling the ``./tox.cover.sh`` script directly (or even ``./tox.cover.sh $pkg1 From 8b50274d8821becfab40b53bb379a7f350ea757b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Dec 2015 11:33:39 -0800 Subject: [PATCH 130/159] --hsts should not use includeSubDomains Fixes #1728 --- letsencrypt-apache/letsencrypt_apache/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index eb004b975..4944ded1f 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -33,7 +33,7 @@ REWRITE_HTTPS_ARGS_WITH_END = [ https vhost""" HSTS_ARGS = ["always", "set", "Strict-Transport-Security", - "\"max-age=31536000; includeSubDomains\""] + "\"max-age=31536000\""] """Apache header arguments for HSTS""" UIR_ARGS = ["always", "set", "Content-Security-Policy", From 263f6d64292181de38ee8e45918fb71de358977b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Dec 2015 12:26:22 -0800 Subject: [PATCH 131/159] We don't want to hardcode a letsencrypt-auto venv anymore --- scripts/test_apache2.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index 583f1f911..3f646aef0 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -58,7 +58,6 @@ if [ $? -ne 0 ] ; then fi if [ "$OS_TYPE" = "ubuntu" ] ; then - export LETSENCRYPT="$HOME/.local/share/letsencrypt/bin/letsencrypt" venv/bin/tox -e apacheconftest else echo Not running hackish apache tests on $OS_TYPE From 8f844928b769fff68a8e2ea873c276a53831f657 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Dec 2015 14:42:26 -0800 Subject: [PATCH 132/159] Make sure we install realpath (some systems don't have it :/) --- scripts/test_apache2.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test_apache2.sh b/scripts/test_apache2.sh index 3f646aef0..4032e2195 100755 --- a/scripts/test_apache2.sh +++ b/scripts/test_apache2.sh @@ -8,6 +8,7 @@ then CONFFILE=/etc/apache2/sites-available/000-default.conf sudo apt-get update sudo apt-get -y --no-upgrade install apache2 #curl + sudo apt-get -y install realpath # needed for test-apache-conf # For apache 2.4, set up ServerName sudo sed -i '/ServerName/ s/#ServerName/ServerName/' $CONFFILE sudo sed -i '/ServerName/ s/www.example.com/'$PUBLIC_HOSTNAME'/' $CONFFILE From ce9e3c1f94213d1c2158664436505e22e732e216 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Dec 2015 15:00:07 -0800 Subject: [PATCH 133/159] Some OSes don't enable the mime module by default? --- .../letsencrypt_apache/tests/apache-conf-files/apache-conf-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test index ec0041534..4e0443bb7 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test @@ -49,7 +49,7 @@ if [ "$1" = --debian-modules ] ; then sudo apt-get install -y libapache2-mod-wsgi sudo apt-get install -y libapache2-mod-macro - for mod in ssl rewrite macro wsgi deflate userdir version ; do + for mod in ssl rewrite macro wsgi deflate userdir version mime ; do sudo a2enmod $mod done fi From 6a026597f4e7961bbe8557f17c823a39cd718d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20HUBSCHER?= Date: Thu, 24 Dec 2015 00:30:51 +0100 Subject: [PATCH 134/159] =?UTF-8?q?Move=20validator=20to=20compatibility-t?= =?UTF-8?q?est=20=E2=80=94=20Refs=20#1997?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_driver.py | 3 +- .../validator.py | 0 .../validator_test.py | 35 ++++++++++--------- letsencrypt-compatibility-test/setup.py | 6 ++++ setup.py | 6 ---- 5 files changed, 27 insertions(+), 23 deletions(-) rename {letsencrypt => letsencrypt-compatibility-test/letsencrypt_compatibility_test}/validator.py (100%) rename {letsencrypt/tests => letsencrypt-compatibility-test/letsencrypt_compatibility_test}/validator_test.py (77%) diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py index 5765003b9..ee679bdb7 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py +++ b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py @@ -15,11 +15,12 @@ from acme import crypto_util from acme import messages from letsencrypt import achallenges from letsencrypt import errors as le_errors -from letsencrypt import validator from letsencrypt.tests import acme_util from letsencrypt_compatibility_test import errors from letsencrypt_compatibility_test import util +from letsencrypt_compatibility_test import validator + from letsencrypt_compatibility_test.configurators.apache import apache24 diff --git a/letsencrypt/validator.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator.py similarity index 100% rename from letsencrypt/validator.py rename to letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator.py diff --git a/letsencrypt/tests/validator_test.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py similarity index 77% rename from letsencrypt/tests/validator_test.py rename to letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py index c7416dc46..3a3bbc4b2 100644 --- a/letsencrypt/tests/validator_test.py +++ b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.validator.""" +"""Tests for letsencrypt_compatibility_test.validator.""" import requests import unittest @@ -6,28 +6,31 @@ import mock import OpenSSL from acme import errors as acme_errors -from letsencrypt import validator +from letsencrypt_compatibility_test import validator class ValidatorTest(unittest.TestCase): def setUp(self): self.validator = validator.Validator() - @mock.patch("letsencrypt.validator.crypto_util.probe_sni") + @mock.patch( + "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_success(self, mock_probe_sni): cert = OpenSSL.crypto.X509() mock_probe_sni.return_value = cert self.assertTrue(self.validator.certificate( cert, "test.com", "127.0.0.1")) - @mock.patch("letsencrypt.validator.crypto_util.probe_sni") + @mock.patch( + "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_error(self, mock_probe_sni): cert = OpenSSL.crypto.X509() mock_probe_sni.side_effect = [acme_errors.Error] self.assertFalse(self.validator.certificate( cert, "test.com", "127.0.0.1")) - @mock.patch("letsencrypt.validator.crypto_util.probe_sni") + @mock.patch( + "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_failure(self, mock_probe_sni): cert = OpenSSL.crypto.X509() cert.set_serial_number(1337) @@ -35,67 +38,67 @@ class ValidatorTest(unittest.TestCase): self.assertFalse(self.validator.certificate( cert, "test.com", "127.0.0.1")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_succesful_redirect(self, mock_get_request): mock_get_request.return_value = create_response( 301, {"location": "https://test.com"}) self.assertTrue(self.validator.redirect("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_redirect_with_headers(self, mock_get_request): mock_get_request.return_value = create_response( 301, {"location": "https://test.com"}) self.assertTrue(self.validator.redirect( "test.com", headers={"Host": "test.com"})) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_redirect_missing_location(self, mock_get_request): mock_get_request.return_value = create_response(301) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_redirect_wrong_status_code(self, mock_get_request): mock_get_request.return_value = create_response( 201, {"location": "https://test.com"}) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_redirect_wrong_redirect_code(self, mock_get_request): mock_get_request.return_value = create_response( 303, {"location": "https://test.com"}) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_hsts_empty(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": ""}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_hsts_malformed(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "sdfal"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_hsts_bad_max_age(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=not-an-int"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_hsts_expire(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=3600"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_hsts(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=31536000"}) self.assertTrue(self.validator.hsts("test.com")) - @mock.patch("letsencrypt.validator.requests.get") + @mock.patch("letsencrypt_compatibility_test.validator.requests.get") def test_hsts_include_subdomains(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index eb7e23036..1ff9e7649 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -10,6 +10,7 @@ install_requires = [ 'letsencrypt=={0}'.format(version), 'letsencrypt-apache=={0}'.format(version), 'docker-py', + 'requests', 'zope.interface', ] @@ -18,6 +19,11 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') +if sys.version_info < (2, 7, 9): + # For secure SSL connexion with Python 2.7 (InsecurePlatformWarning) + install_requires.append('ndg-httpsclient') + install_requires.append('pyasn1') + docs_extras = [ 'repoze.sphinx.autointerface', 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags diff --git a/setup.py b/setup.py index ae36777ef..f95f672ff 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,6 @@ install_requires = [ 'pyrfc3339', 'python2-pythondialog>=3.2.2rc1', # Debian squeeze support, cf. #280 'pytz', - 'requests', 'setuptools', # pkg_resources 'six', 'zope.component', @@ -61,11 +60,6 @@ else: 'mock', ]) -if sys.version_info < (2, 7, 9): - # For secure SSL connexion with Python 2.7 (InsecurePlatformWarning) - install_requires.append('ndg-httpsclient') - install_requires.append('pyasn1') - dev_extras = [ # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 'astroid==1.3.5', From c271dc58ce4efb884666217a182d2785a9a4bf42 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 24 Dec 2015 20:55:44 -0800 Subject: [PATCH 135/159] Fix the letsencrypt-auto update script --- tests/letstest/scripts/test_leauto_upgrades.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/letstest/scripts/test_leauto_upgrades.sh b/tests/letstest/scripts/test_leauto_upgrades.sh index 70f8a2293..b7849755a 100755 --- a/tests/letstest/scripts/test_leauto_upgrades.sh +++ b/tests/letstest/scripts/test_leauto_upgrades.sh @@ -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" From dc5c0933df775a49d93e0401cfc7306127fcb733 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 24 Dec 2015 21:39:28 -0800 Subject: [PATCH 136/159] Definitely need --debug for this --- tests/letstest/scripts/test_letsencrypt_auto_venv_only.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/letstest/scripts/test_letsencrypt_auto_venv_only.sh b/tests/letstest/scripts/test_letsencrypt_auto_venv_only.sh index 476ad8bde..234e70f68 100755 --- a/tests/letstest/scripts/test_letsencrypt_auto_venv_only.sh +++ b/tests/letstest/scripts/test_letsencrypt_auto_venv_only.sh @@ -4,4 +4,4 @@ cd letsencrypt # help installs virtualenv and does nothing else -./letsencrypt-auto -v --help all +./letsencrypt-auto -v --debug --help all From c728219bc970b4a5f738c22ee2092f6d12cc9548 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 25 Dec 2015 10:18:24 +0200 Subject: [PATCH 137/159] Changed define and version commands from string to list to avoid unneeded parsing later on --- .../letsencrypt_apache/configurator.py | 2 +- letsencrypt-apache/letsencrypt_apache/constants.py | 12 ++++++------ letsencrypt-apache/letsencrypt_apache/parser.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 90f60da15..6c6685257 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -1343,7 +1343,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ try: stdout, _ = le_util.run_script( - constants.os_constant("version_cmd").split(" ")) + constants.os_constant("version_cmd")) except errors.SubprocessError: raise errors.PluginError( "Unable to run %s -v" % self.conf("ctl")) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index a859f57d4..6b248b6ad 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -7,8 +7,8 @@ CLI_DEFAULTS_DEBIAN = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/sites-available", ctl="apache2ctl", - version_cmd="apache2ctl -v", - define_cmd="apache2ctl -t -D DUMP_RUN_CFG", + version_cmd=['apache2ctl', '-v'], + define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'], enmod="a2enmod", dismod="a2dismod", le_vhost_ext="-le-ssl.conf", @@ -20,8 +20,8 @@ CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", vhost_root="/etc/httpd/conf.d", ctl="apachectl", - version_cmd="apachectl -v", - define_cmd="apachectl -t -D DUMP_RUN_CFG", + version_cmd=['apachectl', '-v'], + define_cmd=['apachectl', '-t', '-D', 'DUMP_RUN_CFG'], enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", @@ -33,8 +33,8 @@ CLI_DEFAULTS_GENTOO = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/vhosts.d", ctl="apache2ctl", - version_cmd="/usr/sbin/apache2 -v", - define_cmd="/usr/sbin/apache2 -t -D DUMP_RUN_CFG", + version_cmd=['/usr/sbin/apache2', '-v'], + define_cmd=['/usr/sbin/apache2', '-t', '-D', 'DUMP_RUN_CFG'], enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index b1d604631..12704c859 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -129,7 +129,7 @@ class ApacheParser(object): """ try: proc = subprocess.Popen( - constants.os_constant("define_cmd").split(" "), + constants.os_constant("define_cmd"), stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() From e0837ad41f1e2bee0df17ccb77948abc1767f0c9 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 25 Dec 2015 02:39:05 -0800 Subject: [PATCH 138/159] [letstest] create & reuse a persistent boulder server Reduces minimum multitester.py runtime to just a bit under 10 minutes --- tests/letstest/multitester.py | 87 ++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index 5aca79b7a..60be10447 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -77,6 +77,12 @@ 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") cl_args = parser.parse_args() # Credential Variables @@ -292,6 +298,29 @@ 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.micro' + else: + machine_type = '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 +381,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 +410,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 +510,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 From 3e1bc19e0f5e6ef417328c39c027c8e61ac50e2e Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 25 Dec 2015 10:43:52 -0800 Subject: [PATCH 139/159] Optional flag for faster AMIs --- tests/letstest/multitester.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index 60be10447..dee6968c3 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -83,6 +83,9 @@ parser.add_argument('--killboulder', 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 @@ -304,9 +307,10 @@ def create_client_instances(targetlist): print("Creating instances: ", end="") for target in targetlist: if target['virt'] == 'hvm': - machine_type = 't2.micro' + machine_type = 't2.medium' if cl_args.fast else 't2.micro' else: - machine_type = 't1.micro' + # 32 bit systems + machine_type = 'c1.medium' if cl_args.fast else 't1.micro' if 'userdata' in target.keys(): userdata = target['userdata'] else: From d6dcfa7b7fa4db97aacd0d3da5f271baa4a5b7e8 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 25 Dec 2015 13:18:19 -0800 Subject: [PATCH 140/159] Revert "Issue 2002" --- letsencrypt-apache/letsencrypt_apache/configurator.py | 1 - letsencrypt-apache/letsencrypt_apache/tls_sni_01.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 32da8b95d..39b8f2426 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -1272,7 +1272,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ self.config_test() - logger.debug(self.reverter.view_config_changes()) self._reload() def _reload(self): diff --git a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py b/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py index a770804d1..4284e240c 100644 --- a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py +++ b/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py @@ -1,14 +1,12 @@ """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 @@ -106,7 +104,6 @@ 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) From f20cd73e8dc1e16ecff9d1161ec73076c05f836d Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sat, 26 Dec 2015 20:13:45 -0800 Subject: [PATCH 141/159] For now, disable apacheconftest in travis :( --- .travis.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4e0849e3b..a5d6d8a85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,8 @@ language: python services: - rabbitmq - mariadb - - apache2 + # apacheconftest + #- apache2 # http://docs.travis-ci.com/user/ci-environment/#CI-environment-OS # gimme has to be kept in sync with Boulder's Go version setting in .travis.yml @@ -23,7 +24,9 @@ env: - TOXENV=py27 BOULDER_INTEGRATION=1 - TOXENV=lint - TOXENV=cover - - TOXENV=apacheconftest +# Disabled for now due to requiring sudo -> causing more boulder integration +# DNS timeouts :( +# - TOXENV=apacheconftest # Only build pushes to the master branch, PRs, and branches beginning with @@ -35,7 +38,7 @@ branches: - /^test-.*$/ # container-based infrastructure -sudo: true +sudo: false addons: # make sure simplehttp simple verification works (custom /etc/hosts) @@ -61,11 +64,11 @@ addons: # For Boulder integration testing - rsyslog # for apacheconftest - - realpath - - apache2 - - libapache2-mod-wsgi - - libapache2-mod-macro - - sudo + #- realpath + #- apache2 + #- libapache2-mod-wsgi + #- libapache2-mod-macro + #- sudo install: "travis_retry pip install tox coveralls" script: 'travis_retry tox && ([ "xxx$BOULDER_INTEGRATION" = "xxx" ] || ./tests/travis-integration.sh)' From 83812dc16a081ddc43d8f4204d61f0b7e73e8768 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 28 Dec 2015 12:56:44 +0200 Subject: [PATCH 142/159] Abstract config file matching to os based constants --- letsencrypt-apache/letsencrypt_apache/constants.py | 3 +++ letsencrypt-apache/letsencrypt_apache/parser.py | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index f8712c247..800959463 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -6,6 +6,7 @@ from letsencrypt import le_util CLI_DEFAULTS_DEBIAN = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/sites-available", + vhost_files="*", ctl="apache2ctl", version_cmd=['apache2ctl', '-v'], define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'], @@ -19,6 +20,7 @@ CLI_DEFAULTS_DEBIAN = dict( CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", vhost_root="/etc/httpd/conf.d", + vhost_files="*.conf", ctl="apachectl", version_cmd=['apachectl', '-v'], define_cmd=['apachectl', '-t', '-D', 'DUMP_RUN_CFG'], @@ -32,6 +34,7 @@ CLI_DEFAULTS_CENTOS = dict( CLI_DEFAULTS_GENTOO = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/vhosts.d", + vhost_files="*.conf", ctl="apache2ctl", version_cmd=['/usr/sbin/apache2', '-v'], define_cmd=['/usr/sbin/apache2', '-t', '-D', 'DUMP_RUN_CFG'], diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 12704c859..5f84e9f52 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -60,9 +60,10 @@ class ApacheParser(object): self.loc.update(self._set_locations()) # Must also attempt to parse virtual host root - self._parse_file(self.vhostroot + "/*.conf") + self._parse_file(self.vhostroot + "/" + + constants.os_constant("vhost_files")) - #check to see if there were unparsed define statements + # check to see if there were unparsed define statements if version < (2, 4): if self.find_dir("Define", exclude=False): raise errors.PluginError("Error parsing runtime variables") From e3358bb15346cbcedd229c37d072c05f2198cf4a Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 28 Dec 2015 13:47:14 +0200 Subject: [PATCH 143/159] Abstract the remaining commands to configurable ones --- .../letsencrypt_apache/configurator.py | 22 ++++++++----------- .../letsencrypt_apache/constants.py | 9 +++++--- .../letsencrypt_apache/parser.py | 16 ++++++++------ 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 6c6685257..5e2156edc 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -86,10 +86,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): @classmethod def add_parser_arguments(cls, add): - add("ctl", default=constants.os_constant("ctl"), - help="Path to the 'apache2ctl' binary, used for 'configtest', " - "retrieving the Apache2 version number, and initialization " - "parameters.") add("enmod", default=constants.os_constant("enmod"), help="Path to the Apache 'a2enmod' binary.") add("dismod", default=constants.os_constant("dismod"), @@ -148,10 +144,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ # Verify Apache is installed - for exe in (self.conf("ctl"), self.conf("enmod"), self.conf("dismod")): - if exe is not None: - if not le_util.exe_exists(exe): - raise errors.NoInstallationError + for exe in constants.os_constant("restart_cmd")[0]: + if not le_util.exe_exists(exe): + raise errors.NoInstallationError # Make sure configuration is valid self.config_test() @@ -165,7 +160,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.parser = parser.ApacheParser( self.aug, self.conf("server-root"), self.conf("vhost-root"), - self.conf("ctl"), self.version) + self.version) # Check for errors in parsing files with Augeas self.check_parsing_errors("httpd.aug") @@ -1277,7 +1272,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Modules can enable additional config files. Variables may be defined # within these new configuration sections. # Reload is not necessary as DUMP_RUN_CFG uses latest config. - self.parser.update_runtime_variables(self.conf("ctl")) + self.parser.update_runtime_variables() def _add_parser_mod(self, mod_name): """Shortcut for updating parser modules.""" @@ -1315,7 +1310,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ try: - le_util.run_script([self.conf("ctl"), "graceful"]) + le_util.run_script(constants.os_constant("restart_cmd")) except errors.SubprocessError as err: raise errors.MisconfigurationError(str(err)) @@ -1326,7 +1321,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ try: - le_util.run_script([self.conf("ctl"), "configtest"]) + le_util.run_script(constants.os_constant("conftest_cmd")) except errors.SubprocessError as err: raise errors.MisconfigurationError(str(err)) @@ -1346,7 +1341,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): constants.os_constant("version_cmd")) except errors.SubprocessError: raise errors.PluginError( - "Unable to run %s -v" % self.conf("ctl")) + "Unable to run %s -v" % + constants.os_constant("version_cmd")) regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE) matches = regex.findall(stdout) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 800959463..8ac88b197 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -7,9 +7,10 @@ CLI_DEFAULTS_DEBIAN = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/sites-available", vhost_files="*", - ctl="apache2ctl", version_cmd=['apache2ctl', '-v'], define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'], + restart_cmd=['apache2ctl', 'graceful'], + conftest_cmd=['apache2ctl', 'configtest'], enmod="a2enmod", dismod="a2dismod", le_vhost_ext="-le-ssl.conf", @@ -21,9 +22,10 @@ CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", vhost_root="/etc/httpd/conf.d", vhost_files="*.conf", - ctl="apachectl", version_cmd=['apachectl', '-v'], define_cmd=['apachectl', '-t', '-D', 'DUMP_RUN_CFG'], + restart_cmd=['apachectl', 'graceful'], + conftest_cmd=['apachectl', 'configtest'], enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", @@ -35,9 +37,10 @@ CLI_DEFAULTS_GENTOO = dict( server_root="/etc/apache2", vhost_root="/etc/apache2/vhosts.d", vhost_files="*.conf", - ctl="apache2ctl", version_cmd=['/usr/sbin/apache2', '-v'], define_cmd=['/usr/sbin/apache2', '-t', '-D', 'DUMP_RUN_CFG'], + restart_cmd=['apache2ctl', 'graceful'], + conftest_cmd=['apache2ctl', 'configtest'], enmod=None, dismod=None, le_vhost_ext="-le-ssl.conf", diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 5f84e9f52..593c807cc 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -28,7 +28,7 @@ class ApacheParser(object): arg_var_interpreter = re.compile(r"\$\{[^ \}]*}") fnmatch_chars = set(["*", "?", "\\", "[", "]"]) - def __init__(self, aug, root, vhostroot, ctl, version=(2, 4)): + def __init__(self, aug, root, vhostroot, version=(2, 4)): # Note: Order is important here. # This uses the binary, so it can be done first. @@ -37,7 +37,7 @@ class ApacheParser(object): # This only handles invocation parameters and Define directives! self.variables = {} if version >= (2, 4): - self.update_runtime_variables(ctl) + self.update_runtime_variables() self.aug = aug # Find configuration root and make sure augeas can parse it. @@ -92,7 +92,7 @@ class ApacheParser(object): self.modules.add( os.path.basename(self.get_arg(match_filename))[:-2] + "c") - def update_runtime_variables(self, ctl): + def update_runtime_variables(self): """" .. note:: Compile time variables (apache2ctl -V) are not used within the @@ -102,7 +102,7 @@ class ApacheParser(object): .. todo:: Create separate compile time variables... simply for arg_get() """ - stdout = self._get_runtime_cfg(ctl) + stdout = self._get_runtime_cfg() variables = dict() matches = re.compile(r"Define: ([^ \n]*)").findall(stdout) @@ -122,7 +122,7 @@ class ApacheParser(object): self.variables = variables - def _get_runtime_cfg(self, ctl): # pylint: disable=no-self-use + def _get_runtime_cfg(self): # pylint: disable=no-self-use """Get runtime configuration info. :returns: stdout from DUMP_RUN_CFG @@ -137,9 +137,11 @@ class ApacheParser(object): except (OSError, ValueError): logger.error( - "Error accessing %s for runtime parameters!%s", ctl, os.linesep) + "Error running command %s for runtime parameters!%s", + constants.os_constant("define_cmd"), os.linesep) raise errors.MisconfigurationError( - "Error accessing loaded Apache parameters: %s", ctl) + "Error accessing loaded Apache parameters: %s", + constants.os_constant("define_cmd")) # Small errors that do not impede if proc.returncode != 0: logger.warn("Error in checking parameter list: %s", stderr) From ab069741f2c4cf32b911180e1a166c809a144c0a Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 28 Dec 2015 14:03:50 +0200 Subject: [PATCH 144/159] Fix tests --- .../tests/constants_test.py | 9 +++++--- .../letsencrypt_apache/tests/parser_test.py | 22 ++++++++++--------- .../letsencrypt_apache/tests/util.py | 2 +- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py b/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py index 63eb5c783..289b61bb1 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py @@ -11,14 +11,17 @@ class ConstantsTest(unittest.TestCase): @mock.patch("letsencrypt.le_util.get_os_info") def test_get_debian_value(self, os_info): os_info.return_value = ('Debian', '', '') - self.assertEqual(constants.os_constant("ctl"), "apache2ctl") + self.assertEqual(constants.os_constant("vhost_root"), + "/etc/apache2/sites-available") @mock.patch("letsencrypt.le_util.get_os_info") def test_get_centos_value(self, os_info): os_info.return_value = ('CentOS Linux', '', '') - self.assertEqual(constants.os_constant("ctl"), "apachectl") + self.assertEqual(constants.os_constant("vhost_root"), + "/etc/httpd/conf.d") @mock.patch("letsencrypt.le_util.get_os_info") def test_get_default_value(self, os_info): os_info.return_value = ('Nonexistent Linux', '', '') - self.assertEqual(constants.os_constant("ctl"), "apache2ctl") + self.assertEqual(constants.os_constant("vhost_root"), + "/etc/apache2/sites-available") diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py index 023b3990a..b871f89b7 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py @@ -145,24 +145,26 @@ class BasicParserTest(util.ParserTest): expected_vars = {"TEST": "", "U_MICH": "", "TLS": "443", "example_path": "Documents/path"} - self.parser.update_runtime_variables("ctl") + self.parser.update_runtime_variables() self.assertEqual(self.parser.variables, expected_vars) @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") def test_update_runtime_vars_bad_output(self, mock_cfg): mock_cfg.return_value = "Define: TLS=443=24" - self.parser.update_runtime_variables("ctl") + self.parser.update_runtime_variables() mock_cfg.return_value = "Define: DUMP_RUN_CFG\nDefine: TLS=443=24" self.assertRaises( - errors.PluginError, self.parser.update_runtime_variables, "ctl") + errors.PluginError, self.parser.update_runtime_variables) + @mock.patch("letsencrypt_apache.constants.os_constant") @mock.patch("letsencrypt_apache.parser.subprocess.Popen") - def test_update_runtime_vars_bad_ctl(self, mock_popen): + def test_update_runtime_vars_bad_ctl(self, mock_popen, mock_const): mock_popen.side_effect = OSError + mock_const.return_value = "nonexistent" self.assertRaises( errors.MisconfigurationError, - self.parser.update_runtime_variables, "ctl") + self.parser.update_runtime_variables) @mock.patch("letsencrypt_apache.parser.subprocess.Popen") def test_update_runtime_vars_bad_exit(self, mock_popen): @@ -170,7 +172,7 @@ class BasicParserTest(util.ParserTest): mock_popen.returncode = -1 self.assertRaises( errors.MisconfigurationError, - self.parser.update_runtime_variables, "ctl") + self.parser.update_runtime_variables) class ParserInitTest(util.ApacheTest): @@ -191,7 +193,7 @@ class ParserInitTest(util.ApacheTest): self.assertRaises( errors.PluginError, ApacheParser, self.aug, os.path.relpath(self.config_path), - "/dummy/vhostpath", "ctl", version=(2, 2, 22)) + "/dummy/vhostpath", version=(2, 2, 22)) def test_root_normalized(self): from letsencrypt_apache.parser import ApacheParser @@ -203,7 +205,7 @@ class ParserInitTest(util.ApacheTest): "debian_apache_2_4/////two_vhost_80/../two_vhost_80/apache2") parser = ApacheParser(self.aug, path, - "/dummy/vhostpath", "dummy_ctl") + "/dummy/vhostpath") self.assertEqual(parser.root, self.config_path) @@ -213,7 +215,7 @@ class ParserInitTest(util.ApacheTest): "update_runtime_variables"): parser = ApacheParser( self.aug, os.path.relpath(self.config_path), - "/dummy/vhostpath", "dummy_ctl") + "/dummy/vhostpath") self.assertEqual(parser.root, self.config_path) @@ -223,7 +225,7 @@ class ParserInitTest(util.ApacheTest): "update_runtime_variables"): parser = ApacheParser( self.aug, self.config_path + os.path.sep, - "/dummy/vhostpath", "dummy_ctl") + "/dummy/vhostpath") self.assertEqual(parser.root, self.config_path) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/util.py b/letsencrypt-apache/letsencrypt_apache/tests/util.py index 95c95e6a9..798d4814b 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/util.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/util.py @@ -58,7 +58,7 @@ class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods with mock.patch("letsencrypt_apache.parser.ApacheParser." "update_runtime_variables"): self.parser = ApacheParser( - self.aug, self.config_path, self.vhost_path, "dummy_ctl_path") + self.aug, self.config_path, self.vhost_path) def get_apache_configurator( From 3fadfb5444b38e65fb60507c2592eaeac5cdde6f Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 28 Dec 2015 15:56:24 +0200 Subject: [PATCH 145/159] Fixed the find exe condition --- letsencrypt-apache/letsencrypt_apache/configurator.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 5e2156edc..836d77135 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -144,9 +144,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ # Verify Apache is installed - for exe in constants.os_constant("restart_cmd")[0]: - if not le_util.exe_exists(exe): - raise errors.NoInstallationError + if not le_util.exe_exists(constants.os_constant("restart_cmd")[0]): + raise errors.NoInstallationError # Make sure configuration is valid self.config_test() From fd4f6fb2eef3fd24d427836023918103bac08ada Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Tue, 29 Dec 2015 08:47:14 +0000 Subject: [PATCH 146/159] Use GH pages for IETF spec repo link --- acme/acme/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme/acme/__init__.py b/acme/acme/__init__.py index 0f5f0e4bd..e8a0b16a8 100644 --- a/acme/acme/__init__.py +++ b/acme/acme/__init__.py @@ -3,10 +3,10 @@ This module is an implementation of the `ACME protocol`_. Latest supported version: `draft-ietf-acme-01`_. -.. _`ACME protocol`: https://github.com/ietf-wg-acme/acme/ + +.. _`ACME protocol`: https://ietf-wg-acme.github.io/acme .. _`draft-ietf-acme-01`: https://github.com/ietf-wg-acme/acme/tree/draft-ietf-acme-acme-01 - """ From 7788799a9bdb6a67f25833ccba031799b7b6429a Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Tue, 29 Dec 2015 08:55:13 +0000 Subject: [PATCH 147/159] Staging URI in dev-cli.ini example --- examples/cli.ini | 3 --- examples/dev-cli.ini | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/cli.ini b/examples/cli.ini index 6b6b05d7d..f0c993c57 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -5,9 +5,6 @@ # Use a 4096 bit RSA key instead of 2048 rsa-key-size = 4096 -# Always use the staging/testing server -server = https://acme-staging.api.letsencrypt.org/directory - # Uncomment and update to register with the specified e-mail address # email = foo@example.com diff --git a/examples/dev-cli.ini b/examples/dev-cli.ini index be703814a..c02038ca1 100644 --- a/examples/dev-cli.ini +++ b/examples/dev-cli.ini @@ -1,3 +1,6 @@ +# Always use the staging/testing server - avoids rate limiting +server = https://acme-staging.api.letsencrypt.org/directory + # This is an example configuration file for developers config-dir = /tmp/le/conf work-dir = /tmp/le/conf From 8f984bd24f2779490bf526d47f7fbe14633fcfb1 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Fri, 1 Jan 2016 16:35:57 -0800 Subject: [PATCH 148/159] Better Nginx error handling. Raise MisconfigurationError on restart failure, so we don't attempt to continue with an authorization we know will fail. Log at debug level the config files that are about to be written out, so it's easier to debug restart failures. Fix https://github.com/letsencrypt/letsencrypt/issues/942: Error out if adding a conflicting directive. Remove unnecessarily-inserted access_log and error_log directives. These were added to make integration testing easier but are no longer needed. Incidentally this makes the plugin work with some configs where it wouldn't have worked previously. Change the semantics of add_server_directives with replace=True so only the first instance of a given directive is replaced, not all of them. This works fine with the one place in the code that calls add_server_directives with replace=True, because all of the involved directives aren't allowed to be duplicated in a given block. Make add_http_directives do inserts into the structure itself, since its needs were significantly different than the more general add_server_directives. This also allows us to narrow the scope of the `block.insert(0, directive)` hack that we inserted to work around https://trac.nginx.org/nginx/ticket/810, since it's only necessary for http blocks. --- .../letsencrypt_nginx/configurator.py | 24 ++---- letsencrypt-nginx/letsencrypt_nginx/parser.py | 81 +++++++++++++------ .../tests/configurator_test.py | 63 ++++++++------- .../letsencrypt_nginx/tests/parser_test.py | 29 ++++--- .../tests/boulder-integration.conf.sh | 6 +- 5 files changed, 115 insertions(+), 88 deletions(-) diff --git a/letsencrypt-nginx/letsencrypt_nginx/configurator.py b/letsencrypt-nginx/letsencrypt_nginx/configurator.py index aaaf43c5f..89b1145e7 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/configurator.py +++ b/letsencrypt-nginx/letsencrypt_nginx/configurator.py @@ -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.""" diff --git a/letsencrypt-nginx/letsencrypt_nginx/parser.py b/letsencrypt-nginx/letsencrypt_nginx/parser.py index 14db2f8b7..1d424f834 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/parser.py +++ b/letsencrypt-nginx/letsencrypt_nginx/parser.py @@ -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,39 @@ 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. + if location != -1 and directive[0].__str__() not in repeatable_directives: + if block[location][1] == directive[1]: + 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) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py index 56ad5110c..f6a4f7eee 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py +++ b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py @@ -65,16 +65,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): @@ -154,38 +157,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 +298,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): diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py index 2d6156429..6559a5df6 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py +++ b/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py @@ -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) diff --git a/letsencrypt-nginx/tests/boulder-integration.conf.sh b/letsencrypt-nginx/tests/boulder-integration.conf.sh index 12610d895..d77669a76 100755 --- a/letsencrypt-nginx/tests/boulder-integration.conf.sh +++ b/letsencrypt-nginx/tests/boulder-integration.conf.sh @@ -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. From dc3a2da9b11b26be54b035ac1520128e8efbfe22 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 3 Jan 2016 10:49:50 -0500 Subject: [PATCH 149/159] Fixed a typo in a comment --- acme/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme/setup.py b/acme/setup.py index ba2c88394..7314152cd 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -31,7 +31,7 @@ else: install_requires.append('mock') 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') From 0454031cce4b88fef44e3e129e879a35b49c2314 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 3 Jan 2016 14:37:08 -0500 Subject: [PATCH 150/159] Fixed a pair of typos in docstrings --- acme/acme/jose/json_util.py | 2 +- acme/acme/jose/util.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/acme/acme/jose/json_util.py b/acme/acme/jose/json_util.py index 7b95e3fce..977a06622 100644 --- a/acme/acme/jose/json_util.py +++ b/acme/acme/jose/json_util.py @@ -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 """ diff --git a/acme/acme/jose/util.py b/acme/acme/jose/util.py index ab3606efc..600077b20 100644 --- a/acme/acme/jose/util.py +++ b/acme/acme/jose/util.py @@ -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__): From e722a381978afe3faaeb265eefbfc45b9a83e35f Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 4 Jan 2016 11:18:13 -0800 Subject: [PATCH 151/159] Clarify parser check for duplicate values. --- letsencrypt-nginx/letsencrypt_nginx/parser.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/letsencrypt-nginx/letsencrypt_nginx/parser.py b/letsencrypt-nginx/letsencrypt_nginx/parser.py index 1d424f834..c60d0102a 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/parser.py +++ b/letsencrypt-nginx/letsencrypt_nginx/parser.py @@ -518,8 +518,12 @@ def _add_directive(block, directive, replace): # 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. - if location != -1 and directive[0].__str__() not in repeatable_directives: - if block[location][1] == directive[1]: + 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( From 4d3d6ff03157fe9ec77ba60e2fd59cca78ee8ce9 Mon Sep 17 00:00:00 2001 From: watercrossing Date: Tue, 5 Jan 2016 16:51:34 +0000 Subject: [PATCH 152/159] Fix for listen bug --- .../letsencrypt_apache/configurator.py | 2 ++ .../tests/configurator_test.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 836d77135..ff12055ac 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -558,6 +558,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # In case no Listens are set (which really is a broken apache config) if not listens: listens = ["80"] + if port in listens: + return for listen in listens: # For any listen statement, check if the machine also listens on Port 443. # If not, add such a listen statement. diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index 218c085f9..9838b4f52 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -461,6 +461,25 @@ class TwoVhost80Test(util.ApacheTest): self.assertEqual(mock_add_dir.call_args_list[1][0][2], ["[::1]:8080", "https"]) self.assertEqual(mock_add_dir.call_args_list[2][0][2], ["1.1.1.1:8080", "https"]) + def test_prepare_server_https_mixed_listen(self): + + mock_find = mock.Mock() + mock_find.return_value = ["test1", "test2"] + mock_get = mock.Mock() + mock_get.side_effect = ["1.2.3.4:8080", "443"] + mock_add_dir = mock.Mock() + mock_enable = mock.Mock() + + self.config.parser.find_dir = mock_find + self.config.parser.get_arg = mock_get + self.config.parser.add_dir_to_ifmodssl = mock_add_dir + self.config.enable_mod = mock_enable + + # Test Listen statements with specific ip listeed + self.config.prepare_server_https("443") + # Should only be 2 here, as the third interface already listens to the correct port + self.assertEqual(mock_add_dir.call_count, 0) + def test_make_vhost_ssl(self): ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0]) From f7fad9a4affc399a4c6953e330d59b20d44d9d2e Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 23 Dec 2015 23:25:41 -0800 Subject: [PATCH 153/159] Show error detail to the user. Previously this code would show the user a hardcoded message based on the error type, but it's much more useful to show the error detail field, which often helps debug the problem. --- letsencrypt/auth_handler.py | 7 +------ letsencrypt/tests/auth_handler_test.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 027c11158..37b71775d 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -543,13 +543,8 @@ def _generate_failed_chall_msg(failed_achalls): msg = [ "The following '{0}' errors were reported by the server:".format(typ)] - 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\nDetail: %s\n" % (achall.domain, achall.error.detail)) if typ in _ERROR_HELP: msg.append("\n\n") diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index be19ab036..199954278 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -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\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): From 4adc2826318168f8318a7784a5b190599a03a045 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Tue, 5 Jan 2016 11:02:03 -0800 Subject: [PATCH 154/159] Fix formatting of error messages. --- letsencrypt/auth_handler.py | 4 ++-- letsencrypt/tests/auth_handler_test.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 37b71775d..e77f83248 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -541,10 +541,10 @@ 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)] + "The following errors were reported by the server:".format(typ)] for achall in failed_achalls: - msg.append("\n\nDomain: %s\nDetail: %s\n" % (achall.domain, achall.error.detail)) + 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") diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 199954278..5b4c2bfc7 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -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("Domain: example.com\nDetail: detail" 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): From cf74446b58fa1229c6b2ad6bf5465f3b9ebe41ef Mon Sep 17 00:00:00 2001 From: Reinaldo de Souza Jr Date: Mon, 4 Jan 2016 23:04:18 -0500 Subject: [PATCH 155/159] Add test for redirect enhancement --- .../letsencrypt_nginx/tests/configurator_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py index 56ad5110c..5e237b04a 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py +++ b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py @@ -330,6 +330,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 From adcb7934aed5780ba96e9f5c861c467bff8f616d Mon Sep 17 00:00:00 2001 From: Reinaldo de Souza Jr Date: Mon, 4 Jan 2016 22:48:01 -0500 Subject: [PATCH 156/159] Improve choose_vhost() test by verifying the output file --- .../tests/configurator_test.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py index 5e237b04a..9a962a55f 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py +++ b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py @@ -91,12 +91,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) From 20829e05ed6a665a224fdef43e4f91cd5c002199 Mon Sep 17 00:00:00 2001 From: Reinaldo de Souza Jr Date: Tue, 5 Jan 2016 15:15:29 -0500 Subject: [PATCH 157/159] Add missing test condition for prepare() --- .../tests/configurator_test.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py index 9a962a55f..3ad0b834f 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py +++ b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py @@ -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', [], []) From 0a04efe40c55029ba66a5c8b69f0d3f646286aab Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Tue, 5 Jan 2016 17:23:32 -0800 Subject: [PATCH 158/159] Fix lint error and remove extra format() --- letsencrypt/auth_handler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index e77f83248..c63d8c8d4 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -540,11 +540,11 @@ def _generate_failed_chall_msg(failed_achalls): """ typ = failed_achalls[0].error.typ - msg = [ - "The following errors were reported by the server:".format(typ)] + msg = ["The following errors were reported by the server:"] for achall in failed_achalls: - msg.append("\n\nDomain: %s\nType: %s\nDetail: %s" % (achall.domain, achall.error.typ, achall.error.detail)) + 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") From 6548f343bfe8b9fc3e00e1ef2abe356ff4c3c13c Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Thu, 7 Jan 2016 21:20:14 +0000 Subject: [PATCH 159/159] Add invalidEmail error type to acme Related to: - #1923 - https://github.com/ietf-wg-acme/acme/pull/65 --- acme/acme/messages.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 0b73864ec..3b5739da1 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -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'),