Compare commits
1 Commits
py3_metacl
...
fix_nginx_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe49734b67 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -38,7 +38,6 @@ tests/letstest/venv/
|
||||
|
||||
# pytest cache
|
||||
.cache
|
||||
.mypy_cache/
|
||||
|
||||
# docker files
|
||||
.docker
|
||||
|
||||
@@ -29,8 +29,6 @@ matrix:
|
||||
addons:
|
||||
- python: "2.7"
|
||||
env: TOXENV=lint
|
||||
- python: "3.5"
|
||||
env: TOXENV=mypy
|
||||
- python: "2.7"
|
||||
env: TOXENV='py27-{acme,apache,certbot,dns,nginx}-oldest'
|
||||
sudo: required
|
||||
|
||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -2,49 +2,6 @@
|
||||
|
||||
Certbot adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 0.23.0 - 2018-04-04
|
||||
|
||||
### Added
|
||||
|
||||
* Support for OpenResty was added to the Nginx plugin.
|
||||
|
||||
### Changed
|
||||
|
||||
* The timestamps in Certbot's logfiles now use the system's local time zone
|
||||
rather than UTC.
|
||||
* Certbot's DNS plugins that use Lexicon now rely on Lexicon>=2.2.1 to be able
|
||||
to create and delete multiple TXT records on a single domain.
|
||||
* certbot-dns-google's test suite now works without an internet connection.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Removed a small window that if during which an error occurred, Certbot
|
||||
wouldn't clean up performed challenges.
|
||||
* The parameters `default` and `ipv6only` are now removed from `listen`
|
||||
directives when creating a new server block in the Nginx plugin.
|
||||
* `server_name` directives enclosed in quotation marks in Nginx are now properly
|
||||
supported.
|
||||
* Resolved an issue preventing the Apache plugin from starting Apache when it's
|
||||
not currently running on RHEL and Gentoo based systems.
|
||||
|
||||
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
|
||||
packages with changes other than their version number were:
|
||||
|
||||
* certbot
|
||||
* certbot-apache
|
||||
* certbot-dns-cloudxns
|
||||
* certbot-dns-dnsimple
|
||||
* certbot-dns-dnsmadeeasy
|
||||
* certbot-dns-google
|
||||
* certbot-dns-luadns
|
||||
* certbot-dns-nsone
|
||||
* certbot-dns-rfc2136
|
||||
* certbot-nginx
|
||||
|
||||
More details about these changes can be found on our GitHub repo:
|
||||
https://github.com/certbot/certbot/milestone/50?closed=1
|
||||
|
||||
## 0.22.2 - 2018-03-19
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM python:2-alpine3.7
|
||||
FROM python:2-alpine
|
||||
|
||||
ENTRYPOINT [ "certbot" ]
|
||||
EXPOSE 80 443
|
||||
|
||||
@@ -435,7 +435,6 @@ class Authorization(ResourceBody):
|
||||
# be absent'... then acme-spec gives example with 'expires'
|
||||
# present... That's confusing!
|
||||
expires = fields.RFC3339Field('expires', omitempty=True)
|
||||
wildcard = jose.Field('wildcard', omitempty=True)
|
||||
|
||||
@challenges.decoder
|
||||
def challenges(value): # pylint: disable=missing-docstring,no-self-argument
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
||||
@@ -323,7 +323,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# Returned objects are guaranteed to be ssl vhosts
|
||||
return self._choose_vhosts_wildcard(domain, create_if_no_ssl)
|
||||
else:
|
||||
return [self.choose_vhost(domain, create_if_no_ssl)]
|
||||
return [self.choose_vhost(domain)]
|
||||
|
||||
def _vhosts_for_wildcard(self, domain):
|
||||
"""
|
||||
@@ -475,21 +475,20 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
if chain_path is not None:
|
||||
self.save_notes += "\tSSLCertificateChainFile %s\n" % chain_path
|
||||
|
||||
def choose_vhost(self, target_name, create_if_no_ssl=True):
|
||||
def choose_vhost(self, target_name, temp=False):
|
||||
"""Chooses a virtual host based on the given domain name.
|
||||
|
||||
If there is no clear virtual host to be selected, the user is prompted
|
||||
with all available choices.
|
||||
|
||||
The returned vhost is guaranteed to have TLS enabled unless
|
||||
create_if_no_ssl is set to False, in which case there is no such guarantee
|
||||
and the result is not cached.
|
||||
The returned vhost is guaranteed to have TLS enabled unless temp is
|
||||
True. If temp is True, there is no such guarantee and the result is
|
||||
not cached.
|
||||
|
||||
:param str target_name: domain name
|
||||
:param bool create_if_no_ssl: If found VirtualHost doesn't have a HTTPS
|
||||
counterpart, should one get created
|
||||
:param bool temp: whether the vhost is only used temporarily
|
||||
|
||||
:returns: vhost associated with name
|
||||
:returns: ssl vhost associated with name
|
||||
:rtype: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:raises .errors.PluginError: If no vhost is available or chosen
|
||||
@@ -502,7 +501,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# Try to find a reasonable vhost
|
||||
vhost = self._find_best_vhost(target_name)
|
||||
if vhost is not None:
|
||||
if not create_if_no_ssl:
|
||||
if temp:
|
||||
return vhost
|
||||
if not vhost.ssl:
|
||||
vhost = self.make_vhost_ssl(vhost)
|
||||
@@ -511,9 +510,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
self.assoc[target_name] = vhost
|
||||
return vhost
|
||||
|
||||
# Negate create_if_no_ssl value to indicate if we want a SSL vhost
|
||||
# to get created if a non-ssl vhost is selected.
|
||||
return self._choose_vhost_from_list(target_name, temp=not create_if_no_ssl)
|
||||
return self._choose_vhost_from_list(target_name, temp)
|
||||
|
||||
def _choose_vhost_from_list(self, target_name, temp=False):
|
||||
# Select a vhost from a list
|
||||
@@ -1508,20 +1505,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
raise errors.PluginError(
|
||||
"Unsupported enhancement: {0}".format(enhancement))
|
||||
|
||||
matched_vhosts = self.choose_vhosts(domain, create_if_no_ssl=False)
|
||||
# We should be handling only SSL vhosts for enhancements
|
||||
vhosts = [vhost for vhost in matched_vhosts if vhost.ssl]
|
||||
|
||||
if not vhosts:
|
||||
msg_tmpl = ("Certbot was not able to find SSL VirtualHost for a "
|
||||
"domain {0} for enabling enhancement \"{1}\". The requested "
|
||||
"enhancement was not configured.")
|
||||
msg_enhancement = enhancement
|
||||
if options:
|
||||
msg_enhancement += ": " + options
|
||||
msg = msg_tmpl.format(domain, msg_enhancement)
|
||||
logger.warning(msg)
|
||||
raise errors.PluginError(msg)
|
||||
vhosts = self.choose_vhosts(domain, create_if_no_ssl=False)
|
||||
try:
|
||||
for vhost in vhosts:
|
||||
func(vhost, options)
|
||||
@@ -2016,27 +2000,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
:raises .errors.MisconfigurationError: If reload fails
|
||||
|
||||
"""
|
||||
error = ""
|
||||
try:
|
||||
util.run_script(self.constant("restart_cmd"))
|
||||
except errors.SubprocessError as err:
|
||||
logger.info("Unable to restart apache using %s",
|
||||
self.constant("restart_cmd"))
|
||||
alt_restart = self.constant("restart_cmd_alt")
|
||||
if alt_restart:
|
||||
logger.debug("Trying alternative restart command: %s",
|
||||
alt_restart)
|
||||
# There is an alternative restart command available
|
||||
# This usually is "restart" verb while original is "graceful"
|
||||
try:
|
||||
util.run_script(self.constant(
|
||||
"restart_cmd_alt"))
|
||||
return
|
||||
except errors.SubprocessError as secerr:
|
||||
error = str(secerr)
|
||||
else:
|
||||
error = str(err)
|
||||
raise errors.MisconfigurationError(error)
|
||||
raise errors.MisconfigurationError(str(err))
|
||||
|
||||
def config_test(self): # pylint: disable=no-self-use
|
||||
"""Check the configuration of Apache for errors.
|
||||
|
||||
@@ -21,7 +21,6 @@ class CentOSConfigurator(configurator.ApacheConfigurator):
|
||||
version_cmd=['apachectl', '-v'],
|
||||
apache_cmd="apachectl",
|
||||
restart_cmd=['apachectl', 'graceful'],
|
||||
restart_cmd_alt=['apachectl', 'restart'],
|
||||
conftest_cmd=['apachectl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
|
||||
@@ -21,7 +21,6 @@ class GentooConfigurator(configurator.ApacheConfigurator):
|
||||
version_cmd=['/usr/sbin/apache2', '-v'],
|
||||
apache_cmd="apache2ctl",
|
||||
restart_cmd=['apache2ctl', 'graceful'],
|
||||
restart_cmd_alt=['apache2ctl', 'restart'],
|
||||
conftest_cmd=['apache2ctl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
|
||||
@@ -4,8 +4,6 @@ import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from certbot import errors
|
||||
|
||||
from certbot_apache import obj
|
||||
from certbot_apache import override_centos
|
||||
from certbot_apache.tests import util
|
||||
@@ -123,17 +121,5 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
|
||||
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.assertEquals(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
|
||||
|
||||
@@ -246,7 +246,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
def test_choose_vhost_select_vhost_with_temp(self, mock_select):
|
||||
mock_select.return_value = self.vh_truth[0]
|
||||
chosen_vhost = self.config.choose_vhost("none.com", create_if_no_ssl=False)
|
||||
chosen_vhost = self.config.choose_vhost("none.com", temp=True)
|
||||
self.assertEqual(self.vh_truth[0], chosen_vhost)
|
||||
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
@@ -936,22 +936,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
errors.PluginError,
|
||||
self.config.enhance, "certbot.demo", "unknown_enhancement")
|
||||
|
||||
def test_enhance_no_ssl_vhost(self):
|
||||
with mock.patch("certbot_apache.configurator.logger.warning") as mock_log:
|
||||
self.assertRaises(errors.PluginError, self.config.enhance,
|
||||
"certbot.demo", "redirect")
|
||||
# Check that correct logger.warning was printed
|
||||
self.assertTrue("not able to find" in mock_log.call_args[0][0])
|
||||
self.assertTrue("\"redirect\"" in mock_log.call_args[0][0])
|
||||
|
||||
mock_log.reset_mock()
|
||||
|
||||
self.assertRaises(errors.PluginError, self.config.enhance,
|
||||
"certbot.demo", "ensure-http-header", "Test")
|
||||
# Check that correct logger.warning was printed
|
||||
self.assertTrue("not able to find" in mock_log.call_args[0][0])
|
||||
self.assertTrue("Test" in mock_log.call_args[0][0])
|
||||
|
||||
@mock.patch("certbot.util.exe_exists")
|
||||
def test_ocsp_stapling(self, mock_exe):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
@@ -961,7 +945,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
mock_exe.return_value = True
|
||||
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
self.config.enhance("certbot.demo", "staple-ocsp")
|
||||
|
||||
# Get the ssl vhost for certbot.demo
|
||||
@@ -988,7 +971,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
mock_exe.return_value = True
|
||||
|
||||
# Checking the case with already enabled ocsp stapling configuration
|
||||
self.config.choose_vhost("ocspvhost.com")
|
||||
self.config.enhance("ocspvhost.com", "staple-ocsp")
|
||||
|
||||
# Get the ssl vhost for letsencrypt.demo
|
||||
@@ -1013,7 +995,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
self.config.parser.modules.add("socache_shmcb_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 2, 0))
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
|
||||
self.assertRaises(errors.PluginError,
|
||||
self.config.enhance, "certbot.demo", "staple-ocsp")
|
||||
@@ -1039,7 +1020,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
mock_exe.return_value = True
|
||||
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
self.config.enhance("certbot.demo", "ensure-http-header",
|
||||
"Strict-Transport-Security")
|
||||
|
||||
@@ -1059,8 +1039,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
# skip the enable mod
|
||||
self.config.parser.modules.add("headers_module")
|
||||
|
||||
# This will create an ssl vhost for encryption-example.demo
|
||||
self.config.choose_vhost("encryption-example.demo")
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("encryption-example.demo", "ensure-http-header",
|
||||
"Strict-Transport-Security")
|
||||
|
||||
@@ -1079,7 +1058,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
mock_exe.return_value = True
|
||||
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
self.config.enhance("certbot.demo", "ensure-http-header",
|
||||
"Upgrade-Insecure-Requests")
|
||||
|
||||
@@ -1101,8 +1079,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
# skip the enable mod
|
||||
self.config.parser.modules.add("headers_module")
|
||||
|
||||
# This will create an ssl vhost for encryption-example.demo
|
||||
self.config.choose_vhost("encryption-example.demo")
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("encryption-example.demo", "ensure-http-header",
|
||||
"Upgrade-Insecure-Requests")
|
||||
|
||||
@@ -1120,7 +1097,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
self.config.get_version = mock.Mock(return_value=(2, 2))
|
||||
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
self.config.enhance("certbot.demo", "redirect")
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
@@ -1171,7 +1147,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
self.config.save()
|
||||
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
self.config.enhance("certbot.demo", "redirect")
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
@@ -1238,9 +1213,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
|
||||
|
||||
# Creates ssl vhost for the domain
|
||||
self.config.choose_vhost("red.blue.purple.com")
|
||||
|
||||
self.config.enhance("red.blue.purple.com", "redirect")
|
||||
verify_no_redirect = ("certbot_apache.configurator."
|
||||
"ApacheConfigurator._verify_no_certbot_redirect")
|
||||
@@ -1252,7 +1224,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
|
||||
self.config.choose_vhost("red.blue.purple.com")
|
||||
|
||||
self.config.enhance("red.blue.purple.com", "redirect")
|
||||
# Clear state about enabling redirect on this run
|
||||
# pylint: disable=protected-access
|
||||
@@ -1474,7 +1446,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
# pylint: disable=protected-access
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
self.config.parser.modules.add("headers_module")
|
||||
self.vh_truth[3].ssl = True
|
||||
self.config._wildcard_vhosts["*.certbot.demo"] = [self.vh_truth[3]]
|
||||
self.config.enhance("*.certbot.demo", "ensure-http-header",
|
||||
"Upgrade-Insecure-Requests")
|
||||
@@ -1482,7 +1453,6 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator._choose_vhosts_wildcard")
|
||||
def test_enhance_wildcard_no_install(self, mock_choose):
|
||||
self.vh_truth[3].ssl = True
|
||||
mock_choose.return_value = [self.vh_truth[3]]
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
self.config.parser.modules.add("headers_module")
|
||||
|
||||
@@ -161,8 +161,6 @@ class MultipleVhostsTestDebian(util.ApacheTest):
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 4, 7))
|
||||
mock_exe.return_value = True
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
self.config.enhance("certbot.demo", "staple-ocsp")
|
||||
self.assertTrue("socache_shmcb_module" in self.config.parser.modules)
|
||||
|
||||
@@ -174,7 +172,6 @@ class MultipleVhostsTestDebian(util.ApacheTest):
|
||||
mock_exe.return_value = True
|
||||
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
self.config.enhance("certbot.demo", "ensure-http-header",
|
||||
"Strict-Transport-Security")
|
||||
self.assertTrue("headers_module" in self.config.parser.modules)
|
||||
@@ -186,7 +183,6 @@ class MultipleVhostsTestDebian(util.ApacheTest):
|
||||
mock_exe.return_value = True
|
||||
self.config.get_version = mock.Mock(return_value=(2, 2))
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.choose_vhost("certbot.demo")
|
||||
self.config.enhance("certbot.demo", "redirect")
|
||||
self.assertTrue("rewrite_module" in self.config.parser.modules)
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from certbot import errors
|
||||
|
||||
from certbot_apache import override_gentoo
|
||||
from certbot_apache import obj
|
||||
from certbot_apache.tests import util
|
||||
@@ -125,11 +123,5 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
|
||||
self.assertEquals(len(self.config.parser.modules), 4)
|
||||
self.assertTrue("mod_another.c" in self.config.parser.modules)
|
||||
|
||||
@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.assertEquals(mock_run_script.call_count, 3)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -16,9 +16,30 @@ from certbot_apache.tests import util
|
||||
NUM_ACHALLS = 3
|
||||
|
||||
|
||||
class ApacheHttp01TestMeta(type):
|
||||
"""Generates parmeterized tests for testing perform."""
|
||||
def __new__(mcs, name, bases, class_dict):
|
||||
|
||||
def _gen_test(num_achalls, minor_version):
|
||||
def _test(self):
|
||||
achalls = self.achalls[:num_achalls]
|
||||
vhosts = self.vhosts[:num_achalls]
|
||||
self.config.version = (2, minor_version)
|
||||
self.common_perform_test(achalls, vhosts)
|
||||
return _test
|
||||
|
||||
for i in range(1, NUM_ACHALLS + 1):
|
||||
for j in (2, 4):
|
||||
test_name = "test_perform_{0}_{1}".format(i, j)
|
||||
class_dict[test_name] = _gen_test(i, j)
|
||||
return type.__new__(mcs, name, bases, class_dict)
|
||||
|
||||
|
||||
class ApacheHttp01Test(util.ApacheTest):
|
||||
"""Test for certbot_apache.http_01.ApacheHttp01."""
|
||||
|
||||
__metaclass__ = ApacheHttp01TestMeta
|
||||
|
||||
def setUp(self, *args, **kwargs):
|
||||
super(ApacheHttp01Test, self).setUp(*args, **kwargs)
|
||||
|
||||
@@ -50,7 +71,7 @@ class ApacheHttp01Test(util.ApacheTest):
|
||||
self.assertFalse(self.http.perform())
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod")
|
||||
def test_enable_modules_apache_2_2(self, mock_enmod):
|
||||
def test_enable_modules_22(self, mock_enmod):
|
||||
self.config.version = (2, 2)
|
||||
self.config.parser.modules.remove("authz_host_module")
|
||||
self.config.parser.modules.remove("mod_authz_host.c")
|
||||
@@ -59,7 +80,7 @@ class ApacheHttp01Test(util.ApacheTest):
|
||||
self.assertEqual(enmod_calls[0][0][0], "authz_host")
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod")
|
||||
def test_enable_modules_apache_2_4(self, mock_enmod):
|
||||
def test_enable_modules_24(self, mock_enmod):
|
||||
self.config.parser.modules.remove("authz_core_module")
|
||||
self.config.parser.modules.remove("mod_authz_core.c")
|
||||
|
||||
@@ -116,31 +137,6 @@ class ApacheHttp01Test(util.ApacheTest):
|
||||
self.config.config.http01_port = 12345
|
||||
self.assertRaises(errors.PluginError, self.http.perform)
|
||||
|
||||
def test_perform_1_achall_apache_2_2(self):
|
||||
self.combinations_perform_test(num_achalls=1, minor_version=2)
|
||||
|
||||
def test_perform_1_achall_apache_2_4(self):
|
||||
self.combinations_perform_test(num_achalls=1, minor_version=4)
|
||||
|
||||
def test_perform_2_achall_apache_2_2(self):
|
||||
self.combinations_perform_test(num_achalls=2, minor_version=2)
|
||||
|
||||
def test_perform_2_achall_apache_2_4(self):
|
||||
self.combinations_perform_test(num_achalls=2, minor_version=4)
|
||||
|
||||
def test_perform_3_achall_apache_2_2(self):
|
||||
self.combinations_perform_test(num_achalls=3, minor_version=2)
|
||||
|
||||
def test_perform_3_achall_apache_2_4(self):
|
||||
self.combinations_perform_test(num_achalls=3, minor_version=4)
|
||||
|
||||
def combinations_perform_test(self, num_achalls, minor_version):
|
||||
"""Test perform with the given achall count and Apache version."""
|
||||
achalls = self.achalls[:num_achalls]
|
||||
vhosts = self.vhosts[:num_achalls]
|
||||
self.config.version = (2, minor_version)
|
||||
self.common_perform_test(achalls, vhosts)
|
||||
|
||||
def common_perform_test(self, achalls, vhosts):
|
||||
"""Tests perform with the given achalls."""
|
||||
challenge_dir = self.http.challenge_dir
|
||||
|
||||
@@ -123,8 +123,7 @@ class ApacheTlsSni01(common.TLSSNI01):
|
||||
self.configurator.config.tls_sni_01_port)))
|
||||
|
||||
try:
|
||||
vhost = self.configurator.choose_vhost(achall.domain,
|
||||
create_if_no_ssl=False)
|
||||
vhost = self.configurator.choose_vhost(achall.domain, temp=True)
|
||||
except (PluginError, MissingCommandlineFlag):
|
||||
# We couldn't find the virtualhost for this domain, possibly
|
||||
# because it's a new vhost that's not configured yet
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
26
certbot-auto
26
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.23.0"
|
||||
LE_AUTO_VERSION="0.22.2"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
@@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \
|
||||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.23.0 \
|
||||
--hash=sha256:66c42cf780ddbf582ecc52aa6a61242450a2650227b436ad0d260685c4ef8a49 \
|
||||
--hash=sha256:6cff4c5da1228661ccaf95195064cb29e6cdf80913193bdb2eb20e164c76053e
|
||||
acme==0.23.0 \
|
||||
--hash=sha256:02e9b596bd3bf8f0733d6d43ec2464ac8185a000acb58d2b4fd9e19223bbbf0b \
|
||||
--hash=sha256:08c16635578507f526c338b3418c1147a9f015bf2d366abd51f38918703b4550
|
||||
certbot-apache==0.23.0 \
|
||||
--hash=sha256:50077742d2763b7600dfda618eb89c870aeea5e6a4c00f60157877f7a7d81f7c \
|
||||
--hash=sha256:6b7acec243e224de5268d46c2597277586dffa55e838c252b6931c30d549028e
|
||||
certbot-nginx==0.23.0 \
|
||||
--hash=sha256:f12c21bbe3eb955ca533f1da96d28c6310378b138e844d83253562e18b6cbb32 \
|
||||
--hash=sha256:cadf14e4bd504d9ce5987a5ec6dbd8e136638e55303ad5dc81dcb723ddd64324
|
||||
certbot==0.22.2 \
|
||||
--hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \
|
||||
--hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef
|
||||
acme==0.22.2 \
|
||||
--hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \
|
||||
--hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4
|
||||
certbot-apache==0.22.2 \
|
||||
--hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \
|
||||
--hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759
|
||||
certbot-nginx==0.22.2 \
|
||||
--hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \
|
||||
--hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'certbot',
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -50,8 +50,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
|
||||
|
||||
|
||||
class DigitalOceanClientTest(unittest.TestCase):
|
||||
|
||||
id_num = 1
|
||||
id = 1
|
||||
record_prefix = "_acme-challenge"
|
||||
record_name = record_prefix + "." + DOMAIN
|
||||
record_content = "bar"
|
||||
@@ -71,7 +70,7 @@ class DigitalOceanClientTest(unittest.TestCase):
|
||||
|
||||
domain_mock = mock.MagicMock()
|
||||
domain_mock.name = DOMAIN
|
||||
domain_mock.create_new_domain_record.return_value = {'domain_record': {'id': self.id_num}}
|
||||
domain_mock.create_new_domain_record.return_value = {'domain_record': {'id': self.id}}
|
||||
|
||||
self.manager.get_all_domains.return_value = [wrong_domain_mock, domain_mock]
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include LICENSE.txt
|
||||
include README.rst
|
||||
recursive-include docs *
|
||||
recursive-include certbot_dns_google/testdata *
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -21,9 +21,8 @@ Credentials
|
||||
-----------
|
||||
|
||||
Use of this plugin requires a configuration file containing the target DNS
|
||||
server and optional port that supports RFC 2136 Dynamic Updates, the name
|
||||
of the TSIG key, the TSIG key secret itself and the algorithm used if it's
|
||||
different to HMAC-MD5.
|
||||
server that supports RFC 2136 Dynamic Updates, the name of the TSIG key, the
|
||||
TSIG key secret itself and the algorithm used if it's different to HMAC-MD5.
|
||||
|
||||
.. code-block:: ini
|
||||
:name: credentials.ini
|
||||
@@ -31,8 +30,6 @@ different to HMAC-MD5.
|
||||
|
||||
# Target DNS server
|
||||
dns_rfc2136_server = 192.0.2.1
|
||||
# Target DNS port
|
||||
dns_rfc2136_port = 53
|
||||
# TSIG key name
|
||||
dns_rfc2136_name = keyname.
|
||||
# TSIG key secret
|
||||
|
||||
@@ -36,8 +36,6 @@ class Authenticator(dns_common.DNSAuthenticator):
|
||||
'HMAC-SHA512': dns.tsig.HMAC_SHA512
|
||||
}
|
||||
|
||||
PORT = 53
|
||||
|
||||
description = 'Obtain certificates using a DNS TXT record (if you are using BIND for DNS).'
|
||||
ttl = 120
|
||||
|
||||
@@ -80,7 +78,6 @@ class Authenticator(dns_common.DNSAuthenticator):
|
||||
|
||||
def _get_rfc2136_client(self):
|
||||
return _RFC2136Client(self.credentials.conf('server'),
|
||||
int(self.credentials.conf('port') or self.PORT),
|
||||
self.credentials.conf('name'),
|
||||
self.credentials.conf('secret'),
|
||||
self.ALGORITHMS.get(self.credentials.conf('algorithm'),
|
||||
@@ -91,9 +88,8 @@ class _RFC2136Client(object):
|
||||
"""
|
||||
Encapsulates all communication with the target DNS server.
|
||||
"""
|
||||
def __init__(self, server, port, key_name, key_secret, key_algorithm):
|
||||
def __init__(self, server, key_name, key_secret, key_algorithm):
|
||||
self.server = server
|
||||
self.port = port
|
||||
self.keyring = dns.tsigkeyring.from_text({
|
||||
key_name: key_secret
|
||||
})
|
||||
@@ -122,7 +118,7 @@ class _RFC2136Client(object):
|
||||
update.add(rel, record_ttl, dns.rdatatype.TXT, record_content)
|
||||
|
||||
try:
|
||||
response = dns.query.tcp(update, self.server, port=self.port)
|
||||
response = dns.query.tcp(update, self.server)
|
||||
except Exception as e:
|
||||
raise errors.PluginError('Encountered error adding TXT record: {0}'
|
||||
.format(e))
|
||||
@@ -157,7 +153,7 @@ class _RFC2136Client(object):
|
||||
update.delete(rel, dns.rdatatype.TXT, record_content)
|
||||
|
||||
try:
|
||||
response = dns.query.tcp(update, self.server, port=self.port)
|
||||
response = dns.query.tcp(update, self.server)
|
||||
except Exception as e:
|
||||
raise errors.PluginError('Encountered error deleting TXT record: {0}'
|
||||
.format(e))
|
||||
@@ -206,7 +202,7 @@ class _RFC2136Client(object):
|
||||
request.flags ^= dns.flags.RD
|
||||
|
||||
try:
|
||||
response = dns.query.udp(request, self.server, port=self.port)
|
||||
response = dns.query.udp(request, self.server)
|
||||
rcode = response.rcode()
|
||||
|
||||
# Authoritative Answer bit should be set
|
||||
|
||||
@@ -14,7 +14,6 @@ from certbot.plugins.dns_test_common import DOMAIN
|
||||
from certbot.tests import util as test_util
|
||||
|
||||
SERVER = '192.0.2.1'
|
||||
PORT = 53
|
||||
NAME = 'a-tsig-key.'
|
||||
SECRET = 'SSB3b25kZXIgd2hvIHdpbGwgYm90aGVyIHRvIGRlY29kZSB0aGlzIHRleHQK'
|
||||
VALID_CONFIG = {"rfc2136_server": SERVER, "rfc2136_name": NAME, "rfc2136_secret": SECRET}
|
||||
@@ -75,7 +74,7 @@ class RFC2136ClientTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
from certbot_dns_rfc2136.dns_rfc2136 import _RFC2136Client
|
||||
|
||||
self.rfc2136_client = _RFC2136Client(SERVER, PORT, NAME, SECRET, dns.tsig.HMAC_MD5)
|
||||
self.rfc2136_client = _RFC2136Client(SERVER, NAME, SECRET, dns.tsig.HMAC_MD5)
|
||||
|
||||
@mock.patch("dns.query.tcp")
|
||||
def test_add_txt_record(self, query_mock):
|
||||
@@ -85,7 +84,7 @@ class RFC2136ClientTest(unittest.TestCase):
|
||||
|
||||
self.rfc2136_client.add_txt_record("bar", "baz", 42)
|
||||
|
||||
query_mock.assert_called_with(mock.ANY, SERVER, port=PORT)
|
||||
query_mock.assert_called_with(mock.ANY, SERVER)
|
||||
self.assertTrue("bar. 42 IN TXT \"baz\"" in str(query_mock.call_args[0][0]))
|
||||
|
||||
@mock.patch("dns.query.tcp")
|
||||
@@ -118,7 +117,7 @@ class RFC2136ClientTest(unittest.TestCase):
|
||||
|
||||
self.rfc2136_client.del_txt_record("bar", "baz")
|
||||
|
||||
query_mock.assert_called_with(mock.ANY, SERVER, port=PORT)
|
||||
query_mock.assert_called_with(mock.ANY, SERVER)
|
||||
self.assertTrue("bar. 0 NONE TXT \"baz\"" in str(query_mock.call_args[0][0]))
|
||||
|
||||
@mock.patch("dns.query.tcp")
|
||||
@@ -170,7 +169,7 @@ class RFC2136ClientTest(unittest.TestCase):
|
||||
# _query_soa | pylint: disable=protected-access
|
||||
result = self.rfc2136_client._query_soa(DOMAIN)
|
||||
|
||||
query_mock.assert_called_with(mock.ANY, SERVER, port=PORT)
|
||||
query_mock.assert_called_with(mock.ANY, SERVER)
|
||||
self.assertTrue(result == True)
|
||||
|
||||
@mock.patch("dns.query.udp")
|
||||
@@ -180,7 +179,7 @@ class RFC2136ClientTest(unittest.TestCase):
|
||||
# _query_soa | pylint: disable=protected-access
|
||||
result = self.rfc2136_client._query_soa(DOMAIN)
|
||||
|
||||
query_mock.assert_called_with(mock.ANY, SERVER, port=PORT)
|
||||
query_mock.assert_called_with(mock.ANY, SERVER)
|
||||
self.assertTrue(result == False)
|
||||
|
||||
@mock.patch("dns.query.udp")
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -42,26 +42,14 @@ class Authenticator(dns_common.DNSAuthenticator):
|
||||
def _setup_credentials(self):
|
||||
pass
|
||||
|
||||
def _perform(self, domain, validation_domain_name, validation): # pylint: disable=missing-docstring
|
||||
pass
|
||||
|
||||
def perform(self, achalls):
|
||||
self._attempt_cleanup = True
|
||||
|
||||
def _perform(self, domain, validation_domain_name, validation):
|
||||
try:
|
||||
change_ids = [
|
||||
self._change_txt_record("UPSERT",
|
||||
achall.validation_domain_name(achall.domain),
|
||||
achall.validation(achall.account_key))
|
||||
for achall in achalls
|
||||
]
|
||||
change_id = self._change_txt_record("UPSERT", validation_domain_name, validation)
|
||||
|
||||
for change_id in change_ids:
|
||||
self._wait_for_change(change_id)
|
||||
self._wait_for_change(change_id)
|
||||
except (NoCredentialsError, ClientError) as e:
|
||||
logger.debug('Encountered error during perform: %s', e, exc_info=True)
|
||||
raise errors.PluginError("\n".join([str(e), INSTRUCTIONS]))
|
||||
return [achall.response(achall.account_key) for achall in achalls]
|
||||
|
||||
def _cleanup(self, domain, validation_domain_name, validation):
|
||||
try:
|
||||
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
from distutils.core import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -743,7 +743,7 @@ def _parse_server_raw(server):
|
||||
if addr.ssl:
|
||||
parsed_server['ssl'] = True
|
||||
elif directive[0] == 'server_name':
|
||||
parsed_server['names'].update(x.strip('"\'') for x in directive[1:])
|
||||
parsed_server['names'].update(directive[1:])
|
||||
elif _is_ssl_on_directive(directive):
|
||||
parsed_server['ssl'] = True
|
||||
apply_ssl_to_all_addrs = True
|
||||
|
||||
@@ -639,7 +639,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
||||
self.assertEqual([[['server'],
|
||||
[['listen', 'myhost', 'default_server'],
|
||||
['listen', 'otherhost', 'default_server'],
|
||||
['server_name', '"www.example.org"'],
|
||||
['server_name', 'www.example.org'],
|
||||
[['location', '/'],
|
||||
[['root', 'html'],
|
||||
['index', 'index.html', 'index.htm']]]]],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
server {
|
||||
listen myhost default_server;
|
||||
listen otherhost default_server;
|
||||
server_name "www.example.org";
|
||||
server_name www.example.org;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.24.0.dev0'
|
||||
version = '0.23.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Certbot client."""
|
||||
|
||||
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
|
||||
__version__ = '0.24.0.dev0'
|
||||
__version__ = '0.23.0.dev0'
|
||||
|
||||
@@ -189,7 +189,7 @@ class AuthHandler(object):
|
||||
return active_achalls
|
||||
|
||||
def _poll_challenges(self, aauthzrs, chall_update,
|
||||
best_effort, min_sleep=3, max_rounds=30):
|
||||
best_effort, min_sleep=3, max_rounds=15):
|
||||
"""Wait for all challenge results to be determined."""
|
||||
indices_to_check = set(chall_update.keys())
|
||||
comp_indices = set()
|
||||
|
||||
@@ -46,7 +46,7 @@ def rename_lineage(config):
|
||||
"""
|
||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||
|
||||
certname = get_certnames(config, "rename")[0]
|
||||
certname = _get_certnames(config, "rename")[0]
|
||||
|
||||
new_certname = config.new_certname
|
||||
if not new_certname:
|
||||
@@ -88,7 +88,7 @@ def certificates(config):
|
||||
|
||||
def delete(config):
|
||||
"""Delete Certbot files associated with a certificate lineage."""
|
||||
certnames = get_certnames(config, "delete", allow_multiple=True)
|
||||
certnames = _get_certnames(config, "delete", allow_multiple=True)
|
||||
for certname in certnames:
|
||||
storage.delete_files(config, certname)
|
||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||
@@ -288,7 +288,11 @@ def human_readable_cert_info(config, cert, skip_filter_checks=False):
|
||||
cert.privkey))
|
||||
return "".join(certinfo)
|
||||
|
||||
def get_certnames(config, verb, allow_multiple=False, custom_prompt=None):
|
||||
###################
|
||||
# Private Helpers
|
||||
###################
|
||||
|
||||
def _get_certnames(config, verb, allow_multiple=False):
|
||||
"""Get certname from flag, interactively, or error out.
|
||||
"""
|
||||
certname = config.certname
|
||||
@@ -301,32 +305,22 @@ def get_certnames(config, verb, allow_multiple=False, custom_prompt=None):
|
||||
if not choices:
|
||||
raise errors.Error("No existing certificates found.")
|
||||
if allow_multiple:
|
||||
if not custom_prompt:
|
||||
prompt = "Which certificate(s) would you like to {0}?".format(verb)
|
||||
else:
|
||||
prompt = custom_prompt
|
||||
code, certnames = disp.checklist(
|
||||
prompt, choices, cli_flag="--cert-name", force_interactive=True)
|
||||
"Which certificate(s) would you like to {0}?".format(verb),
|
||||
choices, cli_flag="--cert-name",
|
||||
force_interactive=True)
|
||||
if code != display_util.OK:
|
||||
raise errors.Error("User ended interaction.")
|
||||
else:
|
||||
if not custom_prompt:
|
||||
prompt = "Which certificate would you like to {0}?".format(verb)
|
||||
else:
|
||||
prompt = custom_prompt
|
||||
|
||||
code, index = disp.menu(
|
||||
prompt, choices, cli_flag="--cert-name", force_interactive=True)
|
||||
code, index = disp.menu("Which certificate would you like to {0}?".format(verb),
|
||||
choices, cli_flag="--cert-name",
|
||||
force_interactive=True)
|
||||
|
||||
if code != display_util.OK or index not in range(0, len(choices)):
|
||||
raise errors.Error("User ended interaction.")
|
||||
certnames = [choices[index]]
|
||||
return certnames
|
||||
|
||||
###################
|
||||
# Private Helpers
|
||||
###################
|
||||
|
||||
def _report_lines(msgs):
|
||||
"""Format a results report for a category of single-line renewal outcomes"""
|
||||
return " " + "\n ".join(str(msg) for msg in msgs)
|
||||
|
||||
@@ -76,7 +76,6 @@ obtain, install, and renew certificates:
|
||||
(default) run Obtain & install a certificate in your current webserver
|
||||
certonly Obtain or renew a certificate, but do not install it
|
||||
renew Renew all previously obtained certificates that are near expiry
|
||||
enhance Add security enhancements to your existing configuration
|
||||
-d DOMAINS Comma-separated list of domains to obtain a certificate for
|
||||
|
||||
%s
|
||||
@@ -416,12 +415,6 @@ VERB_HELP = [
|
||||
os.path.join(flag_default("config_dir"), "live"))),
|
||||
"usage": "\n\n certbot update_symlinks [options]\n\n"
|
||||
}),
|
||||
("enhance", {
|
||||
"short": "Add security enhancements to your existing configuration",
|
||||
"opts": ("Helps to harden the TLS configration by adding security enhancements "
|
||||
"to already existing configuration."),
|
||||
"usage": "\n\n certbot enhance [options]\n\n"
|
||||
}),
|
||||
|
||||
]
|
||||
# VERB_HELP is a list in order to preserve order, but a dict is sometimes useful
|
||||
@@ -456,7 +449,6 @@ class HelpfulArgumentParser(object):
|
||||
"update_symlinks": main.update_symlinks,
|
||||
"certificates": main.certificates,
|
||||
"delete": main.delete,
|
||||
"enhance": main.enhance,
|
||||
}
|
||||
|
||||
# Get notification function for printing
|
||||
@@ -891,22 +883,21 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
||||
"flag to 0 disables log rotation entirely, causing "
|
||||
"Certbot to always append to the same log file.")
|
||||
helpful.add(
|
||||
[None, "automation", "run", "certonly", "enhance"],
|
||||
"-n", "--non-interactive", "--noninteractive",
|
||||
[None, "automation", "run", "certonly"], "-n", "--non-interactive", "--noninteractive",
|
||||
dest="noninteractive_mode", action="store_true",
|
||||
default=flag_default("noninteractive_mode"),
|
||||
help="Run without ever asking for user input. This may require "
|
||||
"additional command line flags; the client will try to explain "
|
||||
"which ones are required if it finds one missing")
|
||||
helpful.add(
|
||||
[None, "register", "run", "certonly", "enhance"],
|
||||
[None, "register", "run", "certonly"],
|
||||
constants.FORCE_INTERACTIVE_FLAG, action="store_true",
|
||||
default=flag_default("force_interactive"),
|
||||
help="Force Certbot to be interactive even if it detects it's not "
|
||||
"being run in a terminal. This flag cannot be used with the "
|
||||
"renew subcommand.")
|
||||
helpful.add(
|
||||
[None, "run", "certonly", "certificates", "enhance"],
|
||||
[None, "run", "certonly", "certificates"],
|
||||
"-d", "--domains", "--domain", dest="domains",
|
||||
metavar="DOMAIN", action=_DomainsAction,
|
||||
default=flag_default("domains"),
|
||||
@@ -922,8 +913,8 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
||||
"name. In the case of a name collision it will append a number "
|
||||
"like 0001 to the file path name. (default: Ask)")
|
||||
helpful.add(
|
||||
[None, "run", "certonly", "manage", "delete", "certificates",
|
||||
"renew", "enhance"], "--cert-name", dest="certname",
|
||||
[None, "run", "certonly", "manage", "delete", "certificates", "renew"],
|
||||
"--cert-name", dest="certname",
|
||||
metavar="CERTNAME", default=flag_default("certname"),
|
||||
help="Certificate name to apply. This name is used by Certbot for housekeeping "
|
||||
"and in file paths; it doesn't affect the content of the certificate itself. "
|
||||
@@ -1094,8 +1085,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
||||
dest="must_staple", default=flag_default("must_staple"),
|
||||
help=config_help("must_staple"))
|
||||
helpful.add(
|
||||
["security", "enhance"],
|
||||
"--redirect", action="store_true", dest="redirect",
|
||||
"security", "--redirect", action="store_true", dest="redirect",
|
||||
default=flag_default("redirect"),
|
||||
help="Automatically redirect all HTTP traffic to HTTPS for the newly "
|
||||
"authenticated vhost. (default: Ask)")
|
||||
@@ -1105,8 +1095,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
||||
help="Do not automatically redirect all HTTP traffic to HTTPS for the newly "
|
||||
"authenticated vhost. (default: Ask)")
|
||||
helpful.add(
|
||||
["security", "enhance"],
|
||||
"--hsts", action="store_true", dest="hsts", default=flag_default("hsts"),
|
||||
"security", "--hsts", action="store_true", dest="hsts", default=flag_default("hsts"),
|
||||
help="Add the Strict-Transport-Security header to every HTTP response."
|
||||
" Forcing browser to always use SSL for the domain."
|
||||
" Defends against SSL Stripping.")
|
||||
@@ -1114,8 +1103,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
||||
"security", "--no-hsts", action="store_false", dest="hsts",
|
||||
default=flag_default("hsts"), help=argparse.SUPPRESS)
|
||||
helpful.add(
|
||||
["security", "enhance"],
|
||||
"--uir", action="store_true", dest="uir", default=flag_default("uir"),
|
||||
"security", "--uir", action="store_true", dest="uir", default=flag_default("uir"),
|
||||
help='Add the "Content-Security-Policy: upgrade-insecure-requests"'
|
||||
' header to every HTTP response. Forcing the browser to use'
|
||||
' https:// for every http:// resource.')
|
||||
@@ -1192,14 +1180,6 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
||||
default=flag_default("directory_hooks"), dest="directory_hooks",
|
||||
help="Disable running executables found in Certbot's hook directories"
|
||||
" during renewal. (default: False)")
|
||||
helpful.add(
|
||||
"renew", "--disable-renew-updates", action="store_true",
|
||||
default=flag_default("disable_renew_updates"), dest="disable_renew_updates",
|
||||
help="Disable automatic updates to your server configuration that"
|
||||
" would otherwise be done by the selected installer plugin, and triggered"
|
||||
" when the user executes \"certbot renew\", regardless of if the certificate"
|
||||
" is renewed. This setting does not apply to important TLS configuration"
|
||||
" updates.")
|
||||
|
||||
helpful.add_deprecated_argument("--agree-dev-preview", 0)
|
||||
helpful.add_deprecated_argument("--dialog", 0)
|
||||
|
||||
@@ -338,10 +338,9 @@ class Client(object):
|
||||
authenticator and installer, and then create a new renewable lineage
|
||||
containing it.
|
||||
|
||||
:param domains: domains to request a certificate for
|
||||
:type domains: `list` of `str`
|
||||
:param certname: requested name of lineage
|
||||
:type certname: `str` or `None`
|
||||
:param list domains: Domains to request.
|
||||
:param plugins: A PluginsFactory object.
|
||||
:param str certname: Name of new cert
|
||||
|
||||
:returns: A new :class:`certbot.storage.RenewableCert` instance
|
||||
referred to the enrolled cert lineage, False if the cert could not
|
||||
@@ -352,11 +351,17 @@ class Client(object):
|
||||
|
||||
if (self.config.config_dir != constants.CLI_DEFAULTS["config_dir"] or
|
||||
self.config.work_dir != constants.CLI_DEFAULTS["work_dir"]):
|
||||
logger.info(
|
||||
logger.warning(
|
||||
"Non-standard path(s), might not work with crontab installed "
|
||||
"by your operating system package manager")
|
||||
|
||||
new_name = self._choose_lineagename(domains, certname)
|
||||
if certname:
|
||||
new_name = certname
|
||||
elif util.is_wildcard_domain(domains[0]):
|
||||
# Don't make files and directories starting with *.
|
||||
new_name = domains[0][2:]
|
||||
else:
|
||||
new_name = domains[0]
|
||||
|
||||
if self.config.dry_run:
|
||||
logger.debug("Dry run: Skipping creating new lineage for %s",
|
||||
@@ -368,26 +373,6 @@ class Client(object):
|
||||
key.pem, chain,
|
||||
self.config)
|
||||
|
||||
def _choose_lineagename(self, domains, certname):
|
||||
"""Chooses a name for the new lineage.
|
||||
|
||||
:param domains: domains in certificate request
|
||||
:type domains: `list` of `str`
|
||||
:param certname: requested name of lineage
|
||||
:type certname: `str` or `None`
|
||||
|
||||
:returns: lineage name that should be used
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
if certname:
|
||||
return certname
|
||||
elif util.is_wildcard_domain(domains[0]):
|
||||
# Don't make files and directories starting with *.
|
||||
return domains[0][2:]
|
||||
else:
|
||||
return domains[0]
|
||||
|
||||
def save_certificate(self, cert_pem, chain_pem,
|
||||
cert_path, chain_path, fullchain_path):
|
||||
"""Saves the certificate received from the ACME server.
|
||||
@@ -466,7 +451,7 @@ class Client(object):
|
||||
# sites may have been enabled / final cleanup
|
||||
self.installer.restart()
|
||||
|
||||
def enhance_config(self, domains, chain_path, ask_redirect=True):
|
||||
def enhance_config(self, domains, chain_path):
|
||||
"""Enhance the configuration.
|
||||
|
||||
:param list domains: list of domains to configure
|
||||
@@ -493,9 +478,8 @@ class Client(object):
|
||||
for config_name, enhancement_name, option in enhancement_info:
|
||||
config_value = getattr(self.config, config_name)
|
||||
if enhancement_name in supported:
|
||||
if ask_redirect:
|
||||
if config_name == "redirect" and config_value is None:
|
||||
config_value = enhancements.ask(enhancement_name)
|
||||
if config_name == "redirect" and config_value is None:
|
||||
config_value = enhancements.ask(enhancement_name)
|
||||
if config_value:
|
||||
self.apply_enhancement(domains, enhancement_name, option)
|
||||
enhanced = True
|
||||
@@ -531,12 +515,8 @@ class Client(object):
|
||||
try:
|
||||
self.installer.enhance(dom, enhancement, options)
|
||||
except errors.PluginEnhancementAlreadyPresent:
|
||||
if enhancement == "ensure-http-header":
|
||||
logger.warning("Enhancement %s was already set.",
|
||||
options)
|
||||
else:
|
||||
logger.warning("Enhancement %s was already set.",
|
||||
enhancement)
|
||||
logger.warning("Enhancement %s was already set.",
|
||||
enhancement)
|
||||
except errors.PluginError:
|
||||
logger.warning("Unable to set enhancement %s for %s",
|
||||
enhancement, dom)
|
||||
|
||||
@@ -64,7 +64,6 @@ CLI_DEFAULTS = dict(
|
||||
pref_challs=[],
|
||||
validate_hooks=True,
|
||||
directory_hooks=True,
|
||||
disable_renew_updates=False,
|
||||
|
||||
# Subparsers
|
||||
num=None,
|
||||
@@ -85,7 +84,7 @@ CLI_DEFAULTS = dict(
|
||||
config_dir="/etc/letsencrypt",
|
||||
work_dir="/var/lib/letsencrypt",
|
||||
logs_dir="/var/log/letsencrypt",
|
||||
server="https://acme-v01.api.letsencrypt.org/directory",
|
||||
server="https://acme-v02.api.letsencrypt.org/directory",
|
||||
|
||||
# Plugins parsers
|
||||
configurator=None,
|
||||
|
||||
@@ -13,7 +13,7 @@ import pyrfc3339
|
||||
import six
|
||||
import zope.component
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography import x509 # type: ignore
|
||||
from cryptography import x509
|
||||
|
||||
from acme import crypto_util as acme_crypto_util
|
||||
|
||||
|
||||
@@ -86,31 +86,13 @@ def choose_account(accounts):
|
||||
else:
|
||||
return None
|
||||
|
||||
def choose_values(values, question=None):
|
||||
"""Display screen to let user pick one or multiple values from the provided
|
||||
list.
|
||||
|
||||
:param list values: Values to select from
|
||||
|
||||
:returns: List of selected values
|
||||
:rtype: list
|
||||
"""
|
||||
code, items = z_util(interfaces.IDisplay).checklist(
|
||||
question, tags=values, force_interactive=True)
|
||||
if code == display_util.OK and items:
|
||||
return items
|
||||
else:
|
||||
return []
|
||||
|
||||
def choose_names(installer, question=None):
|
||||
def choose_names(installer):
|
||||
"""Display screen to select domains to validate.
|
||||
|
||||
:param installer: An installer object
|
||||
:type installer: :class:`certbot.interfaces.IInstaller`
|
||||
|
||||
:param `str` question: Overriding dialog question to ask the user if asked
|
||||
to choose from domain names.
|
||||
|
||||
:returns: List of selected names
|
||||
:rtype: `list` of `str`
|
||||
|
||||
@@ -126,7 +108,7 @@ def choose_names(installer, question=None):
|
||||
return _choose_names_manually(
|
||||
"No names were found in your configuration files. ")
|
||||
|
||||
code, names = _filter_names(names, question)
|
||||
code, names = _filter_names(names)
|
||||
if code == display_util.OK and names:
|
||||
return names
|
||||
else:
|
||||
@@ -160,7 +142,7 @@ def _sort_names(FQDNs):
|
||||
return sorted(FQDNs, key=lambda fqdn: fqdn.split('.')[::-1][1:])
|
||||
|
||||
|
||||
def _filter_names(names, override_question=None):
|
||||
def _filter_names(names):
|
||||
"""Determine which names the user would like to select from a list.
|
||||
|
||||
:param list names: domain names
|
||||
@@ -173,12 +155,10 @@ def _filter_names(names, override_question=None):
|
||||
"""
|
||||
#Sort by domain first, and then by subdomain
|
||||
sorted_names = _sort_names(names)
|
||||
if override_question:
|
||||
question = override_question
|
||||
else:
|
||||
question = "Which names would you like to activate HTTPS for?"
|
||||
|
||||
code, names = z_util(interfaces.IDisplay).checklist(
|
||||
question, tags=sorted_names, cli_flag="--domains", force_interactive=True)
|
||||
"Which names would you like to activate HTTPS for?",
|
||||
tags=sorted_names, cli_flag="--domains", force_interactive=True)
|
||||
return code, [str(s) for s in names]
|
||||
|
||||
|
||||
|
||||
@@ -87,10 +87,6 @@ class NotSupportedError(PluginError):
|
||||
"""Certbot Plugin function not supported error."""
|
||||
|
||||
|
||||
class PluginStorageError(PluginError):
|
||||
"""Certbot Plugin Storage error."""
|
||||
|
||||
|
||||
class StandaloneBindError(Error):
|
||||
"""Standalone plugin bind error."""
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Certbot client interfaces."""
|
||||
import abc
|
||||
import six
|
||||
import zope.interface
|
||||
|
||||
# pylint: disable=no-self-argument,no-method-argument,no-init,inherit-non-class
|
||||
@@ -257,10 +256,6 @@ class IConfig(zope.interface.Interface):
|
||||
"user; only needed if your config is somewhere unsafe like /tmp/."
|
||||
"This is a boolean")
|
||||
|
||||
disable_renew_updates = zope.interface.Attribute(
|
||||
"If updates provided by installer enhancements when Certbot is being run"
|
||||
" with \"renew\" verb should be disabled.")
|
||||
|
||||
class IInstaller(IPlugin):
|
||||
"""Generic Certbot Installer Interface.
|
||||
|
||||
@@ -596,72 +591,3 @@ class IReporter(zope.interface.Interface):
|
||||
|
||||
def print_messages(self):
|
||||
"""Prints messages to the user and clears the message queue."""
|
||||
|
||||
|
||||
# Updater interfaces
|
||||
#
|
||||
# When "certbot renew" is run, Certbot will iterate over each lineage and check
|
||||
# if the selected installer for that lineage is a subclass of each updater
|
||||
# class. If it is and the update of that type is configured to be run for that
|
||||
# lineage, the relevant update function will be called for each domain in the
|
||||
# lineage. These functions are never called for other subcommands, so if an
|
||||
# installer wants to perform an update during the run or install subcommand, it
|
||||
# should do so when :func:`IInstaller.deploy_cert` is called.
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class GenericUpdater(object):
|
||||
"""Interface for update types not currently specified by Certbot.
|
||||
|
||||
This class allows plugins to perform types of updates that Certbot hasn't
|
||||
defined (yet).
|
||||
|
||||
To make use of this interface, the installer should implement the interface
|
||||
methods, and interfaces.GenericUpdater.register(InstallerClass) should
|
||||
be called from the installer code.
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def generic_updates(self, domain, *args, **kwargs):
|
||||
"""Perform any update types defined by the installer.
|
||||
|
||||
If an installer is a subclass of the class containing this method, this
|
||||
function will always be called when "certbot renew" is run. If the
|
||||
update defined by the installer should be run conditionally, the
|
||||
installer needs to handle checking the conditions itself.
|
||||
|
||||
This method is called once for each domain.
|
||||
|
||||
:param str domain: domain to handle the updates for
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class RenewDeployer(object):
|
||||
"""Interface for update types run when a lineage is renewed
|
||||
|
||||
This class allows plugins to perform types of updates that need to run at
|
||||
lineage renewal that Certbot hasn't defined (yet).
|
||||
|
||||
To make use of this interface, the installer should implement the interface
|
||||
methods, and interfaces.RenewDeployer.register(InstallerClass) should
|
||||
be called from the installer code.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def renew_deploy(self, lineage, *args, **kwargs):
|
||||
"""Perform updates defined by installer when a certificate has been renewed
|
||||
|
||||
If an installer is a subclass of the class containing this method, this
|
||||
function will always be called when a certficate has been renewed by
|
||||
running "certbot renew". For example if a plugin needs to copy a
|
||||
certificate over, or change configuration based on the new certificate.
|
||||
|
||||
This method is called once for each lineage renewed
|
||||
|
||||
:param lineage: Certificate lineage object that is set if certificate
|
||||
was renewed on this run.
|
||||
:type lineage: storage.RenewableCert
|
||||
|
||||
"""
|
||||
|
||||
@@ -29,7 +29,6 @@ from certbot import log
|
||||
from certbot import renewal
|
||||
from certbot import reporter
|
||||
from certbot import storage
|
||||
from certbot import updater
|
||||
from certbot import util
|
||||
|
||||
from certbot.display import util as display_util, ops as display_ops
|
||||
@@ -383,7 +382,7 @@ def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains):
|
||||
if not obj.yesno(msg, "Update cert", "Cancel", default=True):
|
||||
raise errors.ConfigurationError("Specified mismatched cert name and domains.")
|
||||
|
||||
def _find_domains_or_certname(config, installer, question=None):
|
||||
def _find_domains_or_certname(config, installer):
|
||||
"""Retrieve domains and certname from config or user input.
|
||||
|
||||
:param config: Configuration object
|
||||
@@ -392,8 +391,6 @@ def _find_domains_or_certname(config, installer, question=None):
|
||||
:param installer: Installer object
|
||||
:type installer: interfaces.IInstaller
|
||||
|
||||
:param `str` question: Overriding dialog question to ask the user if asked
|
||||
to choose from domain names.
|
||||
|
||||
:returns: Two-part tuple of domains and certname
|
||||
:rtype: `tuple` of list of `str` and `str`
|
||||
@@ -414,7 +411,7 @@ def _find_domains_or_certname(config, installer, question=None):
|
||||
# that certname might not have existed, or there was a problem.
|
||||
# try to get domains from the user.
|
||||
if not domains:
|
||||
domains = display_ops.choose_names(installer, question)
|
||||
domains = display_ops.choose_names(installer)
|
||||
|
||||
if not domains and not certname:
|
||||
raise errors.Error("Please specify --domains, or --installer that "
|
||||
@@ -862,53 +859,6 @@ def plugins_cmd(config, plugins):
|
||||
logger.debug("Prepared plugins: %s", available)
|
||||
notify(str(available))
|
||||
|
||||
def enhance(config, plugins):
|
||||
"""Add security enhancements to existing configuration
|
||||
|
||||
:param config: Configuration object
|
||||
:type config: interfaces.IConfig
|
||||
|
||||
:param plugins: List of plugins
|
||||
:type plugins: `list` of `str`
|
||||
|
||||
:returns: `None`
|
||||
:rtype: None
|
||||
|
||||
"""
|
||||
supported_enhancements = ["hsts", "redirect", "uir", "staple"]
|
||||
# Check that at least one enhancement was requested on command line
|
||||
if not any([getattr(config, enh) for enh in supported_enhancements]):
|
||||
msg = ("Please specify one or more enhancement types to configure. To list "
|
||||
"the available enhancement types, run:\n\n%s --help enhance\n")
|
||||
logger.warning(msg, sys.argv[0])
|
||||
raise errors.MisconfigurationError("No enhancements requested, exiting.")
|
||||
|
||||
try:
|
||||
installer, _ = plug_sel.choose_configurator_plugins(config, plugins, "enhance")
|
||||
except errors.PluginSelectionError as e:
|
||||
return str(e)
|
||||
|
||||
certname_question = ("Which certificate would you like to use to enhance "
|
||||
"your configuration?")
|
||||
config.certname = cert_manager.get_certnames(
|
||||
config, "enhance", allow_multiple=False,
|
||||
custom_prompt=certname_question)[0]
|
||||
cert_domains = cert_manager.domains_for_certname(config, config.certname)
|
||||
if config.noninteractive_mode:
|
||||
domains = cert_domains
|
||||
else:
|
||||
domain_question = ("Which domain names would you like to enable the "
|
||||
"selected enhancements for?")
|
||||
domains = display_ops.choose_values(cert_domains, domain_question)
|
||||
if not domains:
|
||||
raise errors.Error("User cancelled the domain selection. No domains "
|
||||
"defined, exiting.")
|
||||
if not config.chain_path:
|
||||
lineage = cert_manager.lineage_for_certname(config, config.certname)
|
||||
config.chain_path = lineage.chain_path
|
||||
le_client = _init_le_client(config, authenticator=None, installer=installer)
|
||||
le_client.enhance_config(domains, config.chain_path, ask_redirect=False)
|
||||
|
||||
|
||||
def rollback(config, plugins):
|
||||
"""Rollback server configuration changes made during install.
|
||||
@@ -1146,9 +1096,10 @@ def renew_cert(config, plugins, lineage):
|
||||
except errors.PluginSelectionError as e:
|
||||
logger.info("Could not choose appropriate plugin: %s", e)
|
||||
raise
|
||||
|
||||
le_client = _init_le_client(config, auth, installer)
|
||||
|
||||
renewed_lineage = _get_and_save_cert(le_client, config, lineage=lineage)
|
||||
_get_and_save_cert(le_client, config, lineage=lineage)
|
||||
|
||||
notify = zope.component.getUtility(interfaces.IDisplay).notification
|
||||
if installer is None:
|
||||
@@ -1158,11 +1109,9 @@ def renew_cert(config, plugins, lineage):
|
||||
# In case of a renewal, reload server to pick up new certificate.
|
||||
# In principle we could have a configuration option to inhibit this
|
||||
# from happening.
|
||||
updater.run_renewal_deployer(renewed_lineage, installer, config)
|
||||
installer.restart()
|
||||
notify("new certificate deployed with reload of {0} server; fullchain is {1}".format(
|
||||
config.installer, lineage.fullchain), pause=False)
|
||||
# Run deployer
|
||||
|
||||
def certonly(config, plugins):
|
||||
"""Authenticate & obtain cert, but do not install it.
|
||||
|
||||
@@ -18,8 +18,6 @@ from certbot import interfaces
|
||||
from certbot import reverter
|
||||
from certbot import util
|
||||
|
||||
from certbot.plugins.storage import PluginStorage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -101,6 +99,7 @@ class Plugin(object):
|
||||
def conf(self, var):
|
||||
"""Find a configuration value for variable ``var``."""
|
||||
return getattr(self.config, self.dest(var))
|
||||
# other
|
||||
|
||||
|
||||
class Installer(Plugin):
|
||||
@@ -111,7 +110,6 @@ class Installer(Plugin):
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Installer, self).__init__(*args, **kwargs)
|
||||
self.storage = PluginStorage(self.config, self.name)
|
||||
self.reverter = reverter.Reverter(self.config)
|
||||
|
||||
def add_to_checkpoint(self, save_files, save_notes, temporary=False):
|
||||
|
||||
@@ -147,7 +147,6 @@ def record_chosen_plugins(config, plugins, auth, inst):
|
||||
|
||||
|
||||
def choose_configurator_plugins(config, plugins, verb):
|
||||
# pylint: disable=too-many-branches
|
||||
"""
|
||||
Figure out which configurator we're going to use, modifies
|
||||
config.authenticator and config.installer strings to reflect that choice if
|
||||
@@ -160,11 +159,6 @@ def choose_configurator_plugins(config, plugins, verb):
|
||||
"""
|
||||
|
||||
req_auth, req_inst = cli_plugin_requests(config)
|
||||
installer_question = None
|
||||
|
||||
if verb == "enhance":
|
||||
installer_question = ("Which installer would you like to use to "
|
||||
"configure the selected enhancements?")
|
||||
|
||||
# Which plugins do we need?
|
||||
if verb == "run":
|
||||
@@ -182,11 +176,11 @@ def choose_configurator_plugins(config, plugins, verb):
|
||||
need_inst = need_auth = False
|
||||
if verb == "certonly":
|
||||
need_auth = True
|
||||
if verb == "install" or verb == "enhance":
|
||||
if verb == "install":
|
||||
need_inst = True
|
||||
if config.authenticator:
|
||||
logger.warning("Specifying an authenticator doesn't make sense when "
|
||||
"running Certbot with verb \"%s\"", verb)
|
||||
logger.warning("Specifying an authenticator doesn't make sense in install mode")
|
||||
|
||||
# Try to meet the user's request and/or ask them to pick plugins
|
||||
authenticator = installer = None
|
||||
if verb == "run" and req_auth == req_inst:
|
||||
@@ -195,7 +189,7 @@ def choose_configurator_plugins(config, plugins, verb):
|
||||
authenticator = installer = pick_configurator(config, req_inst, plugins)
|
||||
else:
|
||||
if need_inst or req_inst:
|
||||
installer = pick_installer(config, req_inst, plugins, installer_question)
|
||||
installer = pick_installer(config, req_inst, plugins)
|
||||
if need_auth:
|
||||
authenticator = pick_authenticator(config, req_auth, plugins)
|
||||
logger.debug("Selected authenticator %s and installer %s", authenticator, installer)
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
"""Plugin storage class."""
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from certbot import errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class PluginStorage(object):
|
||||
"""Class implementing storage functionality for plugins"""
|
||||
|
||||
def __init__(self, config, classkey):
|
||||
"""Initializes PluginStorage object storing required configuration
|
||||
options.
|
||||
|
||||
:param .configuration.NamespaceConfig config: Configuration object
|
||||
:param str classkey: class name to use as root key in storage file
|
||||
|
||||
"""
|
||||
|
||||
self._config = config
|
||||
self._classkey = classkey
|
||||
self._initialized = False
|
||||
self._data = None
|
||||
self._storagepath = None
|
||||
|
||||
def _initialize_storage(self):
|
||||
"""Initializes PluginStorage data and reads current state from the disk
|
||||
if the storage json exists."""
|
||||
|
||||
self._storagepath = os.path.join(self._config.config_dir, ".pluginstorage.json")
|
||||
self._load()
|
||||
self._initialized = True
|
||||
|
||||
def _load(self):
|
||||
"""Reads PluginStorage content from the disk to a dict structure
|
||||
|
||||
:raises .errors.PluginStorageError: when unable to open or read the file
|
||||
"""
|
||||
data = dict()
|
||||
filedata = ""
|
||||
try:
|
||||
with open(self._storagepath, 'r') as fh:
|
||||
filedata = fh.read()
|
||||
except IOError as e:
|
||||
errmsg = "Could not read PluginStorage data file: {0} : {1}".format(
|
||||
self._storagepath, str(e))
|
||||
if os.path.isfile(self._storagepath):
|
||||
# Only error out if file exists, but cannot be read
|
||||
logger.error(errmsg)
|
||||
raise errors.PluginStorageError(errmsg)
|
||||
try:
|
||||
data = json.loads(filedata)
|
||||
except ValueError:
|
||||
if not filedata:
|
||||
logger.debug("Plugin storage file %s was empty, no values loaded",
|
||||
self._storagepath)
|
||||
else:
|
||||
errmsg = "PluginStorage file {0} is corrupted.".format(
|
||||
self._storagepath)
|
||||
logger.error(errmsg)
|
||||
raise errors.PluginStorageError(errmsg)
|
||||
self._data = data
|
||||
|
||||
def save(self):
|
||||
"""Saves PluginStorage content to disk
|
||||
|
||||
:raises .errors.PluginStorageError: when unable to serialize the data
|
||||
or write it to the filesystem
|
||||
"""
|
||||
if not self._initialized:
|
||||
errmsg = "Unable to save, no values have been added to PluginStorage."
|
||||
logger.error(errmsg)
|
||||
raise errors.PluginStorageError(errmsg)
|
||||
|
||||
try:
|
||||
serialized = json.dumps(self._data)
|
||||
except TypeError as e:
|
||||
errmsg = "Could not serialize PluginStorage data: {0}".format(
|
||||
str(e))
|
||||
logger.error(errmsg)
|
||||
raise errors.PluginStorageError(errmsg)
|
||||
try:
|
||||
with os.fdopen(os.open(self._storagepath,
|
||||
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:
|
||||
fh.write(serialized)
|
||||
except IOError as e:
|
||||
errmsg = "Could not write PluginStorage data to file {0} : {1}".format(
|
||||
self._storagepath, str(e))
|
||||
logger.error(errmsg)
|
||||
raise errors.PluginStorageError(errmsg)
|
||||
|
||||
def put(self, key, value):
|
||||
"""Put configuration value to PluginStorage
|
||||
|
||||
:param str key: Key to store the value to
|
||||
:param value: Data to store
|
||||
"""
|
||||
if not self._initialized:
|
||||
self._initialize_storage()
|
||||
|
||||
if not self._classkey in self._data.keys():
|
||||
self._data[self._classkey] = dict()
|
||||
self._data[self._classkey][key] = value
|
||||
|
||||
def fetch(self, key):
|
||||
"""Get configuration value from PluginStorage
|
||||
|
||||
:param str key: Key to get value from the storage
|
||||
|
||||
:raises KeyError: If the key doesn't exist in the storage
|
||||
"""
|
||||
if not self._initialized:
|
||||
self._initialize_storage()
|
||||
|
||||
return self._data[self._classkey][key]
|
||||
@@ -1,117 +0,0 @@
|
||||
"""Tests for certbot.plugins.storage.PluginStorage"""
|
||||
import json
|
||||
import mock
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from certbot import errors
|
||||
|
||||
from certbot.plugins import common
|
||||
from certbot.tests import util as test_util
|
||||
|
||||
class PluginStorageTest(test_util.ConfigTestCase):
|
||||
"""Test for certbot.plugins.storage.PluginStorage"""
|
||||
|
||||
def setUp(self):
|
||||
super(PluginStorageTest, self).setUp()
|
||||
self.plugin_cls = common.Installer
|
||||
os.mkdir(self.config.config_dir)
|
||||
with mock.patch("certbot.reverter.util"):
|
||||
self.plugin = self.plugin_cls(config=self.config, name="mockplugin")
|
||||
|
||||
def test_load_errors_cant_read(self):
|
||||
with open(os.path.join(self.config.config_dir,
|
||||
".pluginstorage.json"), "w") as fh:
|
||||
fh.write("dummy")
|
||||
# When unable to read file that exists
|
||||
mock_open = mock.mock_open()
|
||||
mock_open.side_effect = IOError
|
||||
self.plugin.storage.storagepath = os.path.join(self.config.config_dir,
|
||||
".pluginstorage.json")
|
||||
with mock.patch("six.moves.builtins.open", mock_open):
|
||||
with mock.patch('os.path.isfile', return_value=True):
|
||||
with mock.patch("certbot.reverter.util"):
|
||||
self.assertRaises(errors.PluginStorageError,
|
||||
self.plugin.storage._load) # pylint: disable=protected-access
|
||||
|
||||
def test_load_errors_empty(self):
|
||||
with open(os.path.join(self.config.config_dir, ".pluginstorage.json"), "w") as fh:
|
||||
fh.write('')
|
||||
with mock.patch("certbot.plugins.storage.logger.debug") as mock_log:
|
||||
# Should not error out but write a debug log line instead
|
||||
with mock.patch("certbot.reverter.util"):
|
||||
nocontent = self.plugin_cls(self.config, "mockplugin")
|
||||
self.assertRaises(KeyError,
|
||||
nocontent.storage.fetch, "value")
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertTrue("no values loaded" in mock_log.call_args[0][0])
|
||||
|
||||
def test_load_errors_corrupted(self):
|
||||
with open(os.path.join(self.config.config_dir,
|
||||
".pluginstorage.json"), "w") as fh:
|
||||
fh.write('invalid json')
|
||||
with mock.patch("certbot.plugins.storage.logger.error") as mock_log:
|
||||
with mock.patch("certbot.reverter.util"):
|
||||
corrupted = self.plugin_cls(self.config, "mockplugin")
|
||||
self.assertRaises(errors.PluginError,
|
||||
corrupted.storage.fetch,
|
||||
"value")
|
||||
self.assertTrue("is corrupted" in mock_log.call_args[0][0])
|
||||
|
||||
def test_save_errors_cant_serialize(self):
|
||||
with mock.patch("certbot.plugins.storage.logger.error") as mock_log:
|
||||
# Set data as something that can't be serialized
|
||||
self.plugin.storage._initialized = True # pylint: disable=protected-access
|
||||
self.plugin.storage.storagepath = "/tmp/whatever"
|
||||
self.plugin.storage._data = self.plugin_cls # pylint: disable=protected-access
|
||||
self.assertRaises(errors.PluginStorageError,
|
||||
self.plugin.storage.save)
|
||||
self.assertTrue("Could not serialize" in mock_log.call_args[0][0])
|
||||
|
||||
def test_save_errors_unable_to_write_file(self):
|
||||
mock_open = mock.mock_open()
|
||||
mock_open.side_effect = IOError
|
||||
with mock.patch("os.open", mock_open):
|
||||
with mock.patch("certbot.plugins.storage.logger.error") as mock_log:
|
||||
self.plugin.storage._data = {"valid": "data"} # pylint: disable=protected-access
|
||||
self.plugin.storage._initialized = True # pylint: disable=protected-access
|
||||
self.assertRaises(errors.PluginStorageError,
|
||||
self.plugin.storage.save)
|
||||
self.assertTrue("Could not write" in mock_log.call_args[0][0])
|
||||
|
||||
def test_save_uninitialized(self):
|
||||
with mock.patch("certbot.reverter.util"):
|
||||
self.assertRaises(errors.PluginStorageError,
|
||||
self.plugin_cls(self.config, "x").storage.save)
|
||||
|
||||
def test_namespace_isolation(self):
|
||||
with mock.patch("certbot.reverter.util"):
|
||||
plugin1 = self.plugin_cls(self.config, "first")
|
||||
plugin2 = self.plugin_cls(self.config, "second")
|
||||
plugin1.storage.put("first_key", "first_value")
|
||||
self.assertRaises(KeyError,
|
||||
plugin2.storage.fetch, "first_key")
|
||||
self.assertRaises(KeyError,
|
||||
plugin2.storage.fetch, "first")
|
||||
self.assertEqual(plugin1.storage.fetch("first_key"), "first_value")
|
||||
|
||||
|
||||
def test_saved_state(self):
|
||||
self.plugin.storage.put("testkey", "testvalue")
|
||||
# Write to disk
|
||||
self.plugin.storage.save()
|
||||
with mock.patch("certbot.reverter.util"):
|
||||
another = self.plugin_cls(self.config, "mockplugin")
|
||||
self.assertEqual(another.storage.fetch("testkey"), "testvalue")
|
||||
|
||||
with open(os.path.join(self.config.config_dir,
|
||||
".pluginstorage.json"), 'r') as fh:
|
||||
psdata = fh.read()
|
||||
psjson = json.loads(psdata)
|
||||
self.assertTrue("mockplugin" in psjson.keys())
|
||||
self.assertEqual(len(psjson), 1)
|
||||
self.assertEqual(psjson["mockplugin"]["testkey"], "testvalue")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -12,14 +12,13 @@ import zope.component
|
||||
import OpenSSL
|
||||
|
||||
from certbot import cli
|
||||
|
||||
from certbot import crypto_util
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot import util
|
||||
from certbot import hooks
|
||||
from certbot import storage
|
||||
from certbot import updater
|
||||
|
||||
from certbot.plugins import disco as plugins_disco
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -412,9 +411,9 @@ def handle_renewal_request(config):
|
||||
# XXX: ensure that each call here replaces the previous one
|
||||
zope.component.provideUtility(lineage_config)
|
||||
renewal_candidate.ensure_deployed()
|
||||
from certbot import main
|
||||
plugins = plugins_disco.PluginsRegistry.find_all()
|
||||
if should_renew(lineage_config, renewal_candidate):
|
||||
plugins = plugins_disco.PluginsRegistry.find_all()
|
||||
from certbot import main
|
||||
# domains have been restored into lineage_config by reconstitute
|
||||
# but they're unnecessary anyway because renew_cert here
|
||||
# will just grab them from the certificate
|
||||
@@ -427,10 +426,6 @@ def handle_renewal_request(config):
|
||||
"cert", renewal_candidate.latest_common_version()))
|
||||
renew_skipped.append("%s expires on %s" % (renewal_candidate.fullchain,
|
||||
expiry.strftime("%Y-%m-%d")))
|
||||
# Run updater interface methods
|
||||
updater.run_generic_updaters(lineage_config, plugins,
|
||||
renewal_candidate)
|
||||
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
# obtain_cert (presumably) encountered an unanticipated problem.
|
||||
logger.warning("Attempting to renew cert (%s) from %s produced an "
|
||||
|
||||
@@ -1053,9 +1053,6 @@ class RenewableCert(object):
|
||||
"`cert.pem` : will break many server configurations, and "
|
||||
"should not be used\n"
|
||||
" without reading further documentation (see link below).\n\n"
|
||||
"WARNING: DO NOT MOVE THESE FILES!\n"
|
||||
" Certbot expects these files to remain in this location in order\n"
|
||||
" to function properly!\n\n"
|
||||
"We recommend not moving these files. For more information, see the Certbot\n"
|
||||
"User Guide at https://certbot.eff.org/docs/using.html#where-are-my-"
|
||||
"certificates.\n")
|
||||
|
||||
@@ -216,12 +216,11 @@ class CertificatesTest(BaseCertManagerTest):
|
||||
cert.is_test_cert = False
|
||||
parsed_certs = [cert]
|
||||
|
||||
mock_config = mock.MagicMock(certname=None, lineagename=None)
|
||||
# pylint: disable=protected-access
|
||||
|
||||
# pylint: disable=protected-access
|
||||
get_report = lambda: cert_manager._report_human_readable(mock_config, parsed_certs)
|
||||
|
||||
mock_config = mock.MagicMock(certname=None, lineagename=None)
|
||||
# pylint: disable=protected-access
|
||||
out = get_report()
|
||||
self.assertTrue("INVALID: EXPIRED" in out)
|
||||
|
||||
@@ -569,103 +568,5 @@ class MatchAndCheckOverlaps(storage_test.BaseRenewableCertTest):
|
||||
self.assertRaises(errors.OverlappingMatchFound, self._call, self.config, None, None, None)
|
||||
|
||||
|
||||
class GetCertnameTest(unittest.TestCase):
|
||||
"""Tests for certbot.cert_manager."""
|
||||
|
||||
def setUp(self):
|
||||
self.get_utility_patch = test_util.patch_get_utility()
|
||||
self.mock_get_utility = self.get_utility_patch.start()
|
||||
self.config = mock.MagicMock()
|
||||
self.config.certname = None
|
||||
|
||||
def tearDown(self):
|
||||
self.get_utility_patch.stop()
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
prompt = "Which certificate would you"
|
||||
self.mock_get_utility().menu.return_value = (display_util.OK, 0)
|
||||
self.assertEquals(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=False), ['example.com'])
|
||||
self.assertTrue(
|
||||
prompt in self.mock_get_utility().menu.call_args[0][0])
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_custom_prompt(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
prompt = "custom prompt"
|
||||
self.mock_get_utility().menu.return_value = (display_util.OK, 0)
|
||||
self.assertEquals(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=False, custom_prompt=prompt),
|
||||
['example.com'])
|
||||
self.assertEquals(self.mock_get_utility().menu.call_args[0][0],
|
||||
prompt)
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_user_abort(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
self.mock_get_utility().menu.return_value = (display_util.CANCEL, 0)
|
||||
self.assertRaises(
|
||||
errors.Error,
|
||||
cert_manager.get_certnames,
|
||||
self.config, "erroring_anyway", allow_multiple=False)
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_allow_multiple(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
prompt = "Which certificate(s) would you"
|
||||
self.mock_get_utility().checklist.return_value = (display_util.OK,
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=True), ['example.com'])
|
||||
self.assertTrue(
|
||||
prompt in self.mock_get_utility().checklist.call_args[0][0])
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_allow_multiple_custom_prompt(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
prompt = "custom prompt"
|
||||
self.mock_get_utility().checklist.return_value = (display_util.OK,
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=True, custom_prompt=prompt),
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
self.mock_get_utility().checklist.call_args[0][0],
|
||||
prompt)
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_allow_multiple_user_abort(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
self.mock_get_utility().checklist.return_value = (display_util.CANCEL, [])
|
||||
self.assertRaises(
|
||||
errors.Error,
|
||||
cert_manager.get_certnames,
|
||||
self.config, "erroring_anyway", allow_multiple=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -433,22 +433,6 @@ class EnhanceConfigTest(ClientTestCommon):
|
||||
self.client.installer.enhance.assert_not_called()
|
||||
mock_enhancements.ask.assert_not_called()
|
||||
|
||||
@mock.patch("certbot.client.logger")
|
||||
def test_already_exists_header(self, mock_log):
|
||||
self.config.hsts = True
|
||||
self._test_with_already_existing()
|
||||
self.assertTrue(mock_log.warning.called)
|
||||
self.assertEquals(mock_log.warning.call_args[0][1],
|
||||
'Strict-Transport-Security')
|
||||
|
||||
@mock.patch("certbot.client.logger")
|
||||
def test_already_exists_redirect(self, mock_log):
|
||||
self.config.redirect = True
|
||||
self._test_with_already_existing()
|
||||
self.assertTrue(mock_log.warning.called)
|
||||
self.assertEquals(mock_log.warning.call_args[0][1],
|
||||
'redirect')
|
||||
|
||||
def test_no_ask_hsts(self):
|
||||
self.config.hsts = True
|
||||
self._test_with_all_supported()
|
||||
@@ -524,13 +508,6 @@ class EnhanceConfigTest(ClientTestCommon):
|
||||
self.assertEqual(self.client.installer.save.call_count, 1)
|
||||
self.assertEqual(self.client.installer.restart.call_count, 1)
|
||||
|
||||
def _test_with_already_existing(self):
|
||||
self.client.installer = mock.MagicMock()
|
||||
self.client.installer.supported_enhancements.return_value = [
|
||||
"ensure-http-header", "redirect", "staple-ocsp"]
|
||||
self.client.installer.enhance.side_effect = errors.PluginEnhancementAlreadyPresent()
|
||||
self.client.enhance_config([self.domain], None)
|
||||
|
||||
|
||||
class RollbackTest(unittest.TestCase):
|
||||
"""Tests for certbot.client.rollback."""
|
||||
|
||||
@@ -207,9 +207,9 @@ class ChooseNamesTest(unittest.TestCase):
|
||||
self.mock_install = mock.MagicMock()
|
||||
|
||||
@classmethod
|
||||
def _call(cls, installer, question=None):
|
||||
def _call(cls, installer):
|
||||
from certbot.display.ops import choose_names
|
||||
return choose_names(installer, question)
|
||||
return choose_names(installer)
|
||||
|
||||
@mock.patch("certbot.display.ops._choose_names_manually")
|
||||
def test_no_installer(self, mock_manual):
|
||||
@@ -281,15 +281,6 @@ class ChooseNamesTest(unittest.TestCase):
|
||||
self.assertEqual(names, ["example.com"])
|
||||
self.assertEqual(mock_util().checklist.call_count, 1)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_filter_namees_override_question(self, mock_util):
|
||||
self.mock_install.get_all_names.return_value = set(["example.com"])
|
||||
mock_util().checklist.return_value = (display_util.OK, ["example.com"])
|
||||
names = self._call(self.mock_install, "Custom")
|
||||
self.assertEqual(names, ["example.com"])
|
||||
self.assertEqual(mock_util().checklist.call_count, 1)
|
||||
self.assertEqual(mock_util().checklist.call_args[0][0], "Custom")
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_filter_names_nothing_selected(self, mock_util):
|
||||
self.mock_install.get_all_names.return_value = set(["example.com"])
|
||||
@@ -490,42 +481,5 @@ class ValidatorTests(unittest.TestCase):
|
||||
self.__validator, "msg", default="")
|
||||
|
||||
|
||||
class ChooseValuesTest(unittest.TestCase):
|
||||
"""Test choose_values."""
|
||||
@classmethod
|
||||
def _call(cls, values, question):
|
||||
from certbot.display.ops import choose_values
|
||||
return choose_values(values, question)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_choose_names_success(self, mock_util):
|
||||
items = ["first", "second", "third"]
|
||||
mock_util().checklist.return_value = (display_util.OK, [items[2]])
|
||||
result = self._call(items, None)
|
||||
self.assertEquals(result, [items[2]])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], None)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_choose_names_success_question(self, mock_util):
|
||||
items = ["first", "second", "third"]
|
||||
question = "Which one?"
|
||||
mock_util().checklist.return_value = (display_util.OK, [items[1]])
|
||||
result = self._call(items, question)
|
||||
self.assertEquals(result, [items[1]])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], question)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_choose_names_user_cancel(self, mock_util):
|
||||
items = ["first", "second", "third"]
|
||||
question = "Want to cancel?"
|
||||
mock_util().checklist.return_value = (display_util.CANCEL, [])
|
||||
result = self._call(items, question)
|
||||
self.assertEquals(result, [])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], question)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -23,7 +23,6 @@ from certbot import configuration
|
||||
from certbot import crypto_util
|
||||
from certbot import errors
|
||||
from certbot import main
|
||||
from certbot import updater
|
||||
from certbot import util
|
||||
|
||||
from certbot.plugins import disco
|
||||
@@ -1233,9 +1232,7 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
||||
self._test_renew_common(renewalparams=renewalparams, error_expected=True,
|
||||
names=names, assert_oc_called=False)
|
||||
|
||||
@mock.patch('certbot.plugins.selection.choose_configurator_plugins')
|
||||
def test_renew_with_configurator(self, mock_sel):
|
||||
mock_sel.return_value = (mock.MagicMock(), mock.MagicMock())
|
||||
def test_renew_with_configurator(self):
|
||||
renewalparams = {'authenticator': 'webroot'}
|
||||
self._test_renew_common(
|
||||
renewalparams=renewalparams, assert_oc_called=True,
|
||||
@@ -1451,18 +1448,6 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
||||
email in mock_utility().add_message.call_args[0][0])
|
||||
self.assertTrue(mock_handle.called)
|
||||
|
||||
@mock.patch('certbot.plugins.selection.choose_configurator_plugins')
|
||||
def test_plugin_selection_error(self, mock_choose):
|
||||
mock_choose.side_effect = errors.PluginSelectionError
|
||||
self.assertRaises(errors.PluginSelectionError, main.renew_cert,
|
||||
None, None, None)
|
||||
|
||||
with mock.patch('certbot.updater.logger.warning') as mock_log:
|
||||
updater.run_generic_updaters(None, None, None)
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertTrue("Could not choose appropriate plugin for updaters"
|
||||
in mock_log.call_args[0][0])
|
||||
|
||||
|
||||
class UnregisterTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -1548,114 +1533,5 @@ class MakeOrVerifyNeededDirs(test_util.ConfigTestCase):
|
||||
strict=self.config.strict_permissions)
|
||||
|
||||
|
||||
class EnhanceTest(unittest.TestCase):
|
||||
"""Tests for certbot.main.enhance."""
|
||||
|
||||
def setUp(self):
|
||||
self.get_utility_patch = test_util.patch_get_utility()
|
||||
self.mock_get_utility = self.get_utility_patch.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.get_utility_patch.stop()
|
||||
|
||||
def _call(self, args):
|
||||
plugins = disco.PluginsRegistry.find_all()
|
||||
config = configuration.NamespaceConfig(
|
||||
cli.prepare_and_parse_args(plugins, args))
|
||||
|
||||
with mock.patch('certbot.cert_manager.get_certnames') as mock_certs:
|
||||
mock_certs.return_value = ['example.com']
|
||||
with mock.patch('certbot.cert_manager.domains_for_certname') as mock_dom:
|
||||
mock_dom.return_value = ['example.com']
|
||||
with mock.patch('certbot.main._init_le_client') as mock_init:
|
||||
mock_client = mock.MagicMock()
|
||||
mock_client.config = config
|
||||
mock_init.return_value = mock_client
|
||||
main.enhance(config, plugins)
|
||||
return mock_client # returns the client
|
||||
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main._find_domains_or_certname')
|
||||
def test_selection_question(self, mock_find, mock_choose, mock_lineage, _rec):
|
||||
mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
|
||||
mock_choose.return_value = ['example.com']
|
||||
mock_find.return_value = (None, None)
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer') as mock_pick:
|
||||
self._call(['enhance', '--redirect'])
|
||||
self.assertTrue(mock_pick.called)
|
||||
# Check that the message includes "enhancements"
|
||||
self.assertTrue("enhancements" in mock_pick.call_args[0][3])
|
||||
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main._find_domains_or_certname')
|
||||
def test_selection_auth_warning(self, mock_find, mock_choose, mock_lineage, _rec):
|
||||
mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
|
||||
mock_choose.return_value = ["example.com"]
|
||||
mock_find.return_value = (None, None)
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer'):
|
||||
with mock.patch('certbot.main.plug_sel.logger.warning') as mock_log:
|
||||
mock_client = self._call(['enhance', '-a', 'webroot', '--redirect'])
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertTrue("make sense" in mock_log.call_args[0][0])
|
||||
self.assertTrue(mock_client.enhance_config.called)
|
||||
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
def test_enhance_config_call(self, _rec, mock_choose, mock_lineage):
|
||||
mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
|
||||
mock_choose.return_value = ["example.com"]
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer'):
|
||||
mock_client = self._call(['enhance', '--redirect', '--hsts'])
|
||||
req_enh = ["redirect", "hsts"]
|
||||
not_req_enh = ["uir"]
|
||||
self.assertTrue(mock_client.enhance_config.called)
|
||||
self.assertTrue(
|
||||
all([getattr(mock_client.config, e) for e in req_enh]))
|
||||
self.assertFalse(
|
||||
any([getattr(mock_client.config, e) for e in not_req_enh]))
|
||||
self.assertTrue(
|
||||
"example.com" in mock_client.enhance_config.call_args[0][0])
|
||||
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
def test_enhance_noninteractive(self, _rec, mock_choose, mock_lineage):
|
||||
mock_lineage.return_value = mock.MagicMock(
|
||||
chain_path="/tmp/nonexistent")
|
||||
mock_choose.return_value = ["example.com"]
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer'):
|
||||
mock_client = self._call(['enhance', '--redirect',
|
||||
'--hsts', '--non-interactive'])
|
||||
self.assertTrue(mock_client.enhance_config.called)
|
||||
self.assertFalse(mock_choose.called)
|
||||
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
def test_user_abort_domains(self, _rec, mock_choose):
|
||||
mock_choose.return_value = []
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer'):
|
||||
self.assertRaises(errors.Error,
|
||||
self._call,
|
||||
['enhance', '--redirect', '--hsts'])
|
||||
|
||||
def test_no_enhancements_defined(self):
|
||||
self.assertRaises(errors.MisconfigurationError,
|
||||
self._call, ['enhance'])
|
||||
|
||||
@mock.patch('certbot.main.plug_sel.choose_configurator_plugins')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
def test_plugin_selection_error(self, _rec, mock_choose, mock_pick):
|
||||
mock_choose.return_value = ["example.com"]
|
||||
mock_pick.return_value = (None, None)
|
||||
mock_pick.side_effect = errors.PluginSelectionError()
|
||||
mock_client = self._call(['enhance', '--hsts'])
|
||||
self.assertFalse(mock_client.enhance_config.called)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
"""Tests for renewal updater interfaces"""
|
||||
import unittest
|
||||
import mock
|
||||
|
||||
from certbot import interfaces
|
||||
from certbot import main
|
||||
from certbot import updater
|
||||
|
||||
import certbot.tests.util as test_util
|
||||
|
||||
|
||||
class RenewUpdaterTest(unittest.TestCase):
|
||||
"""Tests for interfaces.RenewDeployer and interfaces.GenericUpdater"""
|
||||
|
||||
def setUp(self):
|
||||
class MockInstallerGenericUpdater(interfaces.GenericUpdater):
|
||||
"""Mock class that implements GenericUpdater"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
# pylint: disable=unused-argument
|
||||
self.restart = mock.MagicMock()
|
||||
self.callcounter = mock.MagicMock()
|
||||
def generic_updates(self, domain, *args, **kwargs):
|
||||
self.callcounter(*args, **kwargs)
|
||||
|
||||
class MockInstallerRenewDeployer(interfaces.RenewDeployer):
|
||||
"""Mock class that implements RenewDeployer"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
# pylint: disable=unused-argument
|
||||
self.callcounter = mock.MagicMock()
|
||||
def renew_deploy(self, lineage, *args, **kwargs):
|
||||
self.callcounter(*args, **kwargs)
|
||||
|
||||
self.generic_updater = MockInstallerGenericUpdater()
|
||||
self.renew_deployer = MockInstallerRenewDeployer()
|
||||
|
||||
def get_config(self, args):
|
||||
"""Get mock config from dict of parameters"""
|
||||
config = mock.MagicMock()
|
||||
for key in args.keys():
|
||||
config.__dict__[key] = args[key]
|
||||
return config
|
||||
|
||||
@mock.patch('certbot.main._get_and_save_cert')
|
||||
@mock.patch('certbot.plugins.selection.choose_configurator_plugins')
|
||||
@test_util.patch_get_utility()
|
||||
def test_server_updates(self, _, mock_select, mock_getsave):
|
||||
config = self.get_config({"disable_renew_updates": False})
|
||||
|
||||
lineage = mock.MagicMock()
|
||||
lineage.names.return_value = ['firstdomain', 'seconddomain']
|
||||
mock_getsave.return_value = lineage
|
||||
mock_generic_updater = self.generic_updater
|
||||
|
||||
# Generic Updater
|
||||
mock_select.return_value = (mock_generic_updater, None)
|
||||
with mock.patch('certbot.main._init_le_client'):
|
||||
main.renew_cert(config, None, mock.MagicMock())
|
||||
self.assertTrue(mock_generic_updater.restart.called)
|
||||
|
||||
mock_generic_updater.restart.reset_mock()
|
||||
mock_generic_updater.callcounter.reset_mock()
|
||||
updater.run_generic_updaters(config, None, lineage)
|
||||
self.assertEqual(mock_generic_updater.callcounter.call_count, 2)
|
||||
self.assertFalse(mock_generic_updater.restart.called)
|
||||
|
||||
def test_renew_deployer(self):
|
||||
config = self.get_config({"disable_renew_updates": False})
|
||||
lineage = mock.MagicMock()
|
||||
lineage.names.return_value = ['firstdomain', 'seconddomain']
|
||||
mock_deployer = self.renew_deployer
|
||||
updater.run_renewal_deployer(lineage, mock_deployer, config)
|
||||
self.assertTrue(mock_deployer.callcounter.called_with(lineage))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,67 +0,0 @@
|
||||
"""Updaters run at renewal"""
|
||||
import logging
|
||||
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
|
||||
from certbot.plugins import selection as plug_sel
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def run_generic_updaters(config, plugins, lineage):
|
||||
"""Run updaters that the plugin supports
|
||||
|
||||
:param config: Configuration object
|
||||
:type config: interfaces.IConfig
|
||||
|
||||
:param plugins: List of plugins
|
||||
:type plugins: `list` of `str`
|
||||
|
||||
:param lineage: Certificate lineage object
|
||||
:type lineage: storage.RenewableCert
|
||||
|
||||
:returns: `None`
|
||||
:rtype: None
|
||||
"""
|
||||
try:
|
||||
# installers are used in auth mode to determine domain names
|
||||
installer, _ = plug_sel.choose_configurator_plugins(config, plugins, "certonly")
|
||||
except errors.PluginSelectionError as e:
|
||||
logger.warning("Could not choose appropriate plugin for updaters: %s", e)
|
||||
return
|
||||
_run_updaters(lineage, installer, config)
|
||||
|
||||
def run_renewal_deployer(lineage, installer, config):
|
||||
"""Helper function to run deployer interface method if supported by the used
|
||||
installer plugin.
|
||||
|
||||
:param lineage: Certificate lineage object
|
||||
:type lineage: storage.RenewableCert
|
||||
|
||||
:param installer: Installer object
|
||||
:type installer: interfaces.IInstaller
|
||||
|
||||
:returns: `None`
|
||||
:rtype: None
|
||||
"""
|
||||
if not config.disable_renew_updates and isinstance(installer,
|
||||
interfaces.RenewDeployer):
|
||||
installer.renew_deploy(lineage)
|
||||
|
||||
def _run_updaters(lineage, installer, config):
|
||||
"""Helper function to run the updater interface methods if supported by the
|
||||
used installer plugin.
|
||||
|
||||
:param lineage: Certificate lineage object
|
||||
:type lineage: storage.RenewableCert
|
||||
|
||||
:param installer: Installer object
|
||||
:type installer: interfaces.IInstaller
|
||||
|
||||
:returns: `None`
|
||||
:rtype: None
|
||||
"""
|
||||
for domain in lineage.names():
|
||||
if not config.disable_renew_updates:
|
||||
if isinstance(installer, interfaces.GenericUpdater):
|
||||
installer.generic_updates(domain)
|
||||
@@ -54,7 +54,7 @@ _INITIAL_PID = os.getpid()
|
||||
# the dict are attempted to be cleaned up at program exit. If the
|
||||
# program exits before the lock is cleaned up, it is automatically
|
||||
# released, but the file isn't deleted.
|
||||
_LOCKS = OrderedDict() # type: OrderedDict[str, lock.LockFile]
|
||||
_LOCKS = OrderedDict()
|
||||
|
||||
|
||||
def run_script(params, log=logger.error):
|
||||
|
||||
@@ -3,7 +3,3 @@
|
||||
|
||||
.. automodule:: certbot.constants
|
||||
:members:
|
||||
:exclude-members: SSL_DHPARAMS_SRC
|
||||
|
||||
.. autodata:: SSL_DHPARAMS_SRC
|
||||
:annotation: = '/path/to/certbot/ssl-dhparams.pem'
|
||||
|
||||
@@ -107,8 +107,8 @@ 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.23.0 (certbot;
|
||||
darwin 10.13.4) Authenticator/XXX Installer/YYY
|
||||
"". (default: CertbotACMEClient/0.22.2 (certbot;
|
||||
darwin 10.13.3) Authenticator/XXX Installer/YYY
|
||||
(SUBCOMMAND; flags: FLAGS) Py/2.7.14). The flags
|
||||
encoded in the user agent are: --duplicate, --force-
|
||||
renew, --allow-subset-of-names, -n, and whether any
|
||||
|
||||
@@ -63,7 +63,7 @@ Find issues to work on
|
||||
----------------------
|
||||
|
||||
You can find the open issues in the `github issue tracker`_. Comparatively
|
||||
easy ones are marked `good first issue`_. If you're starting work on
|
||||
easy ones are marked `Good Volunteer Task`_. If you're starting work on
|
||||
something, post a comment to let others know and seek feedback on your plan
|
||||
where appropriate.
|
||||
|
||||
@@ -72,7 +72,7 @@ your pull request must have thorough unit test coverage, pass our
|
||||
tests, and be compliant with the :ref:`coding style <coding-style>`.
|
||||
|
||||
.. _github issue tracker: https://github.com/certbot/certbot/issues
|
||||
.. _good first issue: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22
|
||||
.. _Good Volunteer Task: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22
|
||||
|
||||
.. _testing:
|
||||
|
||||
@@ -376,9 +376,6 @@ commands:
|
||||
This should generate documentation in the ``docs/_build/html``
|
||||
directory.
|
||||
|
||||
.. note:: If you skipped the "Getting Started" instructions above,
|
||||
run ``pip install -e ".[docs]"`` to install Certbot's docs extras modules.
|
||||
|
||||
|
||||
.. _docker-dev:
|
||||
|
||||
|
||||
@@ -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.23.0"
|
||||
LE_AUTO_VERSION="0.22.2"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
@@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \
|
||||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.23.0 \
|
||||
--hash=sha256:66c42cf780ddbf582ecc52aa6a61242450a2650227b436ad0d260685c4ef8a49 \
|
||||
--hash=sha256:6cff4c5da1228661ccaf95195064cb29e6cdf80913193bdb2eb20e164c76053e
|
||||
acme==0.23.0 \
|
||||
--hash=sha256:02e9b596bd3bf8f0733d6d43ec2464ac8185a000acb58d2b4fd9e19223bbbf0b \
|
||||
--hash=sha256:08c16635578507f526c338b3418c1147a9f015bf2d366abd51f38918703b4550
|
||||
certbot-apache==0.23.0 \
|
||||
--hash=sha256:50077742d2763b7600dfda618eb89c870aeea5e6a4c00f60157877f7a7d81f7c \
|
||||
--hash=sha256:6b7acec243e224de5268d46c2597277586dffa55e838c252b6931c30d549028e
|
||||
certbot-nginx==0.23.0 \
|
||||
--hash=sha256:f12c21bbe3eb955ca533f1da96d28c6310378b138e844d83253562e18b6cbb32 \
|
||||
--hash=sha256:cadf14e4bd504d9ce5987a5ec6dbd8e136638e55303ad5dc81dcb723ddd64324
|
||||
certbot==0.22.2 \
|
||||
--hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \
|
||||
--hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef
|
||||
acme==0.22.2 \
|
||||
--hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \
|
||||
--hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4
|
||||
certbot-apache==0.22.2 \
|
||||
--hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \
|
||||
--hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759
|
||||
certbot-nginx==0.22.2 \
|
||||
--hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \
|
||||
--hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlrFS/EACgkQTRfJlc2X
|
||||
dfK+rQf8DcKY5bMi5eJnwwAlui6WIyWSrf1KAKt09tEGZSHQ1fcyCPrGVhk7VVDg
|
||||
NJ1/XiYBquPW+7mYUcHrIRsiKYbTUcmVjyqP6tZd67IxRH9ToNqBzA6kq99T+IPd
|
||||
iTGdczHMSPcxM6/Fa5PYMHXy2+ctTr/8+gnsxth9QfcM62Yd6ecfqIdoId3vk9Aw
|
||||
UBMENZhUasIvgZDWuow+1XVZ/DAmdvj2Xl/E3sA9i2ArREJhkhVegtdrHkwSY+Hm
|
||||
MKfZGqNVse6ZAF/8YdEVBum0OngMMs63DwucwFxmw5DqWtmnXm6awLNW/LQ/3R5L
|
||||
xuKjcVaAT1h5TgIyRT6opH8JBKmLpg==
|
||||
=Ouj4
|
||||
iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlqwWJwACgkQTRfJlc2X
|
||||
dfIzmwgAghmc3W63/qpCtJdezYeGLJdu03LvKoWYc7dTNYj2+0P5qmAAgCvKNY34
|
||||
qYzXA1jfCOgILSzRNE5WY+rbgjcmxxsxH+luYm6Ik0909MaMQ0D3h+5cRFs/tTtd
|
||||
5cX0gxL3RQQTBwpnwbAZibe7lhjs9pXBiob2ek67hVr+xEwem69BQMlOhtYJbOs1
|
||||
osccoKc4NqaKbrfgOjjtMaL8YoRPO9vJHS9rRr6hxRZlPsmvusAHAiCbIrbX4XKE
|
||||
CgxJFnuHK+amtfRoZg/xCqIK3Z94yZXPezywsri/YvDteOIs+DZ2qG/StfUrNYFX
|
||||
WYfFFFyld0xwQtb4Oi9u4mx4sPg7lw==
|
||||
=jZDE
|
||||
-----END PGP SIGNATURE-----
|
||||
|
||||
@@ -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.24.0.dev0"
|
||||
LE_AUTO_VERSION="0.23.0.dev0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
@@ -1089,7 +1089,7 @@ cryptography==2.0.2 \
|
||||
--hash=sha256:01e6e60654df64cca53733cda39446d67100c819c181d403afb120e0d2a71e1b \
|
||||
--hash=sha256:d46f4e5d455cb5563685c52ef212696f0a6cc1ea627603218eabbd8a095291d8 \
|
||||
--hash=sha256:3780b2663ee7ebb37cb83263326e3cd7f8b2ea439c448539d4b87de12c8d06ab
|
||||
enum34==1.1.2 ; python_version < '3.4' \
|
||||
enum34==1.1.2 \
|
||||
--hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \
|
||||
--hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501
|
||||
funcsigs==1.0.2 \
|
||||
@@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \
|
||||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.23.0 \
|
||||
--hash=sha256:66c42cf780ddbf582ecc52aa6a61242450a2650227b436ad0d260685c4ef8a49 \
|
||||
--hash=sha256:6cff4c5da1228661ccaf95195064cb29e6cdf80913193bdb2eb20e164c76053e
|
||||
acme==0.23.0 \
|
||||
--hash=sha256:02e9b596bd3bf8f0733d6d43ec2464ac8185a000acb58d2b4fd9e19223bbbf0b \
|
||||
--hash=sha256:08c16635578507f526c338b3418c1147a9f015bf2d366abd51f38918703b4550
|
||||
certbot-apache==0.23.0 \
|
||||
--hash=sha256:50077742d2763b7600dfda618eb89c870aeea5e6a4c00f60157877f7a7d81f7c \
|
||||
--hash=sha256:6b7acec243e224de5268d46c2597277586dffa55e838c252b6931c30d549028e
|
||||
certbot-nginx==0.23.0 \
|
||||
--hash=sha256:f12c21bbe3eb955ca533f1da96d28c6310378b138e844d83253562e18b6cbb32 \
|
||||
--hash=sha256:cadf14e4bd504d9ce5987a5ec6dbd8e136638e55303ad5dc81dcb723ddd64324
|
||||
certbot==0.22.2 \
|
||||
--hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \
|
||||
--hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef
|
||||
acme==0.22.2 \
|
||||
--hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \
|
||||
--hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4
|
||||
certbot-apache==0.22.2 \
|
||||
--hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \
|
||||
--hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759
|
||||
certbot-nginx==0.22.2 \
|
||||
--hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \
|
||||
--hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
Binary file not shown.
@@ -1,12 +1,12 @@
|
||||
certbot==0.23.0 \
|
||||
--hash=sha256:66c42cf780ddbf582ecc52aa6a61242450a2650227b436ad0d260685c4ef8a49 \
|
||||
--hash=sha256:6cff4c5da1228661ccaf95195064cb29e6cdf80913193bdb2eb20e164c76053e
|
||||
acme==0.23.0 \
|
||||
--hash=sha256:02e9b596bd3bf8f0733d6d43ec2464ac8185a000acb58d2b4fd9e19223bbbf0b \
|
||||
--hash=sha256:08c16635578507f526c338b3418c1147a9f015bf2d366abd51f38918703b4550
|
||||
certbot-apache==0.23.0 \
|
||||
--hash=sha256:50077742d2763b7600dfda618eb89c870aeea5e6a4c00f60157877f7a7d81f7c \
|
||||
--hash=sha256:6b7acec243e224de5268d46c2597277586dffa55e838c252b6931c30d549028e
|
||||
certbot-nginx==0.23.0 \
|
||||
--hash=sha256:f12c21bbe3eb955ca533f1da96d28c6310378b138e844d83253562e18b6cbb32 \
|
||||
--hash=sha256:cadf14e4bd504d9ce5987a5ec6dbd8e136638e55303ad5dc81dcb723ddd64324
|
||||
certbot==0.22.2 \
|
||||
--hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \
|
||||
--hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef
|
||||
acme==0.22.2 \
|
||||
--hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \
|
||||
--hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4
|
||||
certbot-apache==0.22.2 \
|
||||
--hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \
|
||||
--hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759
|
||||
certbot-nginx==0.22.2 \
|
||||
--hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \
|
||||
--hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b
|
||||
|
||||
@@ -93,7 +93,7 @@ cryptography==2.0.2 \
|
||||
--hash=sha256:01e6e60654df64cca53733cda39446d67100c819c181d403afb120e0d2a71e1b \
|
||||
--hash=sha256:d46f4e5d455cb5563685c52ef212696f0a6cc1ea627603218eabbd8a095291d8 \
|
||||
--hash=sha256:3780b2663ee7ebb37cb83263326e3cd7f8b2ea439c448539d4b87de12c8d06ab
|
||||
enum34==1.1.2 ; python_version < '3.4' \
|
||||
enum34==1.1.2 \
|
||||
--hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \
|
||||
--hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501
|
||||
funcsigs==1.0.2 \
|
||||
|
||||
2
pytest.ini
Normal file
2
pytest.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
addopts = --quiet
|
||||
9
setup.py
9
setup.py
@@ -34,7 +34,9 @@ version = meta['version']
|
||||
# specified here to avoid masking the more specific request requirements in
|
||||
# acme. See https://github.com/pypa/pip/issues/988 for more info.
|
||||
install_requires = [
|
||||
'acme>=0.22.1',
|
||||
# Remember to update local-oldest-requirements.txt when changing the
|
||||
# minimum acme version.
|
||||
'acme>0.21.1',
|
||||
# We technically need ConfigArgParse 0.10.0 for Python 2.6 support, but
|
||||
# saying so here causes a runtime error against our temporary fork of 0.9.3
|
||||
# in which we added 2.6 support (see #2243), so we relax the requirement.
|
||||
@@ -65,10 +67,6 @@ dev_extras = [
|
||||
'wheel',
|
||||
]
|
||||
|
||||
dev3_extras = [
|
||||
'mypy',
|
||||
]
|
||||
|
||||
docs_extras = [
|
||||
'repoze.sphinx.autointerface',
|
||||
# autodoc_member_order = 'bysource', autodoc_default_flags, and #4686
|
||||
@@ -114,7 +112,6 @@ setup(
|
||||
install_requires=install_requires,
|
||||
extras_require={
|
||||
'dev': dev_extras,
|
||||
'dev3': dev3_extras,
|
||||
'docs': docs_extras,
|
||||
},
|
||||
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
targets:
|
||||
#-----------------------------------------------------------------------------
|
||||
#Ubuntu
|
||||
- ami: ami-26d5af4c
|
||||
name: ubuntu15.10
|
||||
type: ubuntu
|
||||
virt: hvm
|
||||
user: ubuntu
|
||||
- ami: ami-d92e6bb3
|
||||
name: ubuntu15.04LTS
|
||||
type: ubuntu
|
||||
virt: hvm
|
||||
user: ubuntu
|
||||
- ami: ami-7b89cc11
|
||||
name: ubuntu14.04LTS
|
||||
type: ubuntu
|
||||
@@ -11,6 +21,11 @@ targets:
|
||||
type: ubuntu
|
||||
virt: pv
|
||||
user: ubuntu
|
||||
- ami: ami-0611546c
|
||||
name: ubuntu12.04LTS
|
||||
type: ubuntu
|
||||
virt: hvm
|
||||
user: ubuntu
|
||||
#-----------------------------------------------------------------------------
|
||||
# Debian
|
||||
- ami: ami-116d857a
|
||||
|
||||
@@ -30,7 +30,6 @@ josepy==1.0.1
|
||||
logger==1.4
|
||||
logilab-common==1.4.1
|
||||
MarkupSafe==1.0
|
||||
mypy==0.580
|
||||
ndg-httpsclient==0.3.2
|
||||
oauth2client==2.0.0
|
||||
pathlib2==2.3.0
|
||||
@@ -67,8 +66,6 @@ tox==2.9.1
|
||||
tqdm==4.19.4
|
||||
traitlets==4.3.2
|
||||
twine==1.9.1
|
||||
typed-ast==1.1.0
|
||||
typing==3.6.4
|
||||
uritemplate==0.6
|
||||
virtualenv==15.1.0
|
||||
wcwidth==0.1.7
|
||||
|
||||
@@ -19,5 +19,5 @@ for requirement in "$@" ; do
|
||||
if [ $pkg = "." ]; then
|
||||
pkg="certbot"
|
||||
fi
|
||||
pytest --numprocesses auto --quiet --pyargs $pkg
|
||||
"$(dirname $0)/pytest.sh" --pyargs $pkg
|
||||
done
|
||||
|
||||
15
tools/pytest.sh
Executable file
15
tools/pytest.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# Runs pytest with the provided arguments, adding --numprocesses to the command
|
||||
# line. This argument is set to "auto" if the environmnent variable TRAVIS is
|
||||
# not set, otherwise, it is set to 2. This works around
|
||||
# https://github.com/pytest-dev/pytest-xdist/issues/9. Currently every Travis
|
||||
# environnment provides two cores. See
|
||||
# https://docs.travis-ci.com/user/reference/overview/#Virtualization-environments.
|
||||
|
||||
if ${TRAVIS:-false}; then
|
||||
NUMPROCESSES="2"
|
||||
else
|
||||
NUMPROCESSES="auto"
|
||||
fi
|
||||
|
||||
pytest --numprocesses "$NUMPROCESSES" "$@"
|
||||
@@ -51,7 +51,8 @@ cover () {
|
||||
fi
|
||||
|
||||
pkg_dir=$(echo "$1" | tr _ -)
|
||||
pytest --cov "$pkg_dir" --cov-append --cov-report= --numprocesses auto --pyargs "$1"
|
||||
pytest="$(dirname $0)/tools/pytest.sh"
|
||||
"$pytest" --cov "$pkg_dir" --cov-append --cov-report= --pyargs "$1"
|
||||
coverage report --fail-under="$min" --include="$pkg_dir/*" --show-missing
|
||||
}
|
||||
|
||||
|
||||
22
tox.ini
22
tox.ini
@@ -60,6 +60,8 @@ commands =
|
||||
setenv =
|
||||
PYTHONPATH = {toxinidir}
|
||||
PYTHONHASHSEED = 0
|
||||
passenv =
|
||||
TRAVIS
|
||||
|
||||
[testenv:py27-oldest]
|
||||
commands =
|
||||
@@ -67,30 +69,40 @@ commands =
|
||||
setenv =
|
||||
{[testenv]setenv}
|
||||
CERTBOT_OLDEST=1
|
||||
passenv =
|
||||
{[testenv]passenv}
|
||||
|
||||
[testenv:py27-acme-oldest]
|
||||
commands =
|
||||
{[base]install_and_test} acme[dev]
|
||||
setenv =
|
||||
{[testenv:py27-oldest]setenv}
|
||||
passenv =
|
||||
{[testenv:py27-oldest]passenv}
|
||||
|
||||
[testenv:py27-apache-oldest]
|
||||
commands =
|
||||
{[base]install_and_test} certbot-apache
|
||||
setenv =
|
||||
{[testenv:py27-oldest]setenv}
|
||||
passenv =
|
||||
{[testenv:py27-oldest]passenv}
|
||||
|
||||
[testenv:py27-certbot-oldest]
|
||||
commands =
|
||||
{[base]install_and_test} .[dev]
|
||||
setenv =
|
||||
{[testenv:py27-oldest]setenv}
|
||||
passenv =
|
||||
{[testenv:py27-oldest]passenv}
|
||||
|
||||
[testenv:py27-dns-oldest]
|
||||
commands =
|
||||
{[base]install_and_test} {[base]dns_packages}
|
||||
setenv =
|
||||
{[testenv:py27-oldest]setenv}
|
||||
passenv =
|
||||
{[testenv:py27-oldest]passenv}
|
||||
|
||||
[testenv:py27-nginx-oldest]
|
||||
commands =
|
||||
@@ -98,6 +110,8 @@ commands =
|
||||
python tests/lock_test.py
|
||||
setenv =
|
||||
{[testenv:py27-oldest]setenv}
|
||||
passenv =
|
||||
{[testenv:py27-oldest]passenv}
|
||||
|
||||
[testenv:py27_install]
|
||||
basepython = python2.7
|
||||
@@ -109,6 +123,8 @@ basepython = python2.7
|
||||
commands =
|
||||
{[base]install_packages}
|
||||
./tox.cover.sh
|
||||
passenv =
|
||||
{[testenv]passenv}
|
||||
|
||||
[testenv:lint]
|
||||
basepython = python2.7
|
||||
@@ -120,11 +136,11 @@ commands =
|
||||
pylint --reports=n --rcfile=.pylintrc {[base]source_paths}
|
||||
|
||||
[testenv:mypy]
|
||||
basepython = python3
|
||||
basepython = python3.4
|
||||
commands =
|
||||
{[base]pip_install} .[dev3]
|
||||
{[base]pip_install} mypy
|
||||
{[base]install_packages}
|
||||
mypy {[base]source_paths}
|
||||
mypy --py2 --ignore-missing-imports {[base]source_paths}
|
||||
|
||||
[testenv:apacheconftest]
|
||||
#basepython = python2.7
|
||||
|
||||
Reference in New Issue
Block a user