diff --git a/CHANGELOG.md b/CHANGELOG.md index c1af9ffb3..77351c84b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). -## 0.34.0 - master +## 0.35.0 - master ### Added @@ -10,6 +10,24 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). ### Changed +* + +### Fixed + +* + +Despite us having broken lockstep, we are continuing to release new versions of +all Certbot components during releases for the time being, however, the only +package with changes other than its version number was: + +* + +More details about these changes can be found on our GitHub repo. + +## 0.34.0 - 2019-05-01 + +### Changed + * Apache plugin now tries to restart httpd on Fedora using systemctl if a configuration test error is detected. This has to be done due to the way Fedora now generates the self signed certificate files upon first @@ -22,10 +40,20 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). `malformed` error to be received from the ACME server. * Linode DNS plugin now supports api keys created from their new panel at [cloud.linode.com](https://cloud.linode.com) - -### Fixed - -* +* Adding a warning noting that future versions of Certbot will automatically configure the + webserver so that all requests redirect to secure HTTPS access. You can control this + behavior and disable this warning with the --redirect and --no-redirect flags. +* certbot-auto now prints warnings when run as root with insecure file system + permissions. If you see these messages, you should fix the problem by + following the instructions at + https://community.letsencrypt.org/t/certbot-auto-deployment-best-practices/91979/, + however, these warnings can be disabled as necessary with the flag + --no-permissions-check. +* `acme` module uses now a POST-as-GET request to retrieve the registration + from an ACME v2 server +* Convert the tsig algorithm specified in the certbot_dns_rfc2136 configuration file to + all uppercase letters before validating. This makes the value in the config case + insensitive. Despite us having broken lockstep, we are continuing to release new versions of all Certbot components during releases for the time being, however, the only diff --git a/acme/acme/client.py b/acme/acme/client.py index a41787756..5a8fd88ae 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -123,15 +123,6 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes """ return self.update_registration(regr, update={'status': 'deactivated'}) - def query_registration(self, regr): - """Query server about registration. - - :param messages.RegistrationResource: Existing Registration - Resource. - - """ - return self._send_recv_regr(regr, messages.UpdateRegistration()) - def _authzr_from_response(self, response, identifier=None, uri=None): authzr = messages.AuthorizationResource( body=messages.Authorization.from_json(response.json()), @@ -276,6 +267,15 @@ class Client(ClientBase): # pylint: disable=no-member return self._regr_from_response(response) + def query_registration(self, regr): + """Query server about registration. + + :param messages.RegistrationResource: Existing Registration + Resource. + + """ + return self._send_recv_regr(regr, messages.UpdateRegistration()) + def agree_to_tos(self, regr): """Agree to the terms-of-service. @@ -603,10 +603,13 @@ class ClientV2(ClientBase): Resource. """ - self.net.account = regr - updated_regr = super(ClientV2, self).query_registration(regr) - self.net.account = updated_regr - return updated_regr + self.net.account = regr # See certbot/certbot#6258 + # ACME v2 requires to use a POST-as-GET request (POST an empty JWS) here. + # This is done by passing None instead of an empty UpdateRegistration to _post(). + response = self._post(regr.uri, None) + self.net.account = self._regr_from_response(response, uri=regr.uri, + terms_of_service=regr.terms_of_service) + return self.net.account def update_registration(self, regr, update=None): """Update registration. diff --git a/acme/setup.py b/acme/setup.py index 11e4f3372..56a9a63f3 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -3,7 +3,7 @@ from setuptools import find_packages from setuptools.command.test import test as TestCommand import sys -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/certbot_apache/entrypoint.py b/certbot-apache/certbot_apache/entrypoint.py index 6f1443507..df7297d3e 100644 --- a/certbot-apache/certbot_apache/entrypoint.py +++ b/certbot-apache/certbot_apache/entrypoint.py @@ -1,8 +1,13 @@ """ Entry point for Apache Plugin """ +# Pylint does not like disutils.version when running inside a venv. +# See: https://github.com/PyCQA/pylint/issues/73 +from distutils.version import LooseVersion # pylint: disable=no-name-in-module,import-error + from certbot import util from certbot_apache import configurator from certbot_apache import override_arch +from certbot_apache import override_fedora from certbot_apache import override_darwin from certbot_apache import override_debian from certbot_apache import override_centos @@ -16,7 +21,8 @@ OVERRIDE_CLASSES = { "ubuntu": override_debian.DebianConfigurator, "centos": override_centos.CentOSConfigurator, "centos linux": override_centos.CentOSConfigurator, - "fedora": override_centos.CentOSConfigurator, + "fedora_old": override_centos.CentOSConfigurator, + "fedora": override_fedora.FedoraConfigurator, "ol": override_centos.CentOSConfigurator, "red hat enterprise linux server": override_centos.CentOSConfigurator, "rhel": override_centos.CentOSConfigurator, @@ -27,12 +33,19 @@ OVERRIDE_CLASSES = { "suse": override_suse.OpenSUSEConfigurator, } + def get_configurator(): """ Get correct configurator class based on the OS fingerprint """ - os_info = util.get_os_info() + os_name, os_version = util.get_os_info() + os_name = os_name.lower() override_class = None + + # Special case for older Fedora versions + if os_name == 'fedora' and LooseVersion(os_version) < LooseVersion('29'): + os_name = 'fedora_old' + try: - override_class = OVERRIDE_CLASSES[os_info[0].lower()] + override_class = OVERRIDE_CLASSES[os_name] except KeyError: # OS not found in the list os_like = util.get_systemd_os_like() @@ -45,4 +58,5 @@ def get_configurator(): override_class = configurator.ApacheConfigurator return override_class + ENTRYPOINT = get_configurator() diff --git a/certbot-apache/certbot_apache/override_fedora.py b/certbot-apache/certbot_apache/override_fedora.py new file mode 100644 index 000000000..cb0bf06d0 --- /dev/null +++ b/certbot-apache/certbot_apache/override_fedora.py @@ -0,0 +1,98 @@ +""" Distribution specific override class for Fedora 29+ """ +import pkg_resources +import zope.interface + +from certbot import errors +from certbot import interfaces +from certbot import util + +from certbot_apache import apache_util +from certbot_apache import configurator +from certbot_apache import parser + + +@zope.interface.provider(interfaces.IPluginFactory) +class FedoraConfigurator(configurator.ApacheConfigurator): + """Fedora 29+ specific ApacheConfigurator override class""" + + OS_DEFAULTS = dict( + server_root="/etc/httpd", + vhost_root="/etc/httpd/conf.d", + vhost_files="*.conf", + logs_root="/var/log/httpd", + ctl="httpd", + version_cmd=['httpd', '-v'], + restart_cmd=['apachectl', 'graceful'], + restart_cmd_alt=['apachectl', 'restart'], + conftest_cmd=['apachectl', 'configtest'], + enmod=None, + dismod=None, + le_vhost_ext="-le-ssl.conf", + handle_modules=False, + handle_sites=False, + challenge_location="/etc/httpd/conf.d", + MOD_SSL_CONF_SRC=pkg_resources.resource_filename( + # TODO: eventually newest version of Fedora will need their own config + "certbot_apache", "centos-options-ssl-apache.conf") + ) + + def config_test(self): + """ + Override config_test to mitigate configtest error in vanilla installation + of mod_ssl in Fedora. The error is caused by non-existent self-signed + certificates referenced by the configuration, that would be autogenerated + during the first (re)start of httpd. + """ + try: + super(FedoraConfigurator, self).config_test() + except errors.MisconfigurationError: + self._try_restart_fedora() + + def get_parser(self): + """Initializes the ApacheParser""" + return FedoraParser( + self.aug, self.option("server_root"), self.option("vhost_root"), + self.version, configurator=self) + + def _try_restart_fedora(self): + """ + Tries to restart httpd using systemctl to generate the self signed keypair. + """ + try: + util.run_script(['systemctl', 'restart', 'httpd']) + except errors.SubprocessError as err: + raise errors.MisconfigurationError(str(err)) + + # Finish with actual config check to see if systemctl restart helped + super(FedoraConfigurator, self).config_test() + + def _prepare_options(self): + """ + Override the options dictionary initialization to keep using apachectl + instead of httpd and so take advantages of this new bash script in newer versions + of Fedora to restart httpd. + """ + super(FedoraConfigurator, self)._prepare_options() + self.options["restart_cmd"][0] = 'apachectl' + self.options["restart_cmd_alt"][0] = 'apachectl' + self.options["conftest_cmd"][0] = 'apachectl' + + +class FedoraParser(parser.ApacheParser): + """Fedora 29+ specific ApacheParser override class""" + def __init__(self, *args, **kwargs): + # Fedora 29+ specific configuration file for Apache + self.sysconfig_filep = "/etc/sysconfig/httpd" + super(FedoraParser, self).__init__(*args, **kwargs) + + def update_runtime_variables(self): + """ Override for update_runtime_variables for custom parsing """ + # Opportunistic, works if SELinux not enforced + super(FedoraParser, self).update_runtime_variables() + self._parse_sysconfig_var() + + def _parse_sysconfig_var(self): + """ Parses Apache CLI options from Fedora configuration file """ + defines = apache_util.parse_define_file(self.sysconfig_filep, "OPTIONS") + for k in defines: + self.variables[k] = defines[k] diff --git a/certbot-apache/certbot_apache/tests/centos_test.py b/certbot-apache/certbot_apache/tests/centos_test.py index a0c1636b0..5d16c2b55 100644 --- a/certbot-apache/certbot_apache/tests/centos_test.py +++ b/certbot-apache/certbot_apache/tests/centos_test.py @@ -43,13 +43,14 @@ class FedoraRestartTest(util.ApacheTest): vhost_root=vhost_root) self.config = util.get_apache_configurator( self.config_path, self.vhost_path, self.config_dir, self.work_dir, - os_info="fedora") + os_info="fedora_old") self.vh_truth = get_vh_truth( self.temp_dir, "centos7_apache/apache") def _run_fedora_test(self): + self.assertIsInstance(self.config, override_centos.CentOSConfigurator) with mock.patch("certbot.util.get_os_info") as mock_info: - mock_info.return_value = ["fedora"] + mock_info.return_value = ["fedora", "28"] self.config.config_test() def test_non_fedora_error(self): @@ -103,8 +104,7 @@ class MultipleVhostsTestCentOS(util.ApacheTest): self.temp_dir, "centos7_apache/apache") def test_get_parser(self): - self.assertTrue(isinstance(self.config.parser, - override_centos.CentOSParser)) + self.assertIsInstance(self.config.parser, override_centos.CentOSParser) @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") def test_opportunistic_httpd_runtime_parsing(self, mock_get): diff --git a/certbot-apache/certbot_apache/tests/entrypoint_test.py b/certbot-apache/certbot_apache/tests/entrypoint_test.py index 6d85b0db2..9adcd46dc 100644 --- a/certbot-apache/certbot_apache/tests/entrypoint_test.py +++ b/certbot-apache/certbot_apache/tests/entrypoint_test.py @@ -6,6 +6,7 @@ import mock from certbot_apache import configurator from certbot_apache import entrypoint + class EntryPointTest(unittest.TestCase): """Entrypoint tests""" @@ -15,7 +16,12 @@ class EntryPointTest(unittest.TestCase): with mock.patch("certbot.util.get_os_info") as mock_info: for distro in entrypoint.OVERRIDE_CLASSES: - mock_info.return_value = (distro, "whatever") + return_value = (distro, "whatever") + if distro == 'fedora_old': + return_value = ('fedora', '28') + elif distro == 'fedora': + return_value = ('fedora', '29') + mock_info.return_value = return_value self.assertEqual(entrypoint.get_configurator(), entrypoint.OVERRIDE_CLASSES[distro]) diff --git a/certbot-apache/certbot_apache/tests/fedora_test.py b/certbot-apache/certbot_apache/tests/fedora_test.py new file mode 100644 index 000000000..67533fe1d --- /dev/null +++ b/certbot-apache/certbot_apache/tests/fedora_test.py @@ -0,0 +1,194 @@ +"""Test for certbot_apache.configurator for Fedora 29+ overrides""" +import unittest + +import mock + +from certbot import errors +from certbot.compat import os + +from certbot_apache import obj +from certbot_apache import override_fedora +from certbot_apache.tests import util + + +def get_vh_truth(temp_dir, config_name): + """Return the ground truth for the specified directory.""" + prefix = os.path.join( + temp_dir, config_name, "httpd/conf.d") + + aug_pre = "/files" + prefix + # TODO: eventually, these tests should have a dedicated configuration instead + # of reusing the ones from centos_test + vh_truth = [ + obj.VirtualHost( + os.path.join(prefix, "centos.example.com.conf"), + os.path.join(aug_pre, "centos.example.com.conf/VirtualHost"), + {obj.Addr.fromstring("*:80")}, + False, True, "centos.example.com"), + obj.VirtualHost( + os.path.join(prefix, "ssl.conf"), + os.path.join(aug_pre, "ssl.conf/VirtualHost"), + {obj.Addr.fromstring("_default_:443")}, + True, True, None) + ] + return vh_truth + + +class FedoraRestartTest(util.ApacheTest): + """Tests for Fedora specific self-signed certificate override""" + + # TODO: eventually, these tests should have a dedicated configuration instead + # of reusing the ones from centos_test + def setUp(self): # pylint: disable=arguments-differ + test_dir = "centos7_apache/apache" + config_root = "centos7_apache/apache/httpd" + vhost_root = "centos7_apache/apache/httpd/conf.d" + super(FedoraRestartTest, self).setUp(test_dir=test_dir, + config_root=config_root, + vhost_root=vhost_root) + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, self.work_dir, + os_info="fedora") + self.vh_truth = get_vh_truth( + self.temp_dir, "centos7_apache/apache") + + def _run_fedora_test(self): + self.assertIsInstance(self.config, override_fedora.FedoraConfigurator) + self.config.config_test() + + def test_fedora_restart_error(self): + c_test = "certbot_apache.configurator.ApacheConfigurator.config_test" + with mock.patch(c_test) as mock_test: + # First call raises error, second doesn't + mock_test.side_effect = [errors.MisconfigurationError, ''] + with mock.patch("certbot.util.run_script") as mock_run: + mock_run.side_effect = errors.SubprocessError + self.assertRaises(errors.MisconfigurationError, + self._run_fedora_test) + + def test_fedora_restart(self): + c_test = "certbot_apache.configurator.ApacheConfigurator.config_test" + with mock.patch(c_test) as mock_test: + with mock.patch("certbot.util.run_script") as mock_run: + # First call raises error, second doesn't + mock_test.side_effect = [errors.MisconfigurationError, ''] + self._run_fedora_test() + self.assertEqual(mock_test.call_count, 2) + self.assertEqual(mock_run.call_args[0][0], + ['systemctl', 'restart', 'httpd']) + + +class MultipleVhostsTestFedora(util.ApacheTest): + """Multiple vhost tests for CentOS / RHEL family of distros""" + + _multiprocess_can_split_ = True + + def setUp(self): # pylint: disable=arguments-differ + test_dir = "centos7_apache/apache" + config_root = "centos7_apache/apache/httpd" + vhost_root = "centos7_apache/apache/httpd/conf.d" + super(MultipleVhostsTestFedora, self).setUp(test_dir=test_dir, + config_root=config_root, + vhost_root=vhost_root) + + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, self.work_dir, + os_info="fedora") + self.vh_truth = get_vh_truth( + self.temp_dir, "centos7_apache/apache") + + def test_get_parser(self): + self.assertIsInstance(self.config.parser, override_fedora.FedoraParser) + + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") + def test_opportunistic_httpd_runtime_parsing(self, mock_get): + define_val = ( + 'Define: TEST1\n' + 'Define: TEST2\n' + 'Define: DUMP_RUN_CFG\n' + ) + mod_val = ( + 'Loaded Modules:\n' + ' mock_module (static)\n' + ' another_module (static)\n' + ) + def mock_get_cfg(command): + """Mock httpd process stdout""" + if command == ['httpd', '-t', '-D', 'DUMP_RUN_CFG']: + return define_val + elif command == ['httpd', '-t', '-D', 'DUMP_MODULES']: + return mod_val + return "" + mock_get.side_effect = mock_get_cfg + self.config.parser.modules = set() + self.config.parser.variables = {} + + with mock.patch("certbot.util.get_os_info") as mock_osi: + # Make sure we have the have the CentOS httpd constants + mock_osi.return_value = ("fedora", "29") + self.config.parser.update_runtime_variables() + + self.assertEqual(mock_get.call_count, 3) + self.assertEqual(len(self.config.parser.modules), 4) + self.assertEqual(len(self.config.parser.variables), 2) + self.assertTrue("TEST2" in self.config.parser.variables.keys()) + self.assertTrue("mod_another.c" in self.config.parser.modules) + + @mock.patch("certbot_apache.configurator.util.run_script") + def test_get_version(self, mock_run_script): + mock_run_script.return_value = ('', None) + self.assertRaises(errors.PluginError, self.config.get_version) + self.assertEqual(mock_run_script.call_args[0][0][0], 'httpd') + + def test_get_virtual_hosts(self): + """Make sure all vhosts are being properly found.""" + vhs = self.config.get_virtual_hosts() + self.assertEqual(len(vhs), 2) + found = 0 + + for vhost in vhs: + for centos_truth in self.vh_truth: + if vhost == centos_truth: + found += 1 + break + else: + raise Exception("Missed: %s" % vhost) # pragma: no cover + self.assertEqual(found, 2) + + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") + def test_get_sysconfig_vars(self, mock_cfg): + """Make sure we read the sysconfig OPTIONS variable correctly""" + # Return nothing for the process calls + mock_cfg.return_value = "" + self.config.parser.sysconfig_filep = os.path.realpath( + os.path.join(self.config.parser.root, "../sysconfig/httpd")) + self.config.parser.variables = {} + + with mock.patch("certbot.util.get_os_info") as mock_osi: + # Make sure we have the have the CentOS httpd constants + mock_osi.return_value = ("fedora", "29") + self.config.parser.update_runtime_variables() + + self.assertTrue("mock_define" in self.config.parser.variables.keys()) + self.assertTrue("mock_define_too" in self.config.parser.variables.keys()) + self.assertTrue("mock_value" in self.config.parser.variables.keys()) + self.assertEqual("TRUE", self.config.parser.variables["mock_value"]) + self.assertTrue("MOCK_NOSEP" in self.config.parser.variables.keys()) + self.assertEqual("NOSEP_VAL", self.config.parser.variables["NOSEP_TWO"]) + + @mock.patch("certbot_apache.configurator.util.run_script") + def test_alt_restart_works(self, mock_run_script): + mock_run_script.side_effect = [None, errors.SubprocessError, None] + self.config.restart() + self.assertEqual(mock_run_script.call_count, 3) + + @mock.patch("certbot_apache.configurator.util.run_script") + def test_alt_restart_errors(self, mock_run_script): + mock_run_script.side_effect = [None, + errors.SubprocessError, + errors.SubprocessError] + self.assertRaises(errors.MisconfigurationError, self.config.restart) + + +if __name__ == "__main__": + unittest.main() # pragma: no cover diff --git a/certbot-apache/local-oldest-requirements.txt b/certbot-apache/local-oldest-requirements.txt index da509406e..0bc9ee027 100644 --- a/certbot-apache/local-oldest-requirements.txt +++ b/certbot-apache/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.29.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index deb688fd2..e14bcb3b6 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,13 +4,13 @@ from setuptools.command.test import test as TestCommand import sys -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.29.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'mock', 'python-augeas', 'setuptools', diff --git a/certbot-auto b/certbot-auto index d60bdbc70..0d9606372 100755 --- a/certbot-auto +++ b/certbot-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.33.1" +LE_AUTO_VERSION="0.34.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -45,6 +45,7 @@ Help for certbot itself cannot be provided until it is installed. -h, --help print this help -n, --non-interactive, --noninteractive run without asking for user input --no-bootstrap do not install OS dependencies + --no-permissions-check do not warn about file system permissions --no-self-upgrade do not download updates --os-packages-only install OS dependencies and exit --install-only install certbot, upgrade if needed, and exit @@ -67,6 +68,8 @@ for arg in "$@" ; do # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) NO_SELF_UPGRADE=1;; + --no-permissions-check) + NO_PERMISSIONS_CHECK=1;; --no-bootstrap) NO_BOOTSTRAP=1;; --help) @@ -172,7 +175,11 @@ SetRootAuthMechanism() { sudo) SUDO="sudo -E" ;; - '') ;; # Nothing to do for plain root method. + '') + # If we're not running with root, don't check that this script can only + # be modified by system users and groups. + NO_PERMISSIONS_CHECK=1 + ;; *) error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'." exit 1 @@ -534,7 +541,7 @@ BootstrapSuseCommon() { # Since Leap 15.0 (and associated Tumbleweed version), python-virtualenv # is a source package, and python2-virtualenv must be used instead. # Also currently python2-setuptools is not a dependency of python2-virtualenv, - # while it should be. Installing it explicitly until upstreqm fix. + # while it should be. Installing it explicitly until upstream fix. OPENSUSE_VIRTUALENV_PACKAGES="python2-virtualenv python2-setuptools" fi @@ -1138,9 +1145,9 @@ requests-toolbelt==0.9.1 \ six==1.12.0 \ --hash=sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c \ --hash=sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73 -urllib3==1.24.1 \ - --hash=sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39 \ - --hash=sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22 +urllib3==1.24.2 \ + --hash=sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0 \ + --hash=sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3 zope.component==4.5 \ --hash=sha256:6edfd626c3b593b72895a8cfcf79bff41f4619194ce996a85bce31ac02b94e55 \ --hash=sha256:984a06ba3def0b02b1117fa4c45b56e772e8c29c0340820fbf367e440a93a3a4 @@ -1218,18 +1225,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.33.1 \ - --hash=sha256:e2a08467146b7a7ed2c8ca6625b1705d93b51e89866f6ede8a8a262594c18f3f \ - --hash=sha256:d5203f32c50f3ec5a32df97e4affddbcd288a569678ecb5669adda21cd5ac3d9 -acme==0.33.1 \ - --hash=sha256:02467d4b1d246105d6d1ea01822dd9e2eea5bf3a50607523969d8e400d53c07b \ - --hash=sha256:b38cdb71d0071efe1f1190a744f8f95f3c698b76ac0f5d919bbfe3522e277a82 -certbot-apache==0.33.1 \ - --hash=sha256:0d2a463539e6396de2d374de62faba34e1fe40dd8059e3c64dcd5dabaa66887b \ - --hash=sha256:659db7335d919fee52ae707567994e13c31ed25109c94b246c60c97d21c46f3a -certbot-nginx==0.33.1 \ - --hash=sha256:df9fb86e735eb2668e070f20317e85c37952f3f612fa7f6bbc2c63784b213f28 \ - --hash=sha256:b3201eee03be74fc743c21c721d3b5586c3323db63e78b68583a6250ad680cff +certbot==0.34.0 \ + --hash=sha256:51dddf2cb1c50a9f8b993090890bf4858d8fadffce38bafcdf6bf585a2040317 \ + --hash=sha256:e75bdabfd9183bd9842ada42a51070f120d15982e81c490df59dde62e4df2c8b +acme==0.34.0 \ + --hash=sha256:3448024d2c274aebfb9b31b53862576d167626ce2fd1997a78d450c32a292fa3 \ + --hash=sha256:92478e58f541c5c7c527427a50650005cdede799b78f0a0a65b8093d6368bcfd +certbot-apache==0.34.0 \ + --hash=sha256:79e686f25b63dac17d771d71f791f252774da22125f3f6e0665f4cf791d516fe \ + --hash=sha256:d5ae09b4801fbac23d5acf64a5ee265108199d2852fbe743e7b6ab06fa08edf6 +certbot-nginx==0.34.0 \ + --hash=sha256:868d7dcb59bb2548cb4a2ae187db5da1bfe33aac306b1b844b96ee00a39cac52 \ + --hash=sha256:d6c728b85c523711ec0dc800f8d4ebbef192fb0ca1ec7914c173207e4aba5194 UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -1494,6 +1501,108 @@ else exit 0 fi + DeterminePythonVersion "NOCRASH" + # Don't warn about file permissions if the user disabled the check or we + # can't find an up-to-date Python. + if [ "$PYVER" -ge "$MIN_PYVER" -a "$NO_PERMISSIONS_CHECK" != 1 ]; then + # --------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/check_permissions.py" +"""Verifies certbot-auto cannot be modified by unprivileged users. + +This script takes the path to certbot-auto as its only command line +argument. It then checks that the file can only be modified by uid/gid +< 1000 and if other users can modify the file, it prints a warning with +a suggestion on how to solve the problem. + +Permissions on symlinks in the absolute path of certbot-auto are ignored +and only the canonical path to certbot-auto is checked. There could be +permissions problems due to the symlinks that are unreported by this +script, however, issues like this were not caused by our documentation +and are ignored for the sake of simplicity. + +All warnings are printed to stdout rather than stderr so all stderr +output from this script can be suppressed to avoid printing messages if +this script fails for some reason. + +""" +from __future__ import print_function + +import os +import stat +import sys + + +FORUM_POST_URL = 'https://community.letsencrypt.org/t/certbot-auto-deployment-best-practices/91979/' + + +def has_safe_permissions(path): + """Returns True if the given path has secure permissions. + + The permissions are considered safe if the file is only writable by + uid/gid < 1000. + + The reason we allow more IDs than 0 is because on some systems such + as Debian, system users/groups other than uid/gid 0 are used for the + path we recommend in our instructions which is /usr/local/bin. 1000 + was chosen because on Debian 0-999 is reserved for system IDs[1] and + on RHEL either 0-499 or 0-999 is reserved depending on the + version[2][3]. Due to these differences across different OSes, this + detection isn't perfect so we only determine permissions are + insecure when we can be reasonably confident there is a problem + regardless of the underlying OS. + + [1] https://www.debian.org/doc/debian-policy/ch-opersys.html#uid-and-gid-classes + [2] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/ch-managing_users_and_groups + [3] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-managing_users_and_groups + + :param str path: filesystem path to check + :returns: True if the path has secure permissions, otherwise, False + :rtype: bool + + """ + # os.stat follows symlinks before obtaining information about a file. + stat_result = os.stat(path) + if stat_result.st_mode & stat.S_IWOTH: + return False + if stat_result.st_mode & stat.S_IWGRP and stat_result.st_gid >= 1000: + return False + if stat_result.st_mode & stat.S_IWUSR and stat_result.st_uid >= 1000: + return False + return True + + +def main(certbot_auto_path): + current_path = os.path.realpath(certbot_auto_path) + last_path = None + permissions_ok = True + # This loop makes use of the fact that os.path.dirname('/') == '/'. + while current_path != last_path and permissions_ok: + permissions_ok = has_safe_permissions(current_path) + last_path = current_path + current_path = os.path.dirname(current_path) + + if not permissions_ok: + print('{0} has insecure permissions!'.format(certbot_auto_path)) + print('To learn how to fix them, visit {0}'.format(FORUM_POST_URL)) + + +if __name__ == '__main__': + main(sys.argv[1]) + +UNLIKELY_EOF + # --------------------------------------------------------------------------- + # If the script fails for some reason, don't break certbot-auto. + set +e + # Suppress unexpected error output and only print the script's output if it + # ran successfully. + CHECK_PERM_OUT=$("$LE_PYTHON" "$TEMP_DIR/check_permissions.py" "$0" 2>/dev/null) + CHECK_PERM_STATUS="$?" + set -e + if [ "$CHECK_PERM_STATUS" = 0 ]; then + error "$CHECK_PERM_OUT" + fi + fi + if [ "$NO_SELF_UPGRADE" != 1 ]; then TEMP_DIR=$(TempDir) trap 'rm -rf "$TEMP_DIR"' EXIT @@ -1650,7 +1759,6 @@ if __name__ == '__main__': UNLIKELY_EOF # --------------------------------------------------------------------------- - DeterminePythonVersion "NOCRASH" if [ "$PYVER" -lt "$MIN_PYVER" ]; then error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates." elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index 926a5e7b4..c95864e09 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' install_requires = [ 'certbot', diff --git a/certbot-dns-cloudflare/local-oldest-requirements.txt b/certbot-dns-cloudflare/local-oldest-requirements.txt index da509406e..0bc9ee027 100644 --- a/certbot-dns-cloudflare/local-oldest-requirements.txt +++ b/certbot-dns-cloudflare/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.29.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-cloudflare/setup.py b/certbot-dns-cloudflare/setup.py index 971ce7be8..afdfb09e1 100644 --- a/certbot-dns-cloudflare/setup.py +++ b/certbot-dns-cloudflare/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.29.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'cloudflare>=1.5.1', 'mock', 'setuptools', diff --git a/certbot-dns-cloudxns/local-oldest-requirements.txt b/certbot-dns-cloudxns/local-oldest-requirements.txt index 2b3ba9f32..c9999e87a 100644 --- a/certbot-dns-cloudxns/local-oldest-requirements.txt +++ b/certbot-dns-cloudxns/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-cloudxns/setup.py b/certbot-dns-cloudxns/setup.py index 6af7bb6e7..4883150fb 100644 --- a/certbot-dns-cloudxns/setup.py +++ b/certbot-dns-cloudxns/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name 'mock', 'setuptools', diff --git a/certbot-dns-digitalocean/local-oldest-requirements.txt b/certbot-dns-digitalocean/local-oldest-requirements.txt index da509406e..0bc9ee027 100644 --- a/certbot-dns-digitalocean/local-oldest-requirements.txt +++ b/certbot-dns-digitalocean/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.29.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index 81803d7da..07d406fd9 100644 --- a/certbot-dns-digitalocean/setup.py +++ b/certbot-dns-digitalocean/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.29.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'mock', 'python-digitalocean>=1.11', 'setuptools', diff --git a/certbot-dns-dnsimple/local-oldest-requirements.txt b/certbot-dns-dnsimple/local-oldest-requirements.txt index 2b3ba9f32..c9999e87a 100644 --- a/certbot-dns-dnsimple/local-oldest-requirements.txt +++ b/certbot-dns-dnsimple/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-dnsimple/setup.py b/certbot-dns-dnsimple/setup.py index 4512f9fc0..f781bd0a5 100644 --- a/certbot-dns-dnsimple/setup.py +++ b/certbot-dns-dnsimple/setup.py @@ -1,20 +1,32 @@ +import os from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', - 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name + 'certbot>=0.34.0', 'mock', 'setuptools', 'zope.interface', ] +# This package normally depends on dns-lexicon>=3.2.1 to address the +# problem described in https://github.com/AnalogJ/lexicon/issues/387, +# however, the fix there has been backported to older versions of +# lexicon found in various Linux distros. This conditional helps us test +# that we've maintained compatibility with these versions of lexicon +# which allows us to potentially upgrade our packages in these distros +# as necessary. +if os.environ.get('CERTBOT_OLDEST') == '1': + install_requires.append('dns-lexicon>=2.2.1') +else: + install_requires.append('dns-lexicon>=3.2.1') + docs_extras = [ 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags 'sphinx_rtd_theme', diff --git a/certbot-dns-dnsmadeeasy/local-oldest-requirements.txt b/certbot-dns-dnsmadeeasy/local-oldest-requirements.txt index 2b3ba9f32..c9999e87a 100644 --- a/certbot-dns-dnsmadeeasy/local-oldest-requirements.txt +++ b/certbot-dns-dnsmadeeasy/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-dnsmadeeasy/setup.py b/certbot-dns-dnsmadeeasy/setup.py index 51c6637a9..41c1b75a5 100644 --- a/certbot-dns-dnsmadeeasy/setup.py +++ b/certbot-dns-dnsmadeeasy/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name 'mock', 'setuptools', diff --git a/certbot-dns-gehirn/local-oldest-requirements.txt b/certbot-dns-gehirn/local-oldest-requirements.txt index 2b3ba9f32..c9999e87a 100644 --- a/certbot-dns-gehirn/local-oldest-requirements.txt +++ b/certbot-dns-gehirn/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-gehirn/setup.py b/certbot-dns-gehirn/setup.py index deb5c442d..f009df8e8 100644 --- a/certbot-dns-gehirn/setup.py +++ b/certbot-dns-gehirn/setup.py @@ -2,12 +2,12 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dns-lexicon>=2.1.22', 'mock', 'setuptools', diff --git a/certbot-dns-google/local-oldest-requirements.txt b/certbot-dns-google/local-oldest-requirements.txt index da509406e..0bc9ee027 100644 --- a/certbot-dns-google/local-oldest-requirements.txt +++ b/certbot-dns-google/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.29.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-google/setup.py b/certbot-dns-google/setup.py index 176c74968..e1d6aeed0 100644 --- a/certbot-dns-google/setup.py +++ b/certbot-dns-google/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.29.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', # 1.5 is the first version that supports oauth2client>=2.0 'google-api-python-client>=1.5', 'mock', diff --git a/certbot-dns-linode/local-oldest-requirements.txt b/certbot-dns-linode/local-oldest-requirements.txt index d48a789bb..ff1651cf7 100644 --- a/certbot-dns-linode/local-oldest-requirements.txt +++ b/certbot-dns-linode/local-oldest-requirements.txt @@ -1,4 +1,4 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 dns-lexicon==2.2.3 diff --git a/certbot-dns-linode/setup.py b/certbot-dns-linode/setup.py index 771e09381..ae8739d61 100644 --- a/certbot-dns-linode/setup.py +++ b/certbot-dns-linode/setup.py @@ -1,12 +1,12 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dns-lexicon>=2.2.3', 'mock', 'setuptools', diff --git a/certbot-dns-luadns/local-oldest-requirements.txt b/certbot-dns-luadns/local-oldest-requirements.txt index 2b3ba9f32..c9999e87a 100644 --- a/certbot-dns-luadns/local-oldest-requirements.txt +++ b/certbot-dns-luadns/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-luadns/setup.py b/certbot-dns-luadns/setup.py index ef77e4143..b2e14869e 100644 --- a/certbot-dns-luadns/setup.py +++ b/certbot-dns-luadns/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name 'mock', 'setuptools', diff --git a/certbot-dns-nsone/local-oldest-requirements.txt b/certbot-dns-nsone/local-oldest-requirements.txt index 2b3ba9f32..c9999e87a 100644 --- a/certbot-dns-nsone/local-oldest-requirements.txt +++ b/certbot-dns-nsone/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-nsone/setup.py b/certbot-dns-nsone/setup.py index 7bb7fbbff..e839cd71d 100644 --- a/certbot-dns-nsone/setup.py +++ b/certbot-dns-nsone/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name 'mock', 'setuptools', diff --git a/certbot-dns-ovh/local-oldest-requirements.txt b/certbot-dns-ovh/local-oldest-requirements.txt index ed5aa6c87..5472399aa 100644 --- a/certbot-dns-ovh/local-oldest-requirements.txt +++ b/certbot-dns-ovh/local-oldest-requirements.txt @@ -1,4 +1,4 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 dns-lexicon==2.7.14 diff --git a/certbot-dns-ovh/setup.py b/certbot-dns-ovh/setup.py index 9a05e69cc..a6a52d648 100644 --- a/certbot-dns-ovh/setup.py +++ b/certbot-dns-ovh/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dns-lexicon>=2.7.14', # Correct proxy use on OVH provider 'mock', 'setuptools', diff --git a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py index f985c9bf4..2061374e0 100644 --- a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py +++ b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py @@ -57,7 +57,7 @@ class Authenticator(dns_common.DNSAuthenticator): def _validate_algorithm(self, credentials): algorithm = credentials.conf('algorithm') if algorithm: - if not self.ALGORITHMS.get(algorithm): + if not self.ALGORITHMS.get(algorithm.upper()): raise errors.PluginError("Unknown algorithm: {0}.".format(algorithm)) def _setup_credentials(self): diff --git a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py index 2eb0517f6..d800f1ec7 100644 --- a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py +++ b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py @@ -64,7 +64,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic def test_valid_algorithm_passes(self): config = VALID_CONFIG.copy() - config["rfc2136_algorithm"] = "HMAC-SHA512" + config["rfc2136_algorithm"] = "HMAC-sha512" dns_test_common.write(config, self.config.rfc2136_credentials) self.auth.perform([self.achall]) diff --git a/certbot-dns-rfc2136/local-oldest-requirements.txt b/certbot-dns-rfc2136/local-oldest-requirements.txt index da509406e..0bc9ee027 100644 --- a/certbot-dns-rfc2136/local-oldest-requirements.txt +++ b/certbot-dns-rfc2136/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.29.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-rfc2136/setup.py b/certbot-dns-rfc2136/setup.py index 8e1d37650..e05104e5d 100644 --- a/certbot-dns-rfc2136/setup.py +++ b/certbot-dns-rfc2136/setup.py @@ -2,13 +2,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.29.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dnspython', 'mock', 'setuptools', diff --git a/certbot-dns-route53/local-oldest-requirements.txt b/certbot-dns-route53/local-oldest-requirements.txt index da509406e..0bc9ee027 100644 --- a/certbot-dns-route53/local-oldest-requirements.txt +++ b/certbot-dns-route53/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.29.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-route53/setup.py b/certbot-dns-route53/setup.py index 787d4a555..09cd4acd2 100644 --- a/certbot-dns-route53/setup.py +++ b/certbot-dns-route53/setup.py @@ -1,13 +1,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.29.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'boto3', 'mock', 'setuptools', diff --git a/certbot-dns-sakuracloud/local-oldest-requirements.txt b/certbot-dns-sakuracloud/local-oldest-requirements.txt index 2b3ba9f32..c9999e87a 100644 --- a/certbot-dns-sakuracloud/local-oldest-requirements.txt +++ b/certbot-dns-sakuracloud/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.31.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-dns-sakuracloud/setup.py b/certbot-dns-sakuracloud/setup.py index 286b13ee9..29f458542 100644 --- a/certbot-dns-sakuracloud/setup.py +++ b/certbot-dns-sakuracloud/setup.py @@ -2,12 +2,12 @@ from setuptools import setup from setuptools import find_packages -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ 'acme>=0.31.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'dns-lexicon>=2.1.23', 'mock', 'setuptools', diff --git a/certbot-nginx/local-oldest-requirements.txt b/certbot-nginx/local-oldest-requirements.txt index da509406e..0bc9ee027 100644 --- a/certbot-nginx/local-oldest-requirements.txt +++ b/certbot-nginx/local-oldest-requirements.txt @@ -1,3 +1,3 @@ # Remember to update setup.py to match the package versions below. acme[dev]==0.29.0 --e .[dev] +certbot[dev]==0.34.0 diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index a6da1d851..51055ce64 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,13 +4,13 @@ from setuptools.command.test import test as TestCommand import sys -version = '0.34.0.dev0' +version = '0.35.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ 'acme>=0.29.0', - 'certbot>=0.34.0.dev0', + 'certbot>=0.34.0', 'mock', 'PyOpenSSL', 'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary? diff --git a/certbot/__init__.py b/certbot/__init__.py index dc2ea5c99..abec68040 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.34.0.dev0' +__version__ = '0.35.0.dev0' diff --git a/certbot/cli.py b/certbot/cli.py index 96f58caf7..866b64aa6 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -1089,6 +1089,11 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis help="(certbot-auto only) prevent the certbot-auto script from" " installing OS-level dependencies (default: Prompt to install " " OS-wide dependencies, but exit if the user says 'No')") + helpful.add( + "automation", "--no-permissions-check", action="store_true", + default=flag_default("no_permissions_check"), + help="(certbot-auto only) skip the check on the file system" + " permissions of the certbot-auto script") helpful.add( ["automation", "renew", "certonly", "run"], "-q", "--quiet", dest="quiet", action="store_true", diff --git a/certbot/client.py b/certbot/client.py index 5ec3c4d92..3c4a894ae 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -549,6 +549,11 @@ class Client(object): if ask_redirect: if config_name == "redirect" and config_value is None: config_value = enhancements.ask(enhancement_name) + if not config_value: + logger.warning("Future versions of Certbot will automatically " + "configure the webserver so that all requests redirect to secure " + "HTTPS access. You can control this behavior and disable this " + "warning with the --redirect and --no-redirect flags.") if config_value: self.apply_enhancement(domains, enhancement_name, option) enhanced = True diff --git a/certbot/constants.py b/certbot/constants.py index c23effe2d..5b268e157 100644 --- a/certbot/constants.py +++ b/certbot/constants.py @@ -46,6 +46,7 @@ CLI_DEFAULTS = dict( duplicate=False, os_packages_only=False, no_self_upgrade=False, + no_permissions_check=False, no_bootstrap=False, quiet=False, staging=False, diff --git a/certbot/tests/cli_test.py b/certbot/tests/cli_test.py index 592bd1be7..8259d4040 100644 --- a/certbot/tests/cli_test.py +++ b/certbot/tests/cli_test.py @@ -453,6 +453,10 @@ class ParseTest(unittest.TestCase): # pylint: disable=too-many-public-methods for topic in ['all', 'plugins', 'dns-route53']: self.assertFalse('certbot-route53:auth' in self._help_output([help_flag, topic])) + def test_no_permissions_check_accepted(self): + namespace = self.parse(["--no-permissions-check"]) + self.assertTrue(namespace.no_permissions_check) + class DefaultTest(unittest.TestCase): """Tests for certbot.cli._Default.""" diff --git a/certbot/tests/client_test.py b/certbot/tests/client_test.py index 711346dbb..844758ad5 100644 --- a/certbot/tests/client_test.py +++ b/certbot/tests/client_test.py @@ -564,6 +564,21 @@ class EnhanceConfigTest(ClientTestCommon): self.assertEqual(mock_log.warning.call_args[0][1], 'redirect') + @mock.patch("certbot.client.logger") + def test_config_set_no_warning_redirect(self, mock_log): + self.config.redirect = False + self._test_with_already_existing() + self.assertFalse(mock_log.warning.called) + + @mock.patch("certbot.client.enhancements.ask") + @mock.patch("certbot.client.logger") + def test_warn_redirect(self, mock_log, mock_ask): + self.config.redirect = None + mock_ask.return_value = False + self._test_with_already_existing() + self.assertTrue(mock_log.warning.called) + self.assertTrue("disable" in mock_log.warning.call_args[0][0]) + def test_no_ask_hsts(self): self.config.hsts = True self._test_with_all_supported() diff --git a/certbot/util.py b/certbot/util.py index e15d02779..66e5d2524 100644 --- a/certbot/util.py +++ b/certbot/util.py @@ -323,7 +323,7 @@ def get_os_info(filepath="/etc/os-release"): # Systemd os-release parsing might be viable os_name, os_version = get_systemd_os_info(filepath=filepath) if os_name: - return (os_name, os_version) + return os_name, os_version # Fallback to platform module return get_python_os_info() diff --git a/docs/ciphers.rst b/docs/ciphers.rst index b748dd87a..c3d6abc42 100644 --- a/docs/ciphers.rst +++ b/docs/ciphers.rst @@ -286,7 +286,7 @@ https://weakdh.org/sysadmin.html These lists may have been derived from Mozilla's recommendations. One of the authors clarified his view of the priorities for various changes as a result of the research at -https://www.ietf.org/mail-archive/web/tls/current/msg16496.html +https://web.archive.org/web/20150526022820/https://www.ietf.org/mail-archive/web/tls/current/msg16496.html In particular, he supports ECDHE and also supports the use of the standardized groups in the FF-DHE Internet-Draft mentioned above (which isn't clear from the group's original recommendations). diff --git a/docs/cli-help.txt b/docs/cli-help.txt index e0979b989..da5b51d3c 100644 --- a/docs/cli-help.txt +++ b/docs/cli-help.txt @@ -113,7 +113,7 @@ optional arguments: case, and to know when to deprecate support for past Python versions and flags. If you wish to hide this information from the Let's Encrypt server, set this to - "". (default: CertbotACMEClient/0.33.1 + "". (default: CertbotACMEClient/0.34.0 (certbot(-auto); OS_NAME OS_VERSION) Authenticator/XXX Installer/YYY (SUBCOMMAND; flags: FLAGS) Py/major.minor.patchlevel). The flags encoded in the @@ -171,6 +171,10 @@ automation: from installing OS-level dependencies (default: Prompt to install OS-wide dependencies, but exit if the user says 'No') + --no-permissions-check + (certbot-auto only) skip the check on the file system + permissions of the certbot-auto script (default: + False) -q, --quiet Silence all output except errors. Useful for automation via cron. Implies --non-interactive. (default: False) diff --git a/letsencrypt-auto b/letsencrypt-auto index d60bdbc70..0d9606372 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.33.1" +LE_AUTO_VERSION="0.34.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -45,6 +45,7 @@ Help for certbot itself cannot be provided until it is installed. -h, --help print this help -n, --non-interactive, --noninteractive run without asking for user input --no-bootstrap do not install OS dependencies + --no-permissions-check do not warn about file system permissions --no-self-upgrade do not download updates --os-packages-only install OS dependencies and exit --install-only install certbot, upgrade if needed, and exit @@ -67,6 +68,8 @@ for arg in "$@" ; do # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) NO_SELF_UPGRADE=1;; + --no-permissions-check) + NO_PERMISSIONS_CHECK=1;; --no-bootstrap) NO_BOOTSTRAP=1;; --help) @@ -172,7 +175,11 @@ SetRootAuthMechanism() { sudo) SUDO="sudo -E" ;; - '') ;; # Nothing to do for plain root method. + '') + # If we're not running with root, don't check that this script can only + # be modified by system users and groups. + NO_PERMISSIONS_CHECK=1 + ;; *) error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'." exit 1 @@ -534,7 +541,7 @@ BootstrapSuseCommon() { # Since Leap 15.0 (and associated Tumbleweed version), python-virtualenv # is a source package, and python2-virtualenv must be used instead. # Also currently python2-setuptools is not a dependency of python2-virtualenv, - # while it should be. Installing it explicitly until upstreqm fix. + # while it should be. Installing it explicitly until upstream fix. OPENSUSE_VIRTUALENV_PACKAGES="python2-virtualenv python2-setuptools" fi @@ -1138,9 +1145,9 @@ requests-toolbelt==0.9.1 \ six==1.12.0 \ --hash=sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c \ --hash=sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73 -urllib3==1.24.1 \ - --hash=sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39 \ - --hash=sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22 +urllib3==1.24.2 \ + --hash=sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0 \ + --hash=sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3 zope.component==4.5 \ --hash=sha256:6edfd626c3b593b72895a8cfcf79bff41f4619194ce996a85bce31ac02b94e55 \ --hash=sha256:984a06ba3def0b02b1117fa4c45b56e772e8c29c0340820fbf367e440a93a3a4 @@ -1218,18 +1225,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.33.1 \ - --hash=sha256:e2a08467146b7a7ed2c8ca6625b1705d93b51e89866f6ede8a8a262594c18f3f \ - --hash=sha256:d5203f32c50f3ec5a32df97e4affddbcd288a569678ecb5669adda21cd5ac3d9 -acme==0.33.1 \ - --hash=sha256:02467d4b1d246105d6d1ea01822dd9e2eea5bf3a50607523969d8e400d53c07b \ - --hash=sha256:b38cdb71d0071efe1f1190a744f8f95f3c698b76ac0f5d919bbfe3522e277a82 -certbot-apache==0.33.1 \ - --hash=sha256:0d2a463539e6396de2d374de62faba34e1fe40dd8059e3c64dcd5dabaa66887b \ - --hash=sha256:659db7335d919fee52ae707567994e13c31ed25109c94b246c60c97d21c46f3a -certbot-nginx==0.33.1 \ - --hash=sha256:df9fb86e735eb2668e070f20317e85c37952f3f612fa7f6bbc2c63784b213f28 \ - --hash=sha256:b3201eee03be74fc743c21c721d3b5586c3323db63e78b68583a6250ad680cff +certbot==0.34.0 \ + --hash=sha256:51dddf2cb1c50a9f8b993090890bf4858d8fadffce38bafcdf6bf585a2040317 \ + --hash=sha256:e75bdabfd9183bd9842ada42a51070f120d15982e81c490df59dde62e4df2c8b +acme==0.34.0 \ + --hash=sha256:3448024d2c274aebfb9b31b53862576d167626ce2fd1997a78d450c32a292fa3 \ + --hash=sha256:92478e58f541c5c7c527427a50650005cdede799b78f0a0a65b8093d6368bcfd +certbot-apache==0.34.0 \ + --hash=sha256:79e686f25b63dac17d771d71f791f252774da22125f3f6e0665f4cf791d516fe \ + --hash=sha256:d5ae09b4801fbac23d5acf64a5ee265108199d2852fbe743e7b6ab06fa08edf6 +certbot-nginx==0.34.0 \ + --hash=sha256:868d7dcb59bb2548cb4a2ae187db5da1bfe33aac306b1b844b96ee00a39cac52 \ + --hash=sha256:d6c728b85c523711ec0dc800f8d4ebbef192fb0ca1ec7914c173207e4aba5194 UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -1494,6 +1501,108 @@ else exit 0 fi + DeterminePythonVersion "NOCRASH" + # Don't warn about file permissions if the user disabled the check or we + # can't find an up-to-date Python. + if [ "$PYVER" -ge "$MIN_PYVER" -a "$NO_PERMISSIONS_CHECK" != 1 ]; then + # --------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/check_permissions.py" +"""Verifies certbot-auto cannot be modified by unprivileged users. + +This script takes the path to certbot-auto as its only command line +argument. It then checks that the file can only be modified by uid/gid +< 1000 and if other users can modify the file, it prints a warning with +a suggestion on how to solve the problem. + +Permissions on symlinks in the absolute path of certbot-auto are ignored +and only the canonical path to certbot-auto is checked. There could be +permissions problems due to the symlinks that are unreported by this +script, however, issues like this were not caused by our documentation +and are ignored for the sake of simplicity. + +All warnings are printed to stdout rather than stderr so all stderr +output from this script can be suppressed to avoid printing messages if +this script fails for some reason. + +""" +from __future__ import print_function + +import os +import stat +import sys + + +FORUM_POST_URL = 'https://community.letsencrypt.org/t/certbot-auto-deployment-best-practices/91979/' + + +def has_safe_permissions(path): + """Returns True if the given path has secure permissions. + + The permissions are considered safe if the file is only writable by + uid/gid < 1000. + + The reason we allow more IDs than 0 is because on some systems such + as Debian, system users/groups other than uid/gid 0 are used for the + path we recommend in our instructions which is /usr/local/bin. 1000 + was chosen because on Debian 0-999 is reserved for system IDs[1] and + on RHEL either 0-499 or 0-999 is reserved depending on the + version[2][3]. Due to these differences across different OSes, this + detection isn't perfect so we only determine permissions are + insecure when we can be reasonably confident there is a problem + regardless of the underlying OS. + + [1] https://www.debian.org/doc/debian-policy/ch-opersys.html#uid-and-gid-classes + [2] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/ch-managing_users_and_groups + [3] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-managing_users_and_groups + + :param str path: filesystem path to check + :returns: True if the path has secure permissions, otherwise, False + :rtype: bool + + """ + # os.stat follows symlinks before obtaining information about a file. + stat_result = os.stat(path) + if stat_result.st_mode & stat.S_IWOTH: + return False + if stat_result.st_mode & stat.S_IWGRP and stat_result.st_gid >= 1000: + return False + if stat_result.st_mode & stat.S_IWUSR and stat_result.st_uid >= 1000: + return False + return True + + +def main(certbot_auto_path): + current_path = os.path.realpath(certbot_auto_path) + last_path = None + permissions_ok = True + # This loop makes use of the fact that os.path.dirname('/') == '/'. + while current_path != last_path and permissions_ok: + permissions_ok = has_safe_permissions(current_path) + last_path = current_path + current_path = os.path.dirname(current_path) + + if not permissions_ok: + print('{0} has insecure permissions!'.format(certbot_auto_path)) + print('To learn how to fix them, visit {0}'.format(FORUM_POST_URL)) + + +if __name__ == '__main__': + main(sys.argv[1]) + +UNLIKELY_EOF + # --------------------------------------------------------------------------- + # If the script fails for some reason, don't break certbot-auto. + set +e + # Suppress unexpected error output and only print the script's output if it + # ran successfully. + CHECK_PERM_OUT=$("$LE_PYTHON" "$TEMP_DIR/check_permissions.py" "$0" 2>/dev/null) + CHECK_PERM_STATUS="$?" + set -e + if [ "$CHECK_PERM_STATUS" = 0 ]; then + error "$CHECK_PERM_OUT" + fi + fi + if [ "$NO_SELF_UPGRADE" != 1 ]; then TEMP_DIR=$(TempDir) trap 'rm -rf "$TEMP_DIR"' EXIT @@ -1650,7 +1759,6 @@ if __name__ == '__main__': UNLIKELY_EOF # --------------------------------------------------------------------------- - DeterminePythonVersion "NOCRASH" if [ "$PYVER" -lt "$MIN_PYVER" ]; then error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates." elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc index 9e55d6cbb..0b6fb32dc 100644 --- a/letsencrypt-auto-source/certbot-auto.asc +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- -iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlymhBYACgkQTRfJlc2X -dfKmDAf/bkoGkWpxgzKjfd7BELnvhZduQ5Y30P2+Kq43jnop56zjZrt53tRsKeOc -Rat2Rq3e/rozlo5ie939iF2UPIX8fzEQ/IIyk4Om17dJ9ld25hteX7HWJThUX9+t -OtKA0c7jw7nSrCmWjKtGhZoTe2nsMqAtp0LV7kZ7T7Ex0HAxjrYu48wA2h6lgloe -65rXyBDVHdVc3FvevUiHKYkt+SONyWuRZpeQ8xn6YSQNDwYzCub3ro1h55GYfOK2 -65eklH1xVo7TvvR0Wo7l1/hIiK8Gz6ZX5dqDaxHT817zO1cqB4HhkHAl2O3q7TCo -JIo1jxMzlttRGJaegwnMTi20KyimyA== -=8Gjd +iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlzKCkUACgkQTRfJlc2X +dfL8xwf/Sjxb5LWkbvVem9Mc8w76D4DKECQdUdwJJCPrvgkBy2LAXYmpy4ZEBETV +p+QuUk2EuUxBNc81Wdo3PNdoA3eDd8uaxMc/GPCRxSWNH/taqL0Xk7s6Jqhx6rh+ +tQNnJoTmqgWaUwQkfJXiiwlcvIdFjdOoQgZnP3YJaNVrlIi6rd4mDJ1dU7ik2Qvz +pI78mCfHokhvq1tWUFram12z045n4/lZ9uy/auA2VFnAmUvh/18h1VSTEoWJK2vW +Xuxv59G1vtG+cC4jzenMho0oVt18hdqQPOaUstzPhS9XxFuyvYMurHusZ4fysnbQ +cUofX1hY0jmaGkMHBkfjtJfdbOQXUg== +=jqpL -----END PGP SIGNATURE----- diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 15305da39..4e1503715 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.34.0.dev0" +LE_AUTO_VERSION="0.35.0.dev0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -45,6 +45,7 @@ Help for certbot itself cannot be provided until it is installed. -h, --help print this help -n, --non-interactive, --noninteractive run without asking for user input --no-bootstrap do not install OS dependencies + --no-permissions-check do not warn about file system permissions --no-self-upgrade do not download updates --os-packages-only install OS dependencies and exit --install-only install certbot, upgrade if needed, and exit @@ -67,6 +68,8 @@ for arg in "$@" ; do # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) NO_SELF_UPGRADE=1;; + --no-permissions-check) + NO_PERMISSIONS_CHECK=1;; --no-bootstrap) NO_BOOTSTRAP=1;; --help) @@ -172,7 +175,11 @@ SetRootAuthMechanism() { sudo) SUDO="sudo -E" ;; - '') ;; # Nothing to do for plain root method. + '') + # If we're not running with root, don't check that this script can only + # be modified by system users and groups. + NO_PERMISSIONS_CHECK=1 + ;; *) error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'." exit 1 @@ -1218,18 +1225,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.33.1 \ - --hash=sha256:e2a08467146b7a7ed2c8ca6625b1705d93b51e89866f6ede8a8a262594c18f3f \ - --hash=sha256:d5203f32c50f3ec5a32df97e4affddbcd288a569678ecb5669adda21cd5ac3d9 -acme==0.33.1 \ - --hash=sha256:02467d4b1d246105d6d1ea01822dd9e2eea5bf3a50607523969d8e400d53c07b \ - --hash=sha256:b38cdb71d0071efe1f1190a744f8f95f3c698b76ac0f5d919bbfe3522e277a82 -certbot-apache==0.33.1 \ - --hash=sha256:0d2a463539e6396de2d374de62faba34e1fe40dd8059e3c64dcd5dabaa66887b \ - --hash=sha256:659db7335d919fee52ae707567994e13c31ed25109c94b246c60c97d21c46f3a -certbot-nginx==0.33.1 \ - --hash=sha256:df9fb86e735eb2668e070f20317e85c37952f3f612fa7f6bbc2c63784b213f28 \ - --hash=sha256:b3201eee03be74fc743c21c721d3b5586c3323db63e78b68583a6250ad680cff +certbot==0.34.0 \ + --hash=sha256:51dddf2cb1c50a9f8b993090890bf4858d8fadffce38bafcdf6bf585a2040317 \ + --hash=sha256:e75bdabfd9183bd9842ada42a51070f120d15982e81c490df59dde62e4df2c8b +acme==0.34.0 \ + --hash=sha256:3448024d2c274aebfb9b31b53862576d167626ce2fd1997a78d450c32a292fa3 \ + --hash=sha256:92478e58f541c5c7c527427a50650005cdede799b78f0a0a65b8093d6368bcfd +certbot-apache==0.34.0 \ + --hash=sha256:79e686f25b63dac17d771d71f791f252774da22125f3f6e0665f4cf791d516fe \ + --hash=sha256:d5ae09b4801fbac23d5acf64a5ee265108199d2852fbe743e7b6ab06fa08edf6 +certbot-nginx==0.34.0 \ + --hash=sha256:868d7dcb59bb2548cb4a2ae187db5da1bfe33aac306b1b844b96ee00a39cac52 \ + --hash=sha256:d6c728b85c523711ec0dc800f8d4ebbef192fb0ca1ec7914c173207e4aba5194 UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -1494,6 +1501,108 @@ else exit 0 fi + DeterminePythonVersion "NOCRASH" + # Don't warn about file permissions if the user disabled the check or we + # can't find an up-to-date Python. + if [ "$PYVER" -ge "$MIN_PYVER" -a "$NO_PERMISSIONS_CHECK" != 1 ]; then + # --------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/check_permissions.py" +"""Verifies certbot-auto cannot be modified by unprivileged users. + +This script takes the path to certbot-auto as its only command line +argument. It then checks that the file can only be modified by uid/gid +< 1000 and if other users can modify the file, it prints a warning with +a suggestion on how to solve the problem. + +Permissions on symlinks in the absolute path of certbot-auto are ignored +and only the canonical path to certbot-auto is checked. There could be +permissions problems due to the symlinks that are unreported by this +script, however, issues like this were not caused by our documentation +and are ignored for the sake of simplicity. + +All warnings are printed to stdout rather than stderr so all stderr +output from this script can be suppressed to avoid printing messages if +this script fails for some reason. + +""" +from __future__ import print_function + +import os +import stat +import sys + + +FORUM_POST_URL = 'https://community.letsencrypt.org/t/certbot-auto-deployment-best-practices/91979/' + + +def has_safe_permissions(path): + """Returns True if the given path has secure permissions. + + The permissions are considered safe if the file is only writable by + uid/gid < 1000. + + The reason we allow more IDs than 0 is because on some systems such + as Debian, system users/groups other than uid/gid 0 are used for the + path we recommend in our instructions which is /usr/local/bin. 1000 + was chosen because on Debian 0-999 is reserved for system IDs[1] and + on RHEL either 0-499 or 0-999 is reserved depending on the + version[2][3]. Due to these differences across different OSes, this + detection isn't perfect so we only determine permissions are + insecure when we can be reasonably confident there is a problem + regardless of the underlying OS. + + [1] https://www.debian.org/doc/debian-policy/ch-opersys.html#uid-and-gid-classes + [2] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/ch-managing_users_and_groups + [3] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-managing_users_and_groups + + :param str path: filesystem path to check + :returns: True if the path has secure permissions, otherwise, False + :rtype: bool + + """ + # os.stat follows symlinks before obtaining information about a file. + stat_result = os.stat(path) + if stat_result.st_mode & stat.S_IWOTH: + return False + if stat_result.st_mode & stat.S_IWGRP and stat_result.st_gid >= 1000: + return False + if stat_result.st_mode & stat.S_IWUSR and stat_result.st_uid >= 1000: + return False + return True + + +def main(certbot_auto_path): + current_path = os.path.realpath(certbot_auto_path) + last_path = None + permissions_ok = True + # This loop makes use of the fact that os.path.dirname('/') == '/'. + while current_path != last_path and permissions_ok: + permissions_ok = has_safe_permissions(current_path) + last_path = current_path + current_path = os.path.dirname(current_path) + + if not permissions_ok: + print('{0} has insecure permissions!'.format(certbot_auto_path)) + print('To learn how to fix them, visit {0}'.format(FORUM_POST_URL)) + + +if __name__ == '__main__': + main(sys.argv[1]) + +UNLIKELY_EOF + # --------------------------------------------------------------------------- + # If the script fails for some reason, don't break certbot-auto. + set +e + # Suppress unexpected error output and only print the script's output if it + # ran successfully. + CHECK_PERM_OUT=$("$LE_PYTHON" "$TEMP_DIR/check_permissions.py" "$0" 2>/dev/null) + CHECK_PERM_STATUS="$?" + set -e + if [ "$CHECK_PERM_STATUS" = 0 ]; then + error "$CHECK_PERM_OUT" + fi + fi + if [ "$NO_SELF_UPGRADE" != 1 ]; then TEMP_DIR=$(TempDir) trap 'rm -rf "$TEMP_DIR"' EXIT @@ -1650,7 +1759,6 @@ if __name__ == '__main__': UNLIKELY_EOF # --------------------------------------------------------------------------- - DeterminePythonVersion "NOCRASH" if [ "$PYVER" -lt "$MIN_PYVER" ]; then error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates." elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 96adf9078..3afa861cd 100644 Binary files a/letsencrypt-auto-source/letsencrypt-auto.sig and b/letsencrypt-auto-source/letsencrypt-auto.sig differ diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index a5a29c483..21db0f908 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -45,6 +45,7 @@ Help for certbot itself cannot be provided until it is installed. -h, --help print this help -n, --non-interactive, --noninteractive run without asking for user input --no-bootstrap do not install OS dependencies + --no-permissions-check do not warn about file system permissions --no-self-upgrade do not download updates --os-packages-only install OS dependencies and exit --install-only install certbot, upgrade if needed, and exit @@ -67,6 +68,8 @@ for arg in "$@" ; do # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) NO_SELF_UPGRADE=1;; + --no-permissions-check) + NO_PERMISSIONS_CHECK=1;; --no-bootstrap) NO_BOOTSTRAP=1;; --help) @@ -172,7 +175,11 @@ SetRootAuthMechanism() { sudo) SUDO="sudo -E" ;; - '') ;; # Nothing to do for plain root method. + '') + # If we're not running with root, don't check that this script can only + # be modified by system users and groups. + NO_PERMISSIONS_CHECK=1 + ;; *) error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'." exit 1 @@ -652,6 +659,27 @@ else exit 0 fi + DeterminePythonVersion "NOCRASH" + # Don't warn about file permissions if the user disabled the check or we + # can't find an up-to-date Python. + if [ "$PYVER" -ge "$MIN_PYVER" -a "$NO_PERMISSIONS_CHECK" != 1 ]; then + # --------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/check_permissions.py" +{{ check_permissions.py }} +UNLIKELY_EOF + # --------------------------------------------------------------------------- + # If the script fails for some reason, don't break certbot-auto. + set +e + # Suppress unexpected error output and only print the script's output if it + # ran successfully. + CHECK_PERM_OUT=$("$LE_PYTHON" "$TEMP_DIR/check_permissions.py" "$0" 2>/dev/null) + CHECK_PERM_STATUS="$?" + set -e + if [ "$CHECK_PERM_STATUS" = 0 ]; then + error "$CHECK_PERM_OUT" + fi + fi + if [ "$NO_SELF_UPGRADE" != 1 ]; then TEMP_DIR=$(TempDir) trap 'rm -rf "$TEMP_DIR"' EXIT @@ -660,7 +688,6 @@ else {{ fetch.py }} UNLIKELY_EOF # --------------------------------------------------------------------------- - DeterminePythonVersion "NOCRASH" if [ "$PYVER" -lt "$MIN_PYVER" ]; then error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates." elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then diff --git a/letsencrypt-auto-source/pieces/certbot-requirements.txt b/letsencrypt-auto-source/pieces/certbot-requirements.txt index cb79bccd7..d37b22069 100644 --- a/letsencrypt-auto-source/pieces/certbot-requirements.txt +++ b/letsencrypt-auto-source/pieces/certbot-requirements.txt @@ -1,12 +1,12 @@ -certbot==0.33.1 \ - --hash=sha256:e2a08467146b7a7ed2c8ca6625b1705d93b51e89866f6ede8a8a262594c18f3f \ - --hash=sha256:d5203f32c50f3ec5a32df97e4affddbcd288a569678ecb5669adda21cd5ac3d9 -acme==0.33.1 \ - --hash=sha256:02467d4b1d246105d6d1ea01822dd9e2eea5bf3a50607523969d8e400d53c07b \ - --hash=sha256:b38cdb71d0071efe1f1190a744f8f95f3c698b76ac0f5d919bbfe3522e277a82 -certbot-apache==0.33.1 \ - --hash=sha256:0d2a463539e6396de2d374de62faba34e1fe40dd8059e3c64dcd5dabaa66887b \ - --hash=sha256:659db7335d919fee52ae707567994e13c31ed25109c94b246c60c97d21c46f3a -certbot-nginx==0.33.1 \ - --hash=sha256:df9fb86e735eb2668e070f20317e85c37952f3f612fa7f6bbc2c63784b213f28 \ - --hash=sha256:b3201eee03be74fc743c21c721d3b5586c3323db63e78b68583a6250ad680cff +certbot==0.34.0 \ + --hash=sha256:51dddf2cb1c50a9f8b993090890bf4858d8fadffce38bafcdf6bf585a2040317 \ + --hash=sha256:e75bdabfd9183bd9842ada42a51070f120d15982e81c490df59dde62e4df2c8b +acme==0.34.0 \ + --hash=sha256:3448024d2c274aebfb9b31b53862576d167626ce2fd1997a78d450c32a292fa3 \ + --hash=sha256:92478e58f541c5c7c527427a50650005cdede799b78f0a0a65b8093d6368bcfd +certbot-apache==0.34.0 \ + --hash=sha256:79e686f25b63dac17d771d71f791f252774da22125f3f6e0665f4cf791d516fe \ + --hash=sha256:d5ae09b4801fbac23d5acf64a5ee265108199d2852fbe743e7b6ab06fa08edf6 +certbot-nginx==0.34.0 \ + --hash=sha256:868d7dcb59bb2548cb4a2ae187db5da1bfe33aac306b1b844b96ee00a39cac52 \ + --hash=sha256:d6c728b85c523711ec0dc800f8d4ebbef192fb0ca1ec7914c173207e4aba5194 diff --git a/letsencrypt-auto-source/pieces/check_permissions.py b/letsencrypt-auto-source/pieces/check_permissions.py new file mode 100644 index 000000000..ba55e6d97 --- /dev/null +++ b/letsencrypt-auto-source/pieces/check_permissions.py @@ -0,0 +1,81 @@ +"""Verifies certbot-auto cannot be modified by unprivileged users. + +This script takes the path to certbot-auto as its only command line +argument. It then checks that the file can only be modified by uid/gid +< 1000 and if other users can modify the file, it prints a warning with +a suggestion on how to solve the problem. + +Permissions on symlinks in the absolute path of certbot-auto are ignored +and only the canonical path to certbot-auto is checked. There could be +permissions problems due to the symlinks that are unreported by this +script, however, issues like this were not caused by our documentation +and are ignored for the sake of simplicity. + +All warnings are printed to stdout rather than stderr so all stderr +output from this script can be suppressed to avoid printing messages if +this script fails for some reason. + +""" +from __future__ import print_function + +import os +import stat +import sys + + +FORUM_POST_URL = 'https://community.letsencrypt.org/t/certbot-auto-deployment-best-practices/91979/' + + +def has_safe_permissions(path): + """Returns True if the given path has secure permissions. + + The permissions are considered safe if the file is only writable by + uid/gid < 1000. + + The reason we allow more IDs than 0 is because on some systems such + as Debian, system users/groups other than uid/gid 0 are used for the + path we recommend in our instructions which is /usr/local/bin. 1000 + was chosen because on Debian 0-999 is reserved for system IDs[1] and + on RHEL either 0-499 or 0-999 is reserved depending on the + version[2][3]. Due to these differences across different OSes, this + detection isn't perfect so we only determine permissions are + insecure when we can be reasonably confident there is a problem + regardless of the underlying OS. + + [1] https://www.debian.org/doc/debian-policy/ch-opersys.html#uid-and-gid-classes + [2] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/ch-managing_users_and_groups + [3] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-managing_users_and_groups + + :param str path: filesystem path to check + :returns: True if the path has secure permissions, otherwise, False + :rtype: bool + + """ + # os.stat follows symlinks before obtaining information about a file. + stat_result = os.stat(path) + if stat_result.st_mode & stat.S_IWOTH: + return False + if stat_result.st_mode & stat.S_IWGRP and stat_result.st_gid >= 1000: + return False + if stat_result.st_mode & stat.S_IWUSR and stat_result.st_uid >= 1000: + return False + return True + + +def main(certbot_auto_path): + current_path = os.path.realpath(certbot_auto_path) + last_path = None + permissions_ok = True + # This loop makes use of the fact that os.path.dirname('/') == '/'. + while current_path != last_path and permissions_ok: + permissions_ok = has_safe_permissions(current_path) + last_path = current_path + current_path = os.path.dirname(current_path) + + if not permissions_ok: + print('{0} has insecure permissions!'.format(certbot_auto_path)) + print('To learn how to fix them, visit {0}'.format(FORUM_POST_URL)) + + +if __name__ == '__main__': + main(sys.argv[1]) diff --git a/letsencrypt-auto-source/rebuild_dependencies.py b/letsencrypt-auto-source/rebuild_dependencies.py index 22c89fae6..7096e226c 100755 --- a/letsencrypt-auto-source/rebuild_dependencies.py +++ b/letsencrypt-auto-source/rebuild_dependencies.py @@ -217,7 +217,12 @@ def _write_requirements(dest_file, requirements, conflicts): # To generate this, do (with docker and package hashin installed): # ``` # letsencrypt-auto-source/rebuild_dependencies.py \\ -# letsencrypt-auto-sources/pieces/dependency-requirements.txt +# letsencrypt-auto-source/pieces/dependency-requirements.txt +# ``` +# If you want to update a single dependency, run commands similar to these: +# ``` +# pip install hashin +# hashin -r dependency-requirements.txt cryptography==1.5.2 # ``` ''') diff --git a/letsencrypt-auto-source/tests/auto_test.py b/letsencrypt-auto-source/tests/auto_test.py index 16c478f20..9c823fb55 100644 --- a/letsencrypt-auto-source/tests/auto_test.py +++ b/letsencrypt-auto-source/tests/auto_test.py @@ -4,13 +4,13 @@ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from contextlib import contextmanager from functools import partial from json import dumps -from os import chmod, environ, makedirs +from os import chmod, environ, makedirs, stat from os.path import abspath, dirname, exists, join import re from shutil import copy, rmtree import socket import ssl -from stat import S_IRUSR, S_IXUSR +from stat import S_IMODE, S_IRUSR, S_IWUSR, S_IXUSR, S_IWGRP, S_IWOTH from subprocess import CalledProcessError, Popen, PIPE import sys from tempfile import mkdtemp @@ -192,7 +192,7 @@ def install_le_auto(contents, install_path): chmod(install_path, S_IRUSR | S_IXUSR) -def run_le_auto(le_auto_path, venv_dir, base_url, **kwargs): +def run_le_auto(le_auto_path, venv_dir, base_url=None, le_auto_args_str='--version', **kwargs): """Run the prebuilt version of letsencrypt-auto, returning stdout and stderr strings. @@ -201,13 +201,17 @@ def run_le_auto(le_auto_path, venv_dir, base_url, **kwargs): """ env = environ.copy() d = dict(VENV_PATH=venv_dir, - # URL to PyPI-style JSON that tell us the latest released version - # of LE: - LE_AUTO_JSON_URL=base_url + 'certbot/json', - # URL to dir containing letsencrypt-auto and letsencrypt-auto.sig: - LE_AUTO_DIR_TEMPLATE=base_url + '%s/', - # The public key corresponding to signing.key: - LE_AUTO_PUBLIC_KEY="""-----BEGIN PUBLIC KEY----- + NO_CERT_VERIFY='1', + **kwargs) + + if base_url is not None: + # URL to PyPI-style JSON that tell us the latest released version + # of LE: + d['LE_AUTO_JSON_URL'] = base_url + 'certbot/json' + # URL to dir containing letsencrypt-auto and letsencrypt-auto.sig: + d['LE_AUTO_DIR_TEMPLATE'] = base_url + '%s/' + # The public key corresponding to signing.key: + d['LE_AUTO_PUBLIC_KEY'] = """-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMoSzLYQ7E1sdSOkwelg tzKIh2qi3bpXuYtcfFC0XrvWig071NwIj+dZiT0OLZ2hPispEH0B7ISuuWg1ll7G hFW0VdbxL6JdGzS2ShNWkX9hE9z+j8VqwDPOBn3ZHm03qwpYkBDwQib3KqOdYbTT @@ -215,12 +219,12 @@ uUtJmmGcuk3a9Aq/sCT6DdfmTSdP5asdQYwIcaQreDrOosaS84DTWI3IU+UYJVgl LsIVPBuy9IcgHidUQ96hJnoPsDCWsHwX62495QKEarauyKQrJzFes0EY95orDM47 Z5o/NDiQB11m91yNB0MmPYY9QSbnOA9j7IaaC97AwRLuwXY+/R2ablTcxurWou68 iQIDAQAB ------END PUBLIC KEY-----""", - NO_CERT_VERIFY='1', - **kwargs) +-----END PUBLIC KEY-----""" + env.update(d) + return out_and_err( - le_auto_path + ' --version', + le_auto_path + ' ' + le_auto_args_str, shell=True, env=env) @@ -240,6 +244,12 @@ def set_le_script_version(venv_dir, version): chmod(letsencrypt_path, S_IRUSR | S_IXUSR) +def sudo_chmod(path, mode): + """Runs `sudo chmod mode path`.""" + mode = oct(mode).replace('o', '') + out_and_err(['sudo', 'chmod', mode, path]) + + class AutoTests(TestCase): """Test the major branch points of letsencrypt-auto: @@ -395,3 +405,95 @@ class AutoTests(TestCase): else: self.fail("Pip didn't detect a bad hash and stop the " "installation.") + + def test_permissions_warnings(self): + """Make sure letsencrypt-auto properly warns about permissions problems.""" + # This test assumes that only the parent of the directory containing + # letsencrypt-auto (usually /tmp) may have permissions letsencrypt-auto + # considers insecure. + with temp_paths() as (le_auto_path, venv_dir): + le_auto_path = abspath(le_auto_path) + le_auto_dir = dirname(le_auto_path) + le_auto_dir_parent = dirname(le_auto_dir) + install_le_auto(self.NEW_LE_AUTO, le_auto_path) + + run_letsencrypt_auto = partial( + run_le_auto, le_auto_path, venv_dir, + le_auto_args_str='--install-only --no-self-upgrade', + PIP_FIND_LINKS=join(tests_dir(), 'fake-letsencrypt', 'dist')) + # Run letsencrypt-auto once with current permissions to avoid + # potential problems when the script tries to write to temporary + # directories. + run_letsencrypt_auto() + + le_auto_dir_mode = stat(le_auto_dir).st_mode + le_auto_dir_parent_mode = S_IMODE(stat(le_auto_dir_parent).st_mode) + try: + # Make letsencrypt-auto happy with the current permissions + chmod(le_auto_dir, S_IRUSR | S_IXUSR) + sudo_chmod(le_auto_dir_parent, 0o755) + + self._test_permissions_warnings_about_path(le_auto_path, run_letsencrypt_auto) + self._test_permissions_warnings_about_path(le_auto_dir, run_letsencrypt_auto) + finally: + chmod(le_auto_dir, le_auto_dir_mode) + sudo_chmod(le_auto_dir_parent, le_auto_dir_parent_mode) + + def _test_permissions_warnings_about_path(self, path, run_le_auto_func): + # Test that there are no problems with the current permissions + out, _ = run_le_auto_func() + self.assertFalse('insecure permissions' in out) + + stat_result = stat(path) + original_mode = stat_result.st_mode + + # Test world permissions + chmod(path, original_mode | S_IWOTH) + out, _ = run_le_auto_func() + self.assertTrue('insecure permissions' in out) + + # Test group permissions + if stat_result.st_gid >= 1000: + chmod(path, original_mode | S_IWGRP) + out, _ = run_le_auto_func() + self.assertTrue('insecure permissions' in out) + + # Test owner permissions + if stat_result.st_uid >= 1000: + chmod(path, original_mode | S_IWUSR) + out, _ = run_le_auto_func() + self.assertTrue('insecure permissions' in out) + + # Test that permissions were properly restored + chmod(path, original_mode) + out, _ = run_le_auto_func() + self.assertFalse('insecure permissions' in out) + + def test_disabled_permissions_warnings(self): + """Make sure that letsencrypt-auto permissions warnings can be disabled.""" + with temp_paths() as (le_auto_path, venv_dir): + le_auto_path = abspath(le_auto_path) + install_le_auto(self.NEW_LE_AUTO, le_auto_path) + + le_auto_args_str='--install-only --no-self-upgrade' + pip_links=join(tests_dir(), 'fake-letsencrypt', 'dist') + out, _ = run_le_auto(le_auto_path, venv_dir, + le_auto_args_str=le_auto_args_str, + PIP_FIND_LINKS=pip_links) + self.assertTrue('insecure permissions' in out) + + # Test that warnings are disabled when the script isn't run as + # root. + out, _ = run_le_auto(le_auto_path, venv_dir, + le_auto_args_str=le_auto_args_str, + LE_AUTO_SUDO='', + PIP_FIND_LINKS=pip_links) + self.assertFalse('insecure permissions' in out) + + # Test that --no-permissions-check disables warnings. + le_auto_args_str += ' --no-permissions-check' + out, _ = run_le_auto( + le_auto_path, venv_dir, + le_auto_args_str=le_auto_args_str, + PIP_FIND_LINKS=pip_links) + self.assertFalse('insecure permissions' in out) diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index b8ae937ad..430acb634 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -98,7 +98,7 @@ PROFILE = cl_args.aws_profile # Globals #------------------------------------------------------------------------------- -BOULDER_AMI = 'ami-5f490b35' # premade shared boulder AMI 14.04LTS us-east-1 +BOULDER_AMI = 'ami-072a9534772bec854' # premade shared boulder AMI 18.04LTS us-east-1 LOGDIR = "" #points to logging / working directory # boto3/AWS api globals AWS_SESSION = None @@ -290,8 +290,7 @@ def deploy_script(scriptpath, *args): 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 &') + run('sudo docker-compose up -d') def config_and_launch_boulder(instance): execute(deploy_script, 'scripts/boulder_config.sh') diff --git a/tests/letstest/scripts/boulder_config.sh b/tests/letstest/scripts/boulder_config.sh index 1ef63ca10..b99bbabbe 100755 --- a/tests/letstest/scripts/boulder_config.sh +++ b/tests/letstest/scripts/boulder_config.sh @@ -1,32 +1,24 @@ #!/bin/bash -x # Configures and Launches Boulder Server installed on -# us-east-1 ami-5f490b35 bouldertestserver (boulder commit 8b433f54dab) +# us-east-1 ami-072a9534772bec854 bouldertestserver3 (boulder commit b24fe7c3ea4) # 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) +# set to public DNS resolver +resolver_ip=8.8.8.8 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 +sed -i '/httpPort/ s/5002/80/' ./test/config/va.json +sed -i '/httpsPort/ s/5001/443/' ./test/config/va.json +sed -i '/tlsPort/ s/5001/443/' ./test/config/va.json +# set dns resolver +sed -i 's/"127.0.0.1:8053",/"'$resolver'"/' ./test/config/va.json +sed -i 's/"127.0.0.1:8054"//' ./test/config/va.json diff --git a/tests/letstest/scripts/test_letsencrypt_auto_certonly_standalone.sh b/tests/letstest/scripts/test_letsencrypt_auto_certonly_standalone.sh index 901d01e4a..035512ef7 100755 --- a/tests/letstest/scripts/test_letsencrypt_auto_certonly_standalone.sh +++ b/tests/letstest/scripts/test_letsencrypt_auto_certonly_standalone.sh @@ -9,7 +9,13 @@ set -eo pipefail #private_ip=$(curl -s http://169.254.169.254/2014-11-05/meta-data/local-ipv4) cd letsencrypt -export PATH="$PWD/letsencrypt-auto-source:$PATH" +LE_AUTO_DIR="/usr/local/bin" +LE_AUTO_PATH="$LE_AUTO_DIR/letsencrypt-auto" +sudo cp letsencrypt-auto-source/letsencrypt-auto "$LE_AUTO_PATH" +sudo chown root "$LE_AUTO_PATH" +sudo chmod 0755 "$LE_AUTO_PATH" +export PATH="$LE_AUTO_DIR:$PATH" + letsencrypt-auto --os-packages-only --debug --version # Create a venv-like layout at the old virtual environment path to test that a @@ -35,3 +41,9 @@ if ! letsencrypt-auto --help --no-self-upgrade | grep -F "letsencrypt-auto [SUBC echo "letsencrypt-auto not included in help output!" exit 1 fi + +OUTPUT=$(letsencrypt-auto --install-only --no-self-upgrade --quiet 2>&1) +if [ -n "$OUTPUT" ]; then + echo letsencrypt-auto produced unexpected output! + exit 1 +fi diff --git a/tests/letstest/targets.yaml b/tests/letstest/targets.yaml index d784071c2..d1eba43de 100644 --- a/tests/letstest/targets.yaml +++ b/tests/letstest/targets.yaml @@ -33,7 +33,7 @@ targets: type: ubuntu virt: hvm user: admin - - ami: ami-116d857a + - ami: ami-077bf3962f29d3fa4 name: debian8.1 type: ubuntu virt: hvm diff --git a/tools/_release.sh b/tools/_release.sh index 7751f15b9..e228bae99 100755 --- a/tools/_release.sh +++ b/tools/_release.sh @@ -109,6 +109,9 @@ SetVersion() { SetVersion "$version" +# Unset CERTBOT_OLDEST to prevent wheels from being built improperly due to +# conditionals like the one found in certbot-dns-dnsimple's setup.py file. +unset CERTBOT_OLDEST echo "Preparing sdists and wheels" for pkg_dir in . $SUBPKGS_NO_CERTBOT do