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 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/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 """ diff --git a/acme/setup.py b/acme/setup.py index e35b40d6e..ba2c88394 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', @@ -32,6 +30,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 = [ 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags 'sphinx_rtd_theme', diff --git a/bootstrap/README b/bootstrap/README index 89fd8b6ba..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: py26reqs.txt git+https://* - ca-certificates: communication with demo ACMO server at - https://www.letsencrypt-demo.org, py26reqs.txt git+https://* + https://www.letsencrypt-demo.org 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..8b96fe6f1 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -24,26 +24,56 @@ 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 +augeas_pkg=libaugeas0 +AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + +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/* 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 -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")' + + echo deb http://http.debian.net/debian wheezy-backports main >> /etc/apt/sources.list.d/wheezy-backports.list + apt-get update + 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" + echo "Let's Encrypt apache plugin..." + fi + # XXX add a case for ubuntu PPAs +fi + apt-get install -y --no-install-recommends \ - git \ python \ python-dev \ $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 diff --git a/bootstrap/_gentoo_common.sh b/bootstrap/_gentoo_common.sh index a718db7ff..f49dc00f0 100755 --- a/bootstrap/_gentoo_common.sh +++ b/bootstrap/_gentoo_common.sh @@ -1,6 +1,6 @@ #!/bin/sh -PACKAGES="dev-vcs/git +PACKAGES=" dev-lang/python:2.7 dev-python/virtualenv dev-util/dialog diff --git a/bootstrap/_rpm_common.sh b/bootstrap/_rpm_common.sh index 411d7bd92..92b54b720 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..efeebe4f8 100755 --- a/bootstrap/_suse_common.sh +++ b/bootstrap/_suse_common.sh @@ -2,7 +2,7 @@ # SLE12 don't have python-virtualenv -zypper -nq in -l git-core \ +zypper -nq in -l \ python \ python-devel \ python-virtualenv \ 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/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 \ 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/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. diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index f72492ac2..32da8b95d 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -542,7 +542,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 listens = [self.parser.get_arg(x).split()[0] for x in self.parser.find_dir("Listen")] @@ -1271,6 +1272,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ self.config_test() + logger.debug(self.reverter.view_config_changes()) self._reload() def _reload(self): @@ -1399,7 +1401,7 @@ def _get_mod_deps(mod_name): """ deps = { - "ssl": ["setenvif", "mime", "socache_shmcb"] + "ssl": ["setenvif", "mime"] } return deps.get(mod_name, []) 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", diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index ec5211ae4..418e0ec39 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, @@ -36,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 @@ -60,6 +60,11 @@ class ApacheParser(object): # Sites-available is not included naturally in configuration self._parse_file(os.path.join(self.root, "sites-available") + "/*") + #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. @@ -101,7 +106,8 @@ class ApacheParser(object): try: matches.remove("DUMP_RUN_CFG") except ValueError: - raise errors.PluginError("Unable to parse runtime variables") + self.unparsable = True + return 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 f2bf89d2c..617ff96af 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -28,10 +28,21 @@ 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): + """A test for a mock deploy cert""" + self.config.real_deploy_cert = self.config.deploy_cert + def mocked_deploy_cert(*args, **kwargs): + """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 + def tearDown(self): shutil.rmtree(self.temp_dir) shutil.rmtree(self.config_dir) @@ -251,6 +262,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") @@ -277,6 +289,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") @@ -290,6 +303,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..352c2fcf4 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py @@ -151,8 +151,8 @@ 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") + 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,21 @@ 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): + """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) + 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 f4dff7734..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,9 @@ 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"): + sni_responses = self.sni.perform() self.assertEqual(mock_setup_cert.call_count, 2) diff --git a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py b/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py index 4284e240c..a770804d1 100644 --- a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py +++ b/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py @@ -1,12 +1,14 @@ """A class that performs TLS-SNI-01 challenges for Apache""" import os +import logging from letsencrypt.plugins import common from letsencrypt_apache import obj from letsencrypt_apache import parser +logger = logging.getLogger(__name__) class ApacheTlsSni01(common.TLSSNI01): """Class that performs TLS-SNI-01 challenges within the Apache configurator @@ -104,6 +106,7 @@ class ApacheTlsSni01(common.TLSSNI01): self.configurator.reverter.register_file_creation( True, self.challenge_conf) + logger.debug("writing a config file with text: %s", config_text) with open(self.challenge_conf, "w") as new_conf: new_conf.write(config_text) diff --git a/letsencrypt-auto b/letsencrypt-auto index 5ad4abd76..20465dbb1 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")' " @@ -176,7 +176,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 @@ -188,8 +188,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 @@ -202,5 +200,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 "$@" 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/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 " diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 9f5b6bba8..defe9396b 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -66,8 +66,16 @@ 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") + with open(permission_canary, "w") as f: + f.write("thingimy") 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") 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 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()) 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/setup.py b/setup.py index 40c6ac16c..f95f672ff 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,6 @@ version = meta['version'] install_requires = [ 'acme=={0}'.format(version), - 'ConfigArgParse', 'configobj', 'cryptography>=0.7', # load_pem_x509_certificate 'parsedatetime', @@ -41,7 +40,6 @@ install_requires = [ 'pyrfc3339', 'python2-pythondialog>=3.2.2rc1', # Debian squeeze support, cf. #280 'pytz', - 'requests', 'setuptools', # pkg_resources 'six', 'zope.component', @@ -53,10 +51,14 @@ if sys.version_info < (2, 7): install_requires.extend([ # only some distros recognize stdlib argparse as already satisfying 'argparse', + 'ConfigArgParse>=0.10.0', # python2.6 support, upstream #17 'mock<1.1.0', ]) else: - install_requires.append('mock') + install_requires.extend([ + 'ConfigArgParse', + 'mock', + ]) dev_extras = [ # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 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] + 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" 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