Merge branch 'master' into certbot-ci-part5

This commit is contained in:
Adrien Ferrand 2019-05-02 11:55:29 +02:00
commit 0b42ab0aba
67 changed files with 1129 additions and 195 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
# ```
''')

View file

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

View file

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

View file

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

View file

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

View file

@ -33,7 +33,7 @@ targets:
type: ubuntu
virt: hvm
user: admin
- ami: ami-116d857a
- ami: ami-077bf3962f29d3fa4
name: debian8.1
type: ubuntu
virt: hvm

View file

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