mirror of
https://github.com/certbot/certbot.git
synced 2026-01-26 07:41:33 +03:00
Merge remote-tracking branch 'upstream/master' into centos_listen
This commit is contained in:
3
.coveragerc
Normal file
3
.coveragerc
Normal file
@@ -0,0 +1,3 @@
|
||||
[report]
|
||||
# show lines missing coverage in output
|
||||
show_missing = True
|
||||
11
.gitattributes
vendored
11
.gitattributes
vendored
@@ -1,7 +1,16 @@
|
||||
* text=auto eol=lf
|
||||
#Default, normalize CRLF into LF in non-binary files
|
||||
# Files identified as binary by Git are not changed
|
||||
* crlf=auto
|
||||
|
||||
# special files
|
||||
*.sh crlf=input
|
||||
*.py crlf=input
|
||||
|
||||
*.bat text eol=crlf
|
||||
|
||||
*.der binary
|
||||
*.gz binary
|
||||
*.jpeg binary
|
||||
*.jpg binary
|
||||
*.png binary
|
||||
*.gz binary
|
||||
|
||||
@@ -21,12 +21,6 @@ persistent=yes
|
||||
# usually to register additional checkers.
|
||||
load-plugins=linter_plugin
|
||||
|
||||
# DEPRECATED
|
||||
include-ids=no
|
||||
|
||||
# DEPRECATED
|
||||
symbols=no
|
||||
|
||||
# Use multiple processes to speed up Pylint.
|
||||
jobs=1
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.8.0.dev0'
|
||||
version = '0.9.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Class of Augeas Configurators."""
|
||||
import logging
|
||||
|
||||
import augeas
|
||||
|
||||
from certbot import errors
|
||||
from certbot import reverter
|
||||
@@ -29,12 +28,9 @@ class AugeasConfigurator(common.Plugin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AugeasConfigurator, self).__init__(*args, **kwargs)
|
||||
|
||||
self.aug = augeas.Augeas(
|
||||
# specify a directory to load our preferred lens from
|
||||
loadpath=constants.AUGEAS_LENS_DIR,
|
||||
# Do not save backup (we do it ourselves), do not load
|
||||
# anything by default
|
||||
flags=(augeas.Augeas.NONE | augeas.Augeas.NO_MODL_AUTOLOAD))
|
||||
# Placeholder for augeas
|
||||
self.aug = None
|
||||
|
||||
self.save_notes = ""
|
||||
|
||||
# See if any temporary changes need to be recovered
|
||||
@@ -42,6 +38,16 @@ class AugeasConfigurator(common.Plugin):
|
||||
# because this will change the underlying configuration and potential
|
||||
# vhosts
|
||||
self.reverter = reverter.Reverter(self.config)
|
||||
|
||||
def init_augeas(self):
|
||||
""" Initialize the actual Augeas instance """
|
||||
import augeas
|
||||
self.aug = augeas.Augeas(
|
||||
# specify a directory to load our preferred lens from
|
||||
loadpath=constants.AUGEAS_LENS_DIR,
|
||||
# Do not save backup (we do it ourselves), do not load
|
||||
# anything by default
|
||||
flags=(augeas.Augeas.NONE | augeas.Augeas.NO_MODL_AUTOLOAD))
|
||||
self.recovery_routine()
|
||||
|
||||
def check_parsing_errors(self, lens):
|
||||
|
||||
@@ -150,6 +150,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
:raises .errors.PluginError: If there is any other error
|
||||
|
||||
"""
|
||||
# Perform the actual Augeas initialization to be able to react
|
||||
try:
|
||||
self.init_augeas()
|
||||
except ImportError:
|
||||
raise errors.NoInstallationError("Problem in Augeas installation")
|
||||
|
||||
# Verify Apache is installed
|
||||
if not util.exe_exists(constants.os_constant("restart_cmd")[0]):
|
||||
raise errors.NoInstallationError
|
||||
|
||||
@@ -55,6 +55,16 @@ class MultipleVhostsTest(util.ApacheTest):
|
||||
self.assertRaises(
|
||||
errors.NoInstallationError, self.config.prepare)
|
||||
|
||||
@mock.patch("certbot_apache.augeas_configurator.AugeasConfigurator.init_augeas")
|
||||
def test_prepare_no_augeas(self, mock_init_augeas):
|
||||
""" Test augeas initialization ImportError """
|
||||
def side_effect_error():
|
||||
""" Side effect error for the test """
|
||||
raise ImportError
|
||||
mock_init_augeas.side_effect = side_effect_error
|
||||
self.assertRaises(
|
||||
errors.NoInstallationError, self.config.prepare)
|
||||
|
||||
@mock.patch("certbot_apache.parser.ApacheParser")
|
||||
@mock.patch("certbot_apache.configurator.util.exe_exists")
|
||||
def test_prepare_version(self, mock_exe_exists, _):
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.8.0.dev0'
|
||||
version = '0.9.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
||||
61
certbot-auto
61
certbot-auto
@@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
|
||||
VENV_NAME="letsencrypt"
|
||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="0.7.0"
|
||||
LE_AUTO_VERSION="0.8.1"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
@@ -172,7 +172,7 @@ BootstrapDebCommon() {
|
||||
# distro version (#346)
|
||||
|
||||
virtualenv=
|
||||
if apt-cache show virtualenv > /dev/null 2>&1; then
|
||||
if apt-cache show virtualenv > /dev/null 2>&1 && ! apt-cache --quiet=0 show virtualenv 2>&1 | grep -q 'No packages found'; then
|
||||
virtualenv="virtualenv"
|
||||
fi
|
||||
|
||||
@@ -458,12 +458,39 @@ BootstrapSmartOS() {
|
||||
pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv'
|
||||
}
|
||||
|
||||
BootstrapMageiaCommon() {
|
||||
if ! $SUDO urpmi --force \
|
||||
python \
|
||||
libpython-devel \
|
||||
python-virtualenv
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! $SUDO urpmi --force \
|
||||
git \
|
||||
gcc \
|
||||
cdialog \
|
||||
python-augeas \
|
||||
libopenssl-devel \
|
||||
libffi-devel \
|
||||
rootcerts
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Install required OS packages:
|
||||
Bootstrap() {
|
||||
if [ -f /etc/debian_version ]; then
|
||||
echo "Bootstrapping dependencies for Debian-based OSes..."
|
||||
BootstrapDebCommon
|
||||
elif [ -f /etc/mageia-release ] ; then
|
||||
# Mageia has both /etc/mageia-release and /etc/redhat-release
|
||||
ExperimentalBootstrap "Mageia" BootstrapMageiaCommon
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
||||
BootstrapRpmCommon
|
||||
@@ -476,7 +503,7 @@ Bootstrap() {
|
||||
BootstrapArchCommon
|
||||
else
|
||||
echo "Please use pacman to install letsencrypt packages:"
|
||||
echo "# pacman -S letsencrypt letsencrypt-apache"
|
||||
echo "# pacman -S certbot certbot-apache"
|
||||
echo
|
||||
echo "If you would like to use the virtualenv way, please run the script again with the"
|
||||
echo "--debug flag."
|
||||
@@ -500,6 +527,7 @@ Bootstrap() {
|
||||
echo "You will need to bootstrap, configure virtualenv, and run pip install manually."
|
||||
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
||||
echo "for more info."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -713,24 +741,21 @@ zope.interface==4.1.3 \
|
||||
mock==1.0.1 \
|
||||
--hash=sha256:b839dd2d9c117c701430c149956918a423a9863b48b09c90e30a6013e7d2f44f \
|
||||
--hash=sha256:8f83080daa249d036cbccfb8ae5cc6ff007b88d6d937521371afabe7b19badbc
|
||||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.7.0 \
|
||||
--hash=sha256:6e61dba343806ad4cb27af84628152abc9e83a0fa24be6065587d2b46f340d7a \
|
||||
--hash=sha256:9f75a1947978402026b741bdee8a18fc5a1cfd539b78e523b7e5f279bf18eeb9
|
||||
certbot==0.7.0 \
|
||||
--hash=sha256:55604e43d231ac226edefed8dc110d792052095c3d75ad0e4a228ae0989fe5fd \
|
||||
--hash=sha256:ad5083d75e16d1ab806802d3a32f34973b6d7adaf083aee87e07a6c1359efe88
|
||||
certbot-apache==0.7.0 \
|
||||
--hash=sha256:5ab5ed9b2af6c7db9495ce1491122798e9d0764e3df8f0843d11d89690bf7f88 \
|
||||
--hash=sha256:1ddbfaf01bcb0b05c0dcc8b2ebd37637f080cf798151e8140c20c9f5fe7bae75
|
||||
letsencrypt==0.7.0 \
|
||||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
letsencrypt-apache==0.7.0 \
|
||||
--hash=sha256:10445980a6afc810325ea22a56e269229999120848f6c0b323b00275696b5c80 \
|
||||
--hash=sha256:3f4656088a18e4efea7cd7eb4965e14e8d901f3b64f4691e79cafd0bb91890f0
|
||||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.8.1 \
|
||||
--hash=sha256:ccd7883772efbf933f91713b8241455993834f3620c8fbd459d9ed5e50bbaaca \
|
||||
--hash=sha256:d3ea4acf280bf6253ad7d641cb0970f230a19805acfed809e7a8ddcf62157d9f
|
||||
certbot==0.8.1 \
|
||||
--hash=sha256:89805d9f70249ae859ec4d7a99c00b4bb7083ca90cd12d4d202b76dfc284f7c5 \
|
||||
--hash=sha256:6ca8df3d310ced6687d38aac17c0fb8c1b2ec7a3bea156a254e4cc2a1c132771
|
||||
certbot-apache==0.8.1 \
|
||||
--hash=sha256:c9e3fdc15e65589c2e39eb0e6b1f61f0c0a1db3c17b00bb337f0ff636cc61cb3 \
|
||||
--hash=sha256:0faf2879884d3b7a58b071902fba37d4b8b58a50e2c3b8ac262c0a74134045ed
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
# An extremely simplified version of `a2enmod` for disabling modules in the
|
||||
# httpd docker image. First argument is the server_root and the second is the
|
||||
# module to be disabled.
|
||||
|
||||
apache_confdir=$1
|
||||
module=$2
|
||||
|
||||
sed -i "/.*"$module".*/d" "$apache_confdir/test.conf"
|
||||
enabled_conf="$apache_confdir/mods-enabled/"$module".conf"
|
||||
if [ -e "$enabled_conf" ]
|
||||
then
|
||||
rm $enabled_conf
|
||||
fi
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
# An extremely simplified version of `a2enmod` for enabling modules in the
|
||||
# httpd docker image. First argument is the Apache ServerRoot which should be
|
||||
# an absolute path. The second is the module to be enabled, such as `ssl`.
|
||||
|
||||
confdir=$1
|
||||
module=$2
|
||||
|
||||
echo "LoadModule ${module}_module " \
|
||||
"/usr/local/apache2/modules/mod_${module}.so" >> "${confdir}/test.conf"
|
||||
availbase="/mods-available/${module}.conf"
|
||||
availconf=$confdir$availbase
|
||||
enabldir="$confdir/mods-enabled"
|
||||
enablconf="$enabldir/${module}.conf"
|
||||
if [ -e $availconf -a -d $enabldir -a ! -e $enablconf ]
|
||||
then
|
||||
ln -s "..$availbase" $enablconf
|
||||
fi
|
||||
@@ -1,63 +0,0 @@
|
||||
"""Proxies ApacheConfigurator for Apache 2.4 tests"""
|
||||
|
||||
import zope.interface
|
||||
|
||||
from certbot_compatibility_test import errors
|
||||
from certbot_compatibility_test import interfaces
|
||||
from certbot_compatibility_test.configurators.apache import common as apache_common
|
||||
|
||||
|
||||
# The docker image doesn't actually have the watchdog module, but unless the
|
||||
# config uses mod_heartbeat or mod_heartmonitor (which aren't installed and
|
||||
# therefore the config won't be loaded), I believe this isn't a problem
|
||||
# http://httpd.apache.org/docs/2.4/mod/mod_watchdog.html
|
||||
STATIC_MODULES = set(["core", "so", "http", "mpm_event", "watchdog"])
|
||||
|
||||
|
||||
SHARED_MODULES = {
|
||||
"log_config", "logio", "version", "unixd", "access_compat", "actions",
|
||||
"alias", "allowmethods", "auth_basic", "auth_digest", "auth_form",
|
||||
"authn_anon", "authn_core", "authn_dbd", "authn_dbm", "authn_file",
|
||||
"authn_socache", "authnz_ldap", "authz_core", "authz_dbd", "authz_dbm",
|
||||
"authz_groupfile", "authz_host", "authz_owner", "authz_user", "autoindex",
|
||||
"buffer", "cache", "cache_disk", "cache_socache", "cgid", "dav", "dav_fs",
|
||||
"dbd", "deflate", "dir", "dumpio", "env", "expires", "ext_filter",
|
||||
"file_cache", "filter", "headers", "include", "info", "lbmethod_bybusyness",
|
||||
"lbmethod_byrequests", "lbmethod_bytraffic", "lbmethod_heartbeat", "ldap",
|
||||
"log_debug", "macro", "mime", "negotiation", "proxy", "proxy_ajp",
|
||||
"proxy_balancer", "proxy_connect", "proxy_express", "proxy_fcgi",
|
||||
"proxy_ftp", "proxy_http", "proxy_scgi", "proxy_wstunnel", "ratelimit",
|
||||
"remoteip", "reqtimeout", "request", "rewrite", "sed", "session",
|
||||
"session_cookie", "session_crypto", "session_dbd", "setenvif",
|
||||
"slotmem_shm", "socache_dbm", "socache_memcache", "socache_shmcb",
|
||||
"speling", "ssl", "status", "substitute", "unique_id", "userdir",
|
||||
"vhost_alias"}
|
||||
|
||||
|
||||
@zope.interface.implementer(interfaces.IConfiguratorProxy)
|
||||
class Proxy(apache_common.Proxy):
|
||||
"""Wraps the ApacheConfigurator for Apache 2.4 tests"""
|
||||
|
||||
def __init__(self, args):
|
||||
"""Initializes the plugin with the given command line args"""
|
||||
super(Proxy, self).__init__(args)
|
||||
# Running init isn't ideal, but the Docker container needs to survive
|
||||
# Apache restarts
|
||||
self.start_docker("bradmw/apache2.4", "init")
|
||||
|
||||
def preprocess_config(self, server_root):
|
||||
"""Prepares the configuration for use in the Docker"""
|
||||
super(Proxy, self).preprocess_config(server_root)
|
||||
if self.version[1] != 4:
|
||||
raise errors.Error("Apache version not 2.4")
|
||||
|
||||
with open(self.test_conf, "a") as f:
|
||||
for module in self.modules:
|
||||
if module not in STATIC_MODULES:
|
||||
if module in SHARED_MODULES:
|
||||
f.write(
|
||||
"LoadModule {0}_module /usr/local/apache2/modules/"
|
||||
"mod_{0}.so\n".format(module))
|
||||
else:
|
||||
raise errors.Error(
|
||||
"Unsupported module {0}".format(module))
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Provides a common base for Apache proxies"""
|
||||
import re
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import mock
|
||||
@@ -9,6 +10,7 @@ import zope.interface
|
||||
from certbot import configuration
|
||||
from certbot import errors as le_errors
|
||||
from certbot_apache import configurator
|
||||
from certbot_apache import constants
|
||||
from certbot_compatibility_test import errors
|
||||
from certbot_compatibility_test import interfaces
|
||||
from certbot_compatibility_test import util
|
||||
@@ -29,58 +31,14 @@ class Proxy(configurators_common.Proxy):
|
||||
super(Proxy, self).__init__(args)
|
||||
self.le_config.apache_le_vhost_ext = "-le-ssl.conf"
|
||||
|
||||
self._setup_mock()
|
||||
|
||||
self.modules = self.server_root = self.test_conf = self.version = None
|
||||
self._apache_configurator = self._all_names = self._test_names = None
|
||||
|
||||
def _setup_mock(self):
|
||||
"""Replaces specific modules with mock.MagicMock"""
|
||||
mock_subprocess = mock.MagicMock()
|
||||
mock_subprocess.check_call = self.check_call
|
||||
mock_subprocess.Popen = self.popen
|
||||
|
||||
mock.patch(
|
||||
"certbot_apache.configurator.subprocess",
|
||||
mock_subprocess).start()
|
||||
mock.patch(
|
||||
"certbot_apache.parser.subprocess",
|
||||
mock_subprocess).start()
|
||||
mock.patch(
|
||||
"certbot.util.subprocess",
|
||||
mock_subprocess).start()
|
||||
mock.patch(
|
||||
"certbot_apache.configurator.util.exe_exists",
|
||||
_is_apache_command).start()
|
||||
|
||||
patch = mock.patch(
|
||||
"certbot_apache.configurator.display_ops.select_vhost")
|
||||
mock_display = patch.start()
|
||||
mock_display.side_effect = le_errors.PluginError(
|
||||
"Unable to determine vhost")
|
||||
|
||||
def check_call(self, command, *args, **kwargs):
|
||||
"""If command is an Apache command, command is executed in the
|
||||
running docker image. Otherwise, subprocess.check_call is used.
|
||||
|
||||
"""
|
||||
if _is_apache_command(command):
|
||||
command = _modify_command(command)
|
||||
return super(Proxy, self).check_call(command, *args, **kwargs)
|
||||
else:
|
||||
return subprocess.check_call(command, *args, **kwargs)
|
||||
|
||||
def popen(self, command, *args, **kwargs):
|
||||
"""If command is an Apache command, command is executed in the
|
||||
running docker image. Otherwise, subprocess.Popen is used.
|
||||
|
||||
"""
|
||||
if _is_apache_command(command):
|
||||
command = _modify_command(command)
|
||||
return super(Proxy, self).popen(command, *args, **kwargs)
|
||||
else:
|
||||
return subprocess.Popen(command, *args, **kwargs)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Wraps the Apache Configurator methods"""
|
||||
method = getattr(self._apache_configurator, name, None)
|
||||
@@ -91,29 +49,20 @@ class Proxy(configurators_common.Proxy):
|
||||
|
||||
def load_config(self):
|
||||
"""Loads the next configuration for the plugin to test"""
|
||||
if hasattr(self.le_config, "apache_init_script"):
|
||||
try:
|
||||
self.check_call([self.le_config.apache_init_script, "stop"])
|
||||
except errors.Error:
|
||||
raise errors.Error(
|
||||
"Failed to stop previous apache config from running")
|
||||
|
||||
config = super(Proxy, self).load_config()
|
||||
self.modules = _get_modules(config)
|
||||
self.version = _get_version(config)
|
||||
self._all_names, self._test_names = _get_names(config)
|
||||
|
||||
server_root = _get_server_root(config)
|
||||
with open(os.path.join(config, "config_file")) as f:
|
||||
config_file = os.path.join(server_root, f.readline().rstrip())
|
||||
self.test_conf = _create_test_conf(server_root, config_file)
|
||||
# with open(os.path.join(config, "config_file")) as f:
|
||||
# config_file = os.path.join(server_root, f.readline().rstrip())
|
||||
shutil.rmtree("/etc/apache2")
|
||||
shutil.copytree(server_root, "/etc/apache2", symlinks=True)
|
||||
|
||||
self.preprocess_config(server_root)
|
||||
self._prepare_configurator(server_root, config_file)
|
||||
self._prepare_configurator()
|
||||
|
||||
try:
|
||||
self.check_call("apachectl -d {0} -f {1} -k start".format(
|
||||
server_root, config_file))
|
||||
subprocess.check_call("apachectl -k start".split())
|
||||
except errors.Error:
|
||||
raise errors.Error(
|
||||
"Apache failed to load {0} before tests started".format(
|
||||
@@ -121,34 +70,13 @@ class Proxy(configurators_common.Proxy):
|
||||
|
||||
return config
|
||||
|
||||
def preprocess_config(self, server_root):
|
||||
# pylint: disable=anomalous-backslash-in-string, no-self-use
|
||||
"""Prepares the configuration for use in the Docker"""
|
||||
|
||||
find = subprocess.Popen(
|
||||
["find", server_root, "-type", "f"],
|
||||
stdout=subprocess.PIPE)
|
||||
subprocess.check_call([
|
||||
"xargs", "sed", "-e", "s/DocumentRoot.*/DocumentRoot "
|
||||
"\/usr\/local\/apache2\/htdocs/I",
|
||||
"-e", "s/SSLPassPhraseDialog.*/SSLPassPhraseDialog builtin/I",
|
||||
"-e", "s/TypesConfig.*/TypesConfig "
|
||||
"\/usr\/local\/apache2\/conf\/mime.types/I",
|
||||
"-e", "s/LoadModule/#LoadModule/I",
|
||||
"-e", "s/SSLCertificateFile.*/SSLCertificateFile "
|
||||
"\/usr\/local\/apache2\/conf\/empty_cert.pem/I",
|
||||
"-e", "s/SSLCertificateKeyFile.*/SSLCertificateKeyFile "
|
||||
"\/usr\/local\/apache2\/conf\/rsa1024_key2.pem/I",
|
||||
"-i"], stdin=find.stdout)
|
||||
|
||||
def _prepare_configurator(self, server_root, config_file):
|
||||
def _prepare_configurator(self):
|
||||
"""Prepares the Apache plugin for testing"""
|
||||
self.le_config.apache_server_root = server_root
|
||||
self.le_config.apache_ctl = "apachectl -d {0} -f {1}".format(
|
||||
server_root, config_file)
|
||||
self.le_config.apache_enmod = "a2enmod.sh {0}".format(server_root)
|
||||
self.le_config.apache_dismod = "a2dismod.sh {0}".format(server_root)
|
||||
self.le_config.apache_init_script = self.le_config.apache_ctl + " -k"
|
||||
for k in constants.CLI_DEFAULTS_DEBIAN.keys():
|
||||
setattr(self.le_config, "apache_" + k, constants.os_constant(k))
|
||||
|
||||
# An alias
|
||||
self.le_config.apache_handle_modules = self.le_config.apache_handle_mods
|
||||
|
||||
self._apache_configurator = configurator.ApacheConfigurator(
|
||||
config=configuration.NamespaceConfig(self.le_config),
|
||||
@@ -183,39 +111,6 @@ class Proxy(configurators_common.Proxy):
|
||||
domain, cert_path, key_path, chain_path, fullchain_path)
|
||||
|
||||
|
||||
def _is_apache_command(command):
|
||||
"""Returns true if command is an Apache command"""
|
||||
if isinstance(command, list):
|
||||
command = command[0]
|
||||
|
||||
for apache_command in APACHE_COMMANDS:
|
||||
if command.startswith(apache_command):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _modify_command(command):
|
||||
"""Modifies command so configtest works inside the docker image"""
|
||||
if isinstance(command, list):
|
||||
for i in xrange(len(command)):
|
||||
if command[i] == "configtest":
|
||||
command[i] = "-t"
|
||||
else:
|
||||
command = command.replace("configtest", "-t")
|
||||
|
||||
return command
|
||||
|
||||
|
||||
def _create_test_conf(server_root, apache_config):
|
||||
"""Creates a test config file and adds it to the Apache config"""
|
||||
test_conf = os.path.join(server_root, "test.conf")
|
||||
open(test_conf, "w").close()
|
||||
subprocess.check_call(
|
||||
["sed", "-i", "1iInclude test.conf", apache_config])
|
||||
return test_conf
|
||||
|
||||
|
||||
def _get_server_root(config):
|
||||
"""Returns the server root directory in config"""
|
||||
subdirs = [
|
||||
@@ -223,7 +118,7 @@ def _get_server_root(config):
|
||||
if os.path.isdir(os.path.join(config, name))]
|
||||
|
||||
if len(subdirs) != 1:
|
||||
errors.Error("Malformed configuration directiory {0}".format(config))
|
||||
errors.Error("Malformed configuration directory {0}".format(config))
|
||||
|
||||
return os.path.join(config, subdirs[0].rstrip())
|
||||
|
||||
@@ -251,34 +146,3 @@ def _get_names(config):
|
||||
words[1].find(".") != -1):
|
||||
all_names.add(words[1])
|
||||
return all_names, non_ip_names
|
||||
|
||||
|
||||
def _get_modules(config):
|
||||
"""Returns the list of modules found in module_list"""
|
||||
modules = []
|
||||
with open(os.path.join(config, "modules")) as f:
|
||||
for line in f:
|
||||
# Modules list is indented, everything else is headers/footers
|
||||
if line[0].isspace():
|
||||
words = line.split()
|
||||
# Modules redundantly end in "_module" which we can discard
|
||||
modules.append(words[0][:-7])
|
||||
|
||||
return modules
|
||||
|
||||
|
||||
def _get_version(config):
|
||||
"""Return version of Apache Server.
|
||||
|
||||
Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7)). Code taken from
|
||||
the Apache plugin.
|
||||
|
||||
"""
|
||||
with open(os.path.join(config, "version")) as f:
|
||||
# Should be on first line of input
|
||||
matches = APACHE_VERSION_REGEX.findall(f.readline())
|
||||
|
||||
if len(matches) != 1:
|
||||
raise errors.Error("Unable to find Apache version")
|
||||
|
||||
return tuple([int(i) for i in matches[0].split(".")])
|
||||
|
||||
@@ -4,10 +4,7 @@ import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import docker
|
||||
|
||||
from certbot import constants
|
||||
from certbot_compatibility_test import errors
|
||||
from certbot_compatibility_test import util
|
||||
|
||||
|
||||
@@ -18,20 +15,9 @@ class Proxy(object):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""A common base for compatibility test configurators"""
|
||||
|
||||
_NOT_ADDED_ARGS = True
|
||||
|
||||
@classmethod
|
||||
def add_parser_arguments(cls, parser):
|
||||
"""Adds command line arguments needed by the plugin"""
|
||||
if Proxy._NOT_ADDED_ARGS:
|
||||
group = parser.add_argument_group("docker")
|
||||
group.add_argument(
|
||||
"--docker-url", default="unix://var/run/docker.sock",
|
||||
help="URL of the docker server")
|
||||
group.add_argument(
|
||||
"--no-remove", action="store_true",
|
||||
help="do not delete container on program exit")
|
||||
Proxy._NOT_ADDED_ARGS = False
|
||||
|
||||
def __init__(self, args):
|
||||
"""Initializes the plugin with the given command line args"""
|
||||
@@ -43,10 +29,8 @@ class Proxy(object):
|
||||
for config in os.listdir(config_dir)]
|
||||
|
||||
self.args = args
|
||||
self._docker_client = docker.Client(
|
||||
base_url=self.args.docker_url, version="auto")
|
||||
self.http_port, self.https_port = util.get_two_free_ports()
|
||||
self._container_id = None
|
||||
self.http_port = 80
|
||||
self.https_port = 443
|
||||
|
||||
def has_more_configs(self):
|
||||
"""Returns true if there are more configs to test"""
|
||||
@@ -54,9 +38,6 @@ class Proxy(object):
|
||||
|
||||
def cleanup_from_tests(self):
|
||||
"""Performs any necessary cleanup from running plugin tests"""
|
||||
self._docker_client.stop(self._container_id, 0)
|
||||
if not self.args.no_remove:
|
||||
self._docker_client.remove_container(self._container_id)
|
||||
|
||||
def load_config(self):
|
||||
"""Returns the next config directory to be tested"""
|
||||
@@ -65,67 +46,6 @@ class Proxy(object):
|
||||
os.makedirs(backup)
|
||||
return self._configs.pop()
|
||||
|
||||
def start_docker(self, image_name, command):
|
||||
"""Creates and runs a Docker container with the specified image"""
|
||||
logger.warning("Pulling Docker image. This may take a minute.")
|
||||
for line in self._docker_client.pull(image_name, stream=True):
|
||||
logger.debug(line)
|
||||
|
||||
host_config = docker.utils.create_host_config(
|
||||
binds={self._temp_dir: {"bind": self._temp_dir, "mode": "rw"}},
|
||||
port_bindings={
|
||||
80: ("127.0.0.1", self.http_port),
|
||||
443: ("127.0.0.1", self.https_port)},)
|
||||
container = self._docker_client.create_container(
|
||||
image_name, command, ports=[80, 443], volumes=self._temp_dir,
|
||||
host_config=host_config)
|
||||
if container["Warnings"]:
|
||||
logger.warning(container["Warnings"])
|
||||
self._container_id = container["Id"]
|
||||
self._docker_client.start(self._container_id)
|
||||
|
||||
def check_call(self, command, *args, **kwargs):
|
||||
# pylint: disable=unused-argument
|
||||
"""Simulates a call to check_call but executes the command in the
|
||||
running docker image
|
||||
|
||||
"""
|
||||
if self.popen(command).returncode:
|
||||
raise errors.Error(
|
||||
"{0} exited with a nonzero value".format(command))
|
||||
|
||||
def popen(self, command, *args, **kwargs):
|
||||
# pylint: disable=unused-argument
|
||||
"""Simulates a call to Popen but executes the command in the
|
||||
running docker image
|
||||
|
||||
"""
|
||||
class SimplePopen(object):
|
||||
# pylint: disable=too-few-public-methods
|
||||
"""Simplified Popen object"""
|
||||
def __init__(self, returncode, output):
|
||||
self.returncode = returncode
|
||||
self._stdout = output
|
||||
self._stderr = output
|
||||
|
||||
def communicate(self):
|
||||
"""Returns stdout and stderr"""
|
||||
return self._stdout, self._stderr
|
||||
|
||||
if isinstance(command, list):
|
||||
command = " ".join(command)
|
||||
|
||||
returncode, output = self.execute_in_docker(command)
|
||||
return SimplePopen(returncode, output)
|
||||
|
||||
def execute_in_docker(self, command):
|
||||
"""Executes command inside the running docker image"""
|
||||
logger.debug("Executing '%s'", command)
|
||||
exec_id = self._docker_client.exec_create(self._container_id, command)
|
||||
output = self._docker_client.exec_start(exec_id)
|
||||
returncode = self._docker_client.exec_inspect(exec_id)["ExitCode"]
|
||||
return returncode, output
|
||||
|
||||
def copy_certs_and_keys(self, cert_path, key_path, chain_path=None):
|
||||
"""Copies certs and keys into the temporary directory"""
|
||||
cert_and_key_dir = os.path.join(self._temp_dir, "certs_and_keys")
|
||||
|
||||
@@ -7,6 +7,7 @@ import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
import sys
|
||||
|
||||
import OpenSSL
|
||||
|
||||
@@ -21,17 +22,17 @@ from certbot_compatibility_test import errors
|
||||
from certbot_compatibility_test import util
|
||||
from certbot_compatibility_test import validator
|
||||
|
||||
from certbot_compatibility_test.configurators.apache import apache24
|
||||
from certbot_compatibility_test.configurators.apache import common
|
||||
|
||||
|
||||
DESCRIPTION = """
|
||||
Tests Certbot plugins against different server configuratons. It is
|
||||
assumed that Docker is already installed. If no test types is specified, all
|
||||
Tests Certbot plugins against different server configurations. It is
|
||||
assumed that Docker is already installed. If no test type is specified, all
|
||||
tests that the plugin supports are performed.
|
||||
|
||||
"""
|
||||
|
||||
PLUGINS = {"apache": apache24.Proxy}
|
||||
PLUGINS = {"apache": common.Proxy}
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -61,8 +62,8 @@ def test_authenticator(plugin, config, temp_dir):
|
||||
"Plugin failed to complete %s for %s in %s",
|
||||
type(achalls[i]), achalls[i].domain, config)
|
||||
success = False
|
||||
elif isinstance(responses[i], challenges.TLSSNI01):
|
||||
verify = functools.partial(responses[i].simple_verify, achalls[i],
|
||||
elif isinstance(responses[i], challenges.TLSSNI01Response):
|
||||
verify = functools.partial(responses[i].simple_verify, achalls[i].chall,
|
||||
achalls[i].domain,
|
||||
util.JWK.public_key(),
|
||||
host="127.0.0.1",
|
||||
@@ -142,7 +143,8 @@ def test_deploy_cert(plugin, temp_dir, domains):
|
||||
|
||||
for domain in domains:
|
||||
try:
|
||||
plugin.deploy_cert(domain, cert_path, util.KEY_PATH)
|
||||
plugin.deploy_cert(domain, cert_path, util.KEY_PATH, cert_path)
|
||||
plugin.save() # Needed by the Apache plugin
|
||||
except le_errors.Error as error:
|
||||
logger.error("Plugin failed to deploy ceritificate for %s:", domain)
|
||||
logger.exception(error)
|
||||
@@ -177,6 +179,7 @@ def test_enhancements(plugin, domains):
|
||||
for domain in domains:
|
||||
try:
|
||||
plugin.enhance(domain, "redirect")
|
||||
plugin.save() # Needed by the Apache plugin
|
||||
except le_errors.PluginError as error:
|
||||
# Don't immediately fail because a redirect may already be enabled
|
||||
logger.warning("Plugin failed to enable redirect for %s:", domain)
|
||||
@@ -341,7 +344,7 @@ def main():
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
plugin = PLUGINS[args.plugin](args)
|
||||
try:
|
||||
plugin.execute_in_docker("mkdir -p /var/log/apache2")
|
||||
overall_success = True
|
||||
while plugin.has_more_configs():
|
||||
success = True
|
||||
|
||||
@@ -360,10 +363,18 @@ def main():
|
||||
if success:
|
||||
logger.info("All tests on %s succeeded", config)
|
||||
else:
|
||||
overall_success = False
|
||||
logger.error("Tests on %s failed", config)
|
||||
finally:
|
||||
plugin.cleanup_from_tests()
|
||||
|
||||
if overall_success:
|
||||
logger.warn("All compatibility tests succeeded")
|
||||
sys.exit(0)
|
||||
else:
|
||||
logger.warn("One or more compatibility tests failed")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Binary file not shown.
@@ -1,11 +1,9 @@
|
||||
"""Utility functions for Certbot plugin tests."""
|
||||
import argparse
|
||||
import copy
|
||||
import contextlib
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import tarfile
|
||||
|
||||
from acme import jose
|
||||
@@ -52,13 +50,3 @@ def extract_configs(configs, parent_dir):
|
||||
raise errors.Error("Unknown configurations file type")
|
||||
|
||||
return config_dir
|
||||
|
||||
|
||||
def get_two_free_ports():
|
||||
"""Returns two free ports to use for the tests"""
|
||||
with contextlib.closing(socket.socket()) as sock1:
|
||||
with contextlib.closing(socket.socket()) as sock2:
|
||||
sock1.bind(("", 0))
|
||||
sock2.bind(("", 0))
|
||||
|
||||
return sock1.getsockname()[1], sock2.getsockname()[1]
|
||||
|
||||
@@ -4,12 +4,11 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.8.0.dev0'
|
||||
version = '0.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'certbot=={0}'.format(version),
|
||||
'certbot-apache=={0}'.format(version),
|
||||
'docker-py',
|
||||
'certbot',
|
||||
'certbot-apache',
|
||||
'requests',
|
||||
'zope.interface',
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.8.0.dev0'
|
||||
version = '0.9.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Certbot client."""
|
||||
|
||||
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
|
||||
__version__ = '0.8.0.dev0'
|
||||
__version__ = '0.9.0.dev0'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Certbot command line argument & config processing."""
|
||||
from __future__ import print_function
|
||||
import argparse
|
||||
import copy
|
||||
import glob
|
||||
import logging
|
||||
import logging.handlers
|
||||
@@ -211,6 +212,35 @@ def set_by_cli(var):
|
||||
set_by_cli.detector = None
|
||||
|
||||
|
||||
def has_default_value(option, value):
|
||||
"""Does option have the default value?
|
||||
|
||||
If the default value of option is not known, False is returned.
|
||||
|
||||
:param str option: configuration variable being considered
|
||||
:param value: value of the configuration variable named option
|
||||
|
||||
:returns: True if option has the default value, otherwise, False
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
return (option in helpful_parser.defaults and
|
||||
helpful_parser.defaults[option] == value)
|
||||
|
||||
|
||||
def option_was_set(option, value):
|
||||
"""Was option set by the user or does it differ from the default?
|
||||
|
||||
:param str option: configuration variable being considered
|
||||
:param value: value of the configuration variable named option
|
||||
|
||||
:returns: True if the option was set, otherwise, False
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
return set_by_cli(option) or not has_default_value(option, value)
|
||||
|
||||
|
||||
def argparse_type(variable):
|
||||
"Return our argparse type function for a config variable (default: str)"
|
||||
# pylint: disable=protected-access
|
||||
@@ -320,6 +350,7 @@ class HelpfulArgumentParser(object):
|
||||
sys.exit(0)
|
||||
self.visible_topics = self.determine_help_topics(self.help_arg)
|
||||
self.groups = {} # elements are added by .add_group()
|
||||
self.defaults = {} # elements are added by .parse_args()
|
||||
|
||||
def parse_args(self):
|
||||
"""Parses command line arguments and returns the result.
|
||||
@@ -335,6 +366,9 @@ class HelpfulArgumentParser(object):
|
||||
if self.detect_defaults:
|
||||
return parsed_args
|
||||
|
||||
self.defaults = dict((key, copy.deepcopy(self.parser.get_default(key)))
|
||||
for key in vars(parsed_args))
|
||||
|
||||
# Do any post-parsing homework here
|
||||
|
||||
if self.verb == "renew" and not parsed_args.dialog_mode:
|
||||
@@ -359,7 +393,8 @@ class HelpfulArgumentParser(object):
|
||||
" {0} conflicts with dialog_mode").format(arg)
|
||||
)
|
||||
|
||||
hooks.validate_hooks(parsed_args)
|
||||
if parsed_args.validate_hooks:
|
||||
hooks.validate_hooks(parsed_args)
|
||||
|
||||
return parsed_args
|
||||
|
||||
@@ -800,6 +835,14 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
||||
"For this command, the shell variable $RENEWED_LINEAGE will point to the"
|
||||
"config live subdirectory containing the new certs and keys; the shell variable "
|
||||
"$RENEWED_DOMAINS will contain a space-delimited list of renewed cert domains")
|
||||
helpful.add(
|
||||
"renew", "--disable-hook-validation",
|
||||
action='store_false', dest='validate_hooks', default=True,
|
||||
help="Ordinarily the commands specified for --pre-hook/--post-hook/--renew-hook"
|
||||
" will be checked for validity, to see if the programs being run are in the $PATH,"
|
||||
" so that mistakes can be caught early, even when the hooks aren't being run just yet."
|
||||
" The validation is rather simplistic and fails if you use more advanced"
|
||||
" shell constructs, so you can use this switch to disable it.")
|
||||
|
||||
helpful.add_deprecated_argument("--agree-dev-preview", 0)
|
||||
|
||||
|
||||
@@ -296,6 +296,32 @@ def get_sans_from_csr(csr, typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
csr, OpenSSL.crypto.load_certificate_request, typ)
|
||||
|
||||
|
||||
def _get_names_from_cert_or_req(cert_or_req, load_func, typ):
|
||||
loaded_cert_or_req = _load_cert_or_req(cert_or_req, load_func, typ)
|
||||
common_name = loaded_cert_or_req.get_subject().CN
|
||||
# pylint: disable=protected-access
|
||||
sans = acme_crypto_util._pyopenssl_cert_or_req_san(loaded_cert_or_req)
|
||||
|
||||
if common_name is None:
|
||||
return sans
|
||||
else:
|
||||
return [common_name] + [d for d in sans if d != common_name]
|
||||
|
||||
|
||||
def get_names_from_cert(csr, typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
"""Get a list of domains from a cert, including the CN if it is set.
|
||||
|
||||
:param str cert: Certificate (encoded).
|
||||
:param typ: `OpenSSL.crypto.FILETYPE_PEM` or `OpenSSL.crypto.FILETYPE_ASN1`
|
||||
|
||||
:returns: A list of domain names.
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
return _get_names_from_cert_or_req(
|
||||
csr, OpenSSL.crypto.load_certificate, typ)
|
||||
|
||||
|
||||
def get_names_from_csr(csr, typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
"""Get a list of domains from a CSR, including the CN if it is set.
|
||||
|
||||
@@ -306,13 +332,8 @@ def get_names_from_csr(csr, typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
loaded_csr = _load_cert_or_req(
|
||||
return _get_names_from_cert_or_req(
|
||||
csr, OpenSSL.crypto.load_certificate_request, typ)
|
||||
# Use a set to avoid duplication with CN and Subject Alt Names
|
||||
domains = set(d for d in (loaded_csr.get_subject().CN,) if d is not None)
|
||||
# pylint: disable=protected-access
|
||||
domains.update(acme_crypto_util._pyopenssl_cert_or_req_san(loaded_csr))
|
||||
return list(domains)
|
||||
|
||||
|
||||
def dump_pyopenssl_chain(chain, filetype=OpenSSL.crypto.FILETYPE_PEM):
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""Certbot main entry point."""
|
||||
from __future__ import print_function
|
||||
import atexit
|
||||
import dialog
|
||||
import errno
|
||||
import functools
|
||||
import logging.handlers
|
||||
import os
|
||||
@@ -588,8 +590,16 @@ def renew(config, unused_plugins):
|
||||
def setup_log_file_handler(config, logfile, fmt):
|
||||
"""Setup file debug logging."""
|
||||
log_file_path = os.path.join(config.logs_dir, logfile)
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
log_file_path, maxBytes=2 ** 20, backupCount=10)
|
||||
try:
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
log_file_path, maxBytes=2 ** 20, backupCount=10)
|
||||
except IOError as e:
|
||||
if e.errno == errno.EACCES:
|
||||
msg = ("Access denied writing to {0}. To run as non-root, set " +
|
||||
"--logs-dir, --config-dir, --work-dir to writable paths.")
|
||||
raise errors.Error(msg.format(log_file_path))
|
||||
else:
|
||||
raise
|
||||
# rotate on each invocation, rollover only possible when maxBytes
|
||||
# is nonzero and backupCount is nonzero, so we set maxBytes as big
|
||||
# as possible not to overrun in single CLI invocation (1MB).
|
||||
@@ -665,7 +675,10 @@ def _handle_exception(exc_type, exc_value, trace, config):
|
||||
# Here we're passing a client or ACME error out to the client at the shell
|
||||
# Tell the user a bit about what happened, without overwhelming
|
||||
# them with a full traceback
|
||||
err = traceback.format_exception_only(exc_type, exc_value)[0]
|
||||
if issubclass(exc_type, dialog.error):
|
||||
err = exc_value.complete_message()
|
||||
else:
|
||||
err = traceback.format_exception_only(exc_type, exc_value)[0]
|
||||
# Typical error from the ACME module:
|
||||
# acme.messages.Error: urn:acme:error:malformed :: The request message was
|
||||
# malformed :: Error creating new registration :: Validation of contact
|
||||
|
||||
@@ -60,7 +60,8 @@ def _reconstitute(config, full_path):
|
||||
try:
|
||||
renewal_candidate = storage.RenewableCert(
|
||||
full_path, configuration.RenewerConfiguration(config))
|
||||
except (errors.CertStorageError, IOError):
|
||||
except (errors.CertStorageError, IOError) as exc:
|
||||
logger.warning(exc)
|
||||
logger.warning("Renewal configuration file %s is broken. Skipping.", full_path)
|
||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||
return None
|
||||
|
||||
@@ -7,8 +7,10 @@ import re
|
||||
import configobj
|
||||
import parsedatetime
|
||||
import pytz
|
||||
import six
|
||||
|
||||
import certbot
|
||||
from certbot import cli
|
||||
from certbot import constants
|
||||
from certbot import crypto_util
|
||||
from certbot import errors
|
||||
@@ -158,36 +160,13 @@ def relevant_values(all_values):
|
||||
:param dict all_values: The original values.
|
||||
|
||||
:returns: A new dictionary containing items that can be used in renewal.
|
||||
:rtype dict:"""
|
||||
:rtype dict:
|
||||
|
||||
from certbot import cli
|
||||
|
||||
def _is_cli_default(option, value):
|
||||
# Look through the CLI parser defaults and see if this option is
|
||||
# both present and equal to the specified value. If not, return
|
||||
# False.
|
||||
# pylint: disable=protected-access
|
||||
for x in cli.helpful_parser.parser._actions:
|
||||
if x.dest == option:
|
||||
if x.default == value:
|
||||
return True
|
||||
else:
|
||||
break
|
||||
return False
|
||||
|
||||
values = dict()
|
||||
for option, value in all_values.iteritems():
|
||||
# Try to find reasons to store this item in the
|
||||
# renewal config. It can be stored if it is relevant and
|
||||
# (it is set_by_cli() or flag_default() is different
|
||||
# from the value or flag_default() doesn't exist).
|
||||
if _relevant(option):
|
||||
if (cli.set_by_cli(option)
|
||||
or not _is_cli_default(option, value)):
|
||||
# or option not in constants.CLI_DEFAULTS
|
||||
# or constants.CLI_DEFAULTS[option] != value):
|
||||
values[option] = value
|
||||
return values
|
||||
"""
|
||||
return dict(
|
||||
(option, value)
|
||||
for option, value in six.iteritems(all_values)
|
||||
if _relevant(option) and cli.option_was_set(option, value))
|
||||
|
||||
|
||||
class RenewableCert(object): # pylint: disable=too-many-instance-attributes
|
||||
@@ -616,7 +595,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
|
||||
if target is None:
|
||||
raise errors.CertStorageError("could not find cert file")
|
||||
with open(target) as f:
|
||||
return crypto_util.get_sans_from_cert(f.read())
|
||||
return crypto_util.get_names_from_cert(f.read())
|
||||
|
||||
def autodeployment_is_enabled(self):
|
||||
"""Is automatic deployment enabled for this cert?
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import dialog
|
||||
import functools
|
||||
import itertools
|
||||
import os
|
||||
@@ -341,11 +342,11 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
# FQDN
|
||||
self.assertRaises(errors.ConfigurationError,
|
||||
self._call,
|
||||
['-d', 'comma,gotwrong.tld'])
|
||||
['-d', 'a' * 64])
|
||||
# FQDN 2
|
||||
self.assertRaises(errors.ConfigurationError,
|
||||
self._call,
|
||||
['-d', 'illegal.character=.tld'])
|
||||
['-d', (('a' * 50) + '.') * 10])
|
||||
# Wildcard
|
||||
self.assertRaises(errors.ConfigurationError,
|
||||
self._call,
|
||||
@@ -447,6 +448,19 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
short_args += '--server example.com'.split()
|
||||
self._check_server_conflict_message(short_args, '--staging')
|
||||
|
||||
def test_option_was_set(self):
|
||||
key_size_option = 'rsa_key_size'
|
||||
key_size_value = cli.flag_default(key_size_option)
|
||||
self._get_argument_parser()(
|
||||
'--rsa-key-size {0}'.format(key_size_value).split())
|
||||
|
||||
self.assertTrue(cli.option_was_set(key_size_option, key_size_value))
|
||||
self.assertTrue(cli.option_was_set('no_verify_ssl', True))
|
||||
|
||||
config_dir_option = 'config_dir'
|
||||
self.assertFalse(cli.option_was_set(
|
||||
config_dir_option, cli.flag_default(config_dir_option)))
|
||||
|
||||
def _assert_dry_run_flag_worked(self, namespace, existing_account):
|
||||
self.assertTrue(namespace.dry_run)
|
||||
self.assertTrue(namespace.break_my_certs)
|
||||
@@ -651,6 +665,18 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
out = stdout.getvalue()
|
||||
self.assertEqual("", out)
|
||||
|
||||
def test_renew_hook_validation(self):
|
||||
self._make_test_renewal_conf('sample-renewal.conf')
|
||||
args = ["renew", "--dry-run", "--post-hook=no-such-command"]
|
||||
self._test_renewal_common(True, [], args=args, should_renew=False,
|
||||
error_expected=True)
|
||||
|
||||
def test_renew_no_hook_validation(self):
|
||||
self._make_test_renewal_conf('sample-renewal.conf')
|
||||
args = ["renew", "--dry-run", "--post-hook=no-such-command",
|
||||
"--disable-hook-validation"]
|
||||
self._test_renewal_common(True, [], args=args, should_renew=True,
|
||||
error_expected=False)
|
||||
|
||||
@mock.patch("certbot.cli.set_by_cli")
|
||||
def test_ancient_webroot_renewal_conf(self, mock_set_by_cli):
|
||||
@@ -897,6 +923,13 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
mock_sys.exit.assert_called_with(''.join(
|
||||
traceback.format_exception_only(KeyboardInterrupt, interrupt)))
|
||||
|
||||
# Test dialog errors
|
||||
exception = dialog.error(message="test message")
|
||||
main._handle_exception(
|
||||
dialog.DialogError, exc_value=exception, trace=None, config=None)
|
||||
error_msg = mock_sys.exit.call_args_list[-1][0][0]
|
||||
self.assertTrue("test message" in error_msg)
|
||||
|
||||
def test_read_file(self):
|
||||
rel_test_path = os.path.relpath(os.path.join(self.tmp_dir, 'foo'))
|
||||
self.assertRaises(
|
||||
|
||||
@@ -273,6 +273,32 @@ class GetSANsFromCSRTest(unittest.TestCase):
|
||||
[], self._call(test_util.load_vector('csr-nosans.pem')))
|
||||
|
||||
|
||||
class GetNamesFromCertTest(unittest.TestCase):
|
||||
"""Tests for certbot.crypto_util.get_names_from_cert."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from certbot.crypto_util import get_names_from_cert
|
||||
return get_names_from_cert(*args, **kwargs)
|
||||
|
||||
def test_single(self):
|
||||
self.assertEqual(
|
||||
['example.com'],
|
||||
self._call(test_util.load_vector('cert.pem')))
|
||||
|
||||
def test_san(self):
|
||||
self.assertEqual(
|
||||
['example.com', 'www.example.com'],
|
||||
self._call(test_util.load_vector('cert-san.pem')))
|
||||
|
||||
def test_common_name_sans_order(self):
|
||||
# Tests that the common name comes first
|
||||
# followed by the SANS in alphabetical order
|
||||
self.assertEqual(
|
||||
['example.com'] + ['{0}.example.com'.format(c) for c in 'abcd'],
|
||||
self._call(test_util.load_vector('cert-5sans.pem')))
|
||||
|
||||
|
||||
class GetNamesFromCSRTest(unittest.TestCase):
|
||||
"""Tests for certbot.crypto_util.get_names_from_csr."""
|
||||
@classmethod
|
||||
|
||||
@@ -248,9 +248,9 @@ class ChooseNamesTest(unittest.TestCase):
|
||||
def test_get_valid_domains(self):
|
||||
from certbot.display.ops import get_valid_domains
|
||||
all_valid = ["example.com", "second.example.com",
|
||||
"also.example.com"]
|
||||
all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN",
|
||||
"uniçodé.com"]
|
||||
"also.example.com", "under_score.example.com",
|
||||
"justtld"]
|
||||
all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "uniçodé.com"]
|
||||
two_valid = ["example.com", "xn--ls8h.tld", "also.example.com"]
|
||||
self.assertEqual(get_valid_domains(all_valid), all_valid)
|
||||
self.assertEqual(get_valid_domains(all_invalid), [])
|
||||
@@ -276,19 +276,18 @@ class ChooseNamesTest(unittest.TestCase):
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
"xn--ls8h.tld")
|
||||
self.assertEqual(_choose_names_manually(), [])
|
||||
# non-FQDN and no retry
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
"notFQDN")
|
||||
self.assertEqual(_choose_names_manually(), [])
|
||||
# Two valid domains
|
||||
# Valid domains
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
("example.com,"
|
||||
"under_score.example.com,"
|
||||
"justtld,"
|
||||
"valid.example.com"))
|
||||
self.assertEqual(_choose_names_manually(),
|
||||
["example.com", "valid.example.com"])
|
||||
["example.com", "under_score.example.com",
|
||||
"justtld", "valid.example.com"])
|
||||
# Three iterations
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
"notFQDN")
|
||||
"uniçodé.com")
|
||||
yn = mock.MagicMock()
|
||||
yn.side_effect = [True, True, False]
|
||||
mock_util().yesno = yn
|
||||
|
||||
@@ -11,6 +11,7 @@ import mock
|
||||
import pytz
|
||||
|
||||
import certbot
|
||||
from certbot import cli
|
||||
from certbot import configuration
|
||||
from certbot import errors
|
||||
from certbot.storage import ALL_FOUR
|
||||
@@ -84,18 +85,20 @@ class BaseRenewableCertTest(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tempdir)
|
||||
|
||||
def _write_out_kind(self, kind, ver, value=None):
|
||||
link = getattr(self.test_rc, kind)
|
||||
if os.path.lexists(link):
|
||||
os.unlink(link)
|
||||
os.symlink(os.path.join(os.path.pardir, os.path.pardir, "archive",
|
||||
"example.org", "{0}{1}.pem".format(kind, ver)),
|
||||
link)
|
||||
with open(link, "w") as f:
|
||||
f.write(kind if value is None else value)
|
||||
|
||||
def _write_out_ex_kinds(self):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}12.pem".format(kind)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}11.pem".format(kind)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, 12)
|
||||
self._write_out_kind(kind, 11)
|
||||
|
||||
|
||||
class RenewableCertTests(BaseRenewableCertTest):
|
||||
@@ -204,10 +207,7 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
|
||||
def test_current_target(self):
|
||||
# Relative path logic
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert17.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
self._write_out_kind("cert", 17)
|
||||
self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
|
||||
os.path.join(self.tempdir, "archive",
|
||||
"example.org",
|
||||
@@ -225,12 +225,8 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
|
||||
def test_current_version(self):
|
||||
for ver in (1, 5, 10, 20):
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert{0}.pem".format(ver)),
|
||||
self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
os.unlink(self.test_rc.cert)
|
||||
self._write_out_kind("cert", ver)
|
||||
os.unlink(self.test_rc.cert)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert10.pem"), self.test_rc.cert)
|
||||
self.assertEqual(self.test_rc.current_version("cert"), 10)
|
||||
@@ -241,61 +237,30 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
def test_latest_and_next_versions(self):
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, ver)
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
self.assertEqual(self.test_rc.next_free_version(), 6)
|
||||
# Having one kind of file of a later version doesn't change the
|
||||
# result
|
||||
os.unlink(self.test_rc.privkey)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"privkey7.pem"), self.test_rc.privkey)
|
||||
with open(self.test_rc.privkey, "w") as f:
|
||||
f.write("privkey")
|
||||
self._write_out_kind("privkey", 7)
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
# ... although it does change the next free version
|
||||
self.assertEqual(self.test_rc.next_free_version(), 8)
|
||||
# Nor does having three out of four change the result
|
||||
os.unlink(self.test_rc.cert)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert7.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
os.unlink(self.test_rc.fullchain)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"fullchain7.pem"), self.test_rc.fullchain)
|
||||
with open(self.test_rc.fullchain, "w") as f:
|
||||
f.write("fullchain")
|
||||
self._write_out_kind("cert", 7)
|
||||
self._write_out_kind("fullchain", 7)
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
# If we have everything from a much later version, it does change
|
||||
# the result
|
||||
ver = 17
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, 17)
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 17)
|
||||
self.assertEqual(self.test_rc.next_free_version(), 18)
|
||||
|
||||
def test_update_link_to(self):
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, ver)
|
||||
self.assertEqual(ver, self.test_rc.current_version(kind))
|
||||
# pylint: disable=protected-access
|
||||
self.test_rc._update_link_to("cert", 3)
|
||||
@@ -312,10 +277,7 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
"chain3000.pem")
|
||||
|
||||
def test_version(self):
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert12.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
self._write_out_kind("cert", 12)
|
||||
# TODO: We should probably test that the directory is still the
|
||||
# same, but it's tricky because we can get an absolute
|
||||
# path out when we put a relative path in.
|
||||
@@ -325,13 +287,7 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
def test_update_all_links_to_success(self):
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, ver)
|
||||
self.assertEqual(ver, self.test_rc.current_version(kind))
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
for ver in xrange(1, 6):
|
||||
@@ -376,13 +332,7 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
def test_has_pending_deployment(self):
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, ver)
|
||||
self.assertEqual(ver, self.test_rc.current_version(kind))
|
||||
for ver in xrange(1, 6):
|
||||
self.test_rc.update_all_links_to(ver)
|
||||
@@ -395,24 +345,22 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
|
||||
def test_names(self):
|
||||
# Trying the current version
|
||||
test_cert = test_util.load_vector("cert-san.pem")
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert12.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write(test_cert)
|
||||
self._write_out_kind("cert", 12, test_util.load_vector("cert-san.pem"))
|
||||
self.assertEqual(self.test_rc.names(),
|
||||
["example.com", "www.example.com"])
|
||||
|
||||
# Trying a non-current version
|
||||
test_cert = test_util.load_vector("cert.pem")
|
||||
os.unlink(self.test_rc.cert)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert15.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write(test_cert)
|
||||
self._write_out_kind("cert", 15, test_util.load_vector("cert.pem"))
|
||||
self.assertEqual(self.test_rc.names(12),
|
||||
["example.com", "www.example.com"])
|
||||
|
||||
# Testing common name is listed first
|
||||
self._write_out_kind(
|
||||
"cert", 12, test_util.load_vector("cert-5sans.pem"))
|
||||
self.assertEqual(
|
||||
self.test_rc.names(12),
|
||||
["example.com"] + ["{0}.example.com".format(c) for c in "abcd"])
|
||||
|
||||
# Trying missing cert
|
||||
os.unlink(self.test_rc.cert)
|
||||
self.assertRaises(errors.CertStorageError, self.test_rc.names)
|
||||
@@ -480,13 +428,7 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
# No pending deployment
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, ver)
|
||||
self.assertFalse(self.test_rc.should_autodeploy())
|
||||
|
||||
def test_autorenewal_is_enabled(self):
|
||||
@@ -507,11 +449,7 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
self.assertFalse(self.test_rc.should_autorenew())
|
||||
self.test_rc.configuration["autorenew"] = "1"
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}12.pem".format(kind)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, 12)
|
||||
# Mandatory renewal on the basis of OCSP revocation
|
||||
mock_ocsp.return_value = True
|
||||
self.assertTrue(self.test_rc.should_autorenew())
|
||||
@@ -525,13 +463,7 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self._write_out_kind(kind, ver)
|
||||
self.test_rc.update_all_links_to(3)
|
||||
self.assertEqual(
|
||||
6, self.test_rc.save_successor(3, "new cert", None,
|
||||
@@ -586,39 +518,33 @@ class RenewableCertTests(BaseRenewableCertTest):
|
||||
self.assertFalse(os.path.islink(self.test_rc.version("privkey", 10)))
|
||||
self.assertFalse(os.path.exists(temp_config_file))
|
||||
|
||||
@mock.patch("certbot.cli.helpful_parser")
|
||||
def test_relevant_values(self, mock_parser):
|
||||
def _test_relevant_values_common(self, values):
|
||||
option = "rsa_key_size"
|
||||
mock_parser = mock.Mock(args=["--standalone"], verb="certonly",
|
||||
defaults={option: cli.flag_default(option)})
|
||||
|
||||
from certbot.storage import relevant_values
|
||||
with mock.patch("certbot.cli.helpful_parser", mock_parser):
|
||||
return relevant_values(values)
|
||||
|
||||
def test_relevant_values(self):
|
||||
"""Test that relevant_values() can reject an irrelevant value."""
|
||||
# pylint: disable=protected-access
|
||||
from certbot import storage
|
||||
mock_parser.verb = "certonly"
|
||||
mock_parser.args = ["--standalone"]
|
||||
mock_action = mock.Mock(dest="rsa_key_size", default=2048)
|
||||
mock_parser.parser._actions = [mock_action]
|
||||
self.assertEqual(storage.relevant_values({"hello": "there"}), {})
|
||||
self.assertEqual(
|
||||
self._test_relevant_values_common({"hello": "there"}), {})
|
||||
|
||||
@mock.patch("certbot.cli.helpful_parser")
|
||||
def test_relevant_values_default(self, mock_parser):
|
||||
def test_relevant_values_default(self):
|
||||
"""Test that relevant_values() can reject a default value."""
|
||||
# pylint: disable=protected-access
|
||||
from certbot import storage
|
||||
mock_parser.verb = "certonly"
|
||||
mock_parser.args = ["--standalone"]
|
||||
mock_action = mock.Mock(dest="rsa_key_size", default=2048)
|
||||
mock_parser.parser._actions = [mock_action]
|
||||
self.assertEqual(storage.relevant_values({"rsa_key_size": 2048}), {})
|
||||
option = "rsa_key_size"
|
||||
values = {option: cli.flag_default(option)}
|
||||
self.assertEqual(self._test_relevant_values_common(values), {})
|
||||
|
||||
@mock.patch("certbot.cli.helpful_parser")
|
||||
def test_relevant_values_nondefault(self, mock_parser):
|
||||
def test_relevant_values_nondefault(self):
|
||||
"""Test that relevant_values() can retain a non-default value."""
|
||||
# pylint: disable=protected-access
|
||||
from certbot import storage
|
||||
mock_parser.verb = "certonly"
|
||||
mock_parser.args = ["--standalone"]
|
||||
mock_action = mock.Mock(dest="rsa_key_size", default=2048)
|
||||
mock_parser.parser._actions = [mock_action]
|
||||
self.assertEqual(storage.relevant_values({"rsa_key_size": 12}),
|
||||
{"rsa_key_size": 12})
|
||||
values = {"rsa_key_size": 12}
|
||||
# A copy is given to _test_relevant_values_common
|
||||
# to make sure values isn't modified by the method
|
||||
self.assertEqual(
|
||||
self._test_relevant_values_common(values.copy()), values)
|
||||
|
||||
@mock.patch("certbot.storage.relevant_values")
|
||||
def test_new_lineage(self, mock_rv):
|
||||
|
||||
16
certbot/tests/testdata/cert-5sans.pem
vendored
Normal file
16
certbot/tests/testdata/cert-5sans.pem
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICkTCCAjugAwIBAgIJAJNbfABWQ8bbMA0GCSqGSIb3DQEBCwUAMHkxCzAJBgNV
|
||||
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
|
||||
c2NvMScwJQYDVQQKDB5FbGVjdHJvbmljIEZyb250aWVyIEZvdW5kYXRpb24xFDAS
|
||||
BgNVBAMMC2V4YW1wbGUuY29tMB4XDTE2MDYwOTIzMDEzNloXDTE2MDcwOTIzMDEz
|
||||
NloweTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM
|
||||
DVNhbiBGcmFuY2lzY28xJzAlBgNVBAoMHkVsZWN0cm9uaWMgRnJvbnRpZXIgRm91
|
||||
bmRhdGlvbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0BAQEFAANL
|
||||
ADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2+6QWE
|
||||
30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABo4GlMIGiMB0GA1UdDgQWBBQmz8jt
|
||||
S9eUsuQlA1gkjwTAdNWXijAfBgNVHSMEGDAWgBQmz8jtS9eUsuQlA1gkjwTAdNWX
|
||||
ijAMBgNVHRMEBTADAQH/MFIGA1UdEQRLMEmCDWEuZXhhbXBsZS5jb22CDWIuZXhh
|
||||
bXBsZS5jb22CDWMuZXhhbXBsZS5jb22CDWQuZXhhbXBsZS5jb22CC2V4YW1wbGUu
|
||||
Y29tMA0GCSqGSIb3DQEBCwUAA0EAVXmZxB+IJdgFvY2InOYeytTD1QmouDZRtj/T
|
||||
H/HIpSdsfO7qr4d/ZprI2IhLRxp2S4BiU5Qc5HUkeADcpNd06A==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -423,14 +423,17 @@ def enforce_domain_sanity(domain):
|
||||
# It wasn't an IP address, so that's good
|
||||
pass
|
||||
|
||||
# FQDN checks from
|
||||
# http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/
|
||||
# Characters used, domain parts < 63 chars, tld > 1 < 64 chars
|
||||
# first and last char is not "-"
|
||||
fqdn = re.compile("^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,63}$")
|
||||
if not fqdn.match(domain):
|
||||
raise errors.ConfigurationError("Requested domain {0} is not a FQDN"
|
||||
.format(domain))
|
||||
# FQDN checks according to RFC 2181: domain name should be less than 255
|
||||
# octets (inclusive). And each label is 1 - 63 octets (inclusive).
|
||||
# https://tools.ietf.org/html/rfc2181#section-11
|
||||
msg = "Requested domain {0} is not a FQDN because ".format(domain)
|
||||
labels = domain.split('.')
|
||||
for l in labels:
|
||||
if not 0 < len(l) < 64:
|
||||
raise errors.ConfigurationError(msg + "label {0} is too long.".format(l))
|
||||
if len(domain) > 255:
|
||||
raise errors.ConfigurationError(msg + "it is too long.")
|
||||
|
||||
return domain
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ cert. Major SUBCOMMANDS are:
|
||||
install Install a previously obtained cert in a server
|
||||
renew Renew previously obtained certs that are near expiry
|
||||
revoke Revoke a previously obtained certificate
|
||||
register Perform tasks related to registering with the CA
|
||||
rollback Rollback server configuration changes made during install
|
||||
config_changes Show changes made to server config during installation
|
||||
plugins Display information about installed plugins
|
||||
@@ -53,6 +54,11 @@ optional arguments:
|
||||
to the Subscriber Agreement will still affect you, and
|
||||
will be effective 14 days after posting an update to
|
||||
the web site. (default: False)
|
||||
--update-registration
|
||||
With the register verb, indicates that details
|
||||
associated with an existing registration, such as the
|
||||
e-mail address, should be updated, rather than
|
||||
registering a new account. (default: False)
|
||||
-m EMAIL, --email EMAIL
|
||||
Email used for registration and recovery contact.
|
||||
(default: None)
|
||||
@@ -194,6 +200,15 @@ renew:
|
||||
and keys; the shell variable $RENEWED_DOMAINS will
|
||||
contain a space-delimited list of renewed cert domains
|
||||
(default: None)
|
||||
--disable-hook-validation
|
||||
Ordinarily the commands specified for --pre-hook
|
||||
/--post-hook/--renew-hook will be checked for
|
||||
validity, to see if the programs being run are in the
|
||||
$PATH, so that mistakes can be caught early, even when
|
||||
the hooks aren't being run just yet. The validation is
|
||||
rather simplistic and fails if you use more advanced
|
||||
shell constructs, so you can use this switch to
|
||||
disable it. (default: True)
|
||||
|
||||
certonly:
|
||||
Options for modifying how a cert is obtained
|
||||
|
||||
@@ -71,6 +71,9 @@ The following tools are there to help you:
|
||||
experimental, non-production Apache2 install on them. ``tox -e
|
||||
apacheconftest`` can be used to run those specific Apache conf tests.
|
||||
|
||||
- ``tox --skip-missing-interpreters`` runs tox while ignoring missing versions
|
||||
of Python needed for running the tests.
|
||||
|
||||
- ``tox -e py27``, ``tox -e py26`` etc, run unit tests for specific Python
|
||||
versions.
|
||||
|
||||
@@ -313,7 +316,9 @@ Steps:
|
||||
3. Run ``./pep8.travis.sh`` to do a cursory check of your code style.
|
||||
Fix any errors.
|
||||
4. Run ``tox -e lint`` to check for pylint errors. Fix any errors.
|
||||
5. Run ``tox`` to run the entire test suite including coverage. Fix any errors.
|
||||
5. Run ``tox --skip-missing-interpreters`` to run the entire test suite
|
||||
including coverage. The ``--skip-missing-interpreters`` argument ignores
|
||||
missing versions of Python needed for running the tests. Fix any errors.
|
||||
6. If your code touches communication with an ACME server/Boulder, you
|
||||
should run the integration tests, see `integration`_. See `Known Issues`_
|
||||
for some common failures that have nothing to do with your code.
|
||||
|
||||
@@ -552,10 +552,3 @@ Beyond the methods discussed here, other methods may be possible, such as
|
||||
installing Certbot directly with pip from PyPI or downloading a ZIP
|
||||
archive from GitHub may be technically possible but are not presently
|
||||
recommended or supported.
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#venv] By using this virtualized Python environment (`virtualenv
|
||||
<https://virtualenv.pypa.io>`_) we don't pollute the main
|
||||
OS space with packages from PyPI!
|
||||
|
||||
@@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
|
||||
VENV_NAME="letsencrypt"
|
||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="0.7.0"
|
||||
LE_AUTO_VERSION="0.8.1"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
@@ -172,7 +172,7 @@ BootstrapDebCommon() {
|
||||
# distro version (#346)
|
||||
|
||||
virtualenv=
|
||||
if apt-cache show virtualenv > /dev/null 2>&1; then
|
||||
if apt-cache show virtualenv > /dev/null 2>&1 && ! apt-cache --quiet=0 show virtualenv 2>&1 | grep -q 'No packages found'; then
|
||||
virtualenv="virtualenv"
|
||||
fi
|
||||
|
||||
@@ -458,12 +458,39 @@ BootstrapSmartOS() {
|
||||
pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv'
|
||||
}
|
||||
|
||||
BootstrapMageiaCommon() {
|
||||
if ! $SUDO urpmi --force \
|
||||
python \
|
||||
libpython-devel \
|
||||
python-virtualenv
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! $SUDO urpmi --force \
|
||||
git \
|
||||
gcc \
|
||||
cdialog \
|
||||
python-augeas \
|
||||
libopenssl-devel \
|
||||
libffi-devel \
|
||||
rootcerts
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Install required OS packages:
|
||||
Bootstrap() {
|
||||
if [ -f /etc/debian_version ]; then
|
||||
echo "Bootstrapping dependencies for Debian-based OSes..."
|
||||
BootstrapDebCommon
|
||||
elif [ -f /etc/mageia-release ] ; then
|
||||
# Mageia has both /etc/mageia-release and /etc/redhat-release
|
||||
ExperimentalBootstrap "Mageia" BootstrapMageiaCommon
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
||||
BootstrapRpmCommon
|
||||
@@ -476,7 +503,7 @@ Bootstrap() {
|
||||
BootstrapArchCommon
|
||||
else
|
||||
echo "Please use pacman to install letsencrypt packages:"
|
||||
echo "# pacman -S letsencrypt letsencrypt-apache"
|
||||
echo "# pacman -S certbot certbot-apache"
|
||||
echo
|
||||
echo "If you would like to use the virtualenv way, please run the script again with the"
|
||||
echo "--debug flag."
|
||||
@@ -500,6 +527,7 @@ Bootstrap() {
|
||||
echo "You will need to bootstrap, configure virtualenv, and run pip install manually."
|
||||
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
||||
echo "for more info."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -713,24 +741,21 @@ zope.interface==4.1.3 \
|
||||
mock==1.0.1 \
|
||||
--hash=sha256:b839dd2d9c117c701430c149956918a423a9863b48b09c90e30a6013e7d2f44f \
|
||||
--hash=sha256:8f83080daa249d036cbccfb8ae5cc6ff007b88d6d937521371afabe7b19badbc
|
||||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.7.0 \
|
||||
--hash=sha256:6e61dba343806ad4cb27af84628152abc9e83a0fa24be6065587d2b46f340d7a \
|
||||
--hash=sha256:9f75a1947978402026b741bdee8a18fc5a1cfd539b78e523b7e5f279bf18eeb9
|
||||
certbot==0.7.0 \
|
||||
--hash=sha256:55604e43d231ac226edefed8dc110d792052095c3d75ad0e4a228ae0989fe5fd \
|
||||
--hash=sha256:ad5083d75e16d1ab806802d3a32f34973b6d7adaf083aee87e07a6c1359efe88
|
||||
certbot-apache==0.7.0 \
|
||||
--hash=sha256:5ab5ed9b2af6c7db9495ce1491122798e9d0764e3df8f0843d11d89690bf7f88 \
|
||||
--hash=sha256:1ddbfaf01bcb0b05c0dcc8b2ebd37637f080cf798151e8140c20c9f5fe7bae75
|
||||
letsencrypt==0.7.0 \
|
||||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
letsencrypt-apache==0.7.0 \
|
||||
--hash=sha256:10445980a6afc810325ea22a56e269229999120848f6c0b323b00275696b5c80 \
|
||||
--hash=sha256:3f4656088a18e4efea7cd7eb4965e14e8d901f3b64f4691e79cafd0bb91890f0
|
||||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.8.1 \
|
||||
--hash=sha256:ccd7883772efbf933f91713b8241455993834f3620c8fbd459d9ed5e50bbaaca \
|
||||
--hash=sha256:d3ea4acf280bf6253ad7d641cb0970f230a19805acfed809e7a8ddcf62157d9f
|
||||
certbot==0.8.1 \
|
||||
--hash=sha256:89805d9f70249ae859ec4d7a99c00b4bb7083ca90cd12d4d202b76dfc284f7c5 \
|
||||
--hash=sha256:6ca8df3d310ced6687d38aac17c0fb8c1b2ec7a3bea156a254e4cc2a1c132771
|
||||
certbot-apache==0.8.1 \
|
||||
--hash=sha256:c9e3fdc15e65589c2e39eb0e6b1f61f0c0a1db3c17b00bb337f0ff636cc61cb3 \
|
||||
--hash=sha256:0faf2879884d3b7a58b071902fba37d4b8b58a50e2c3b8ac262c0a74134045ed
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1
|
||||
|
||||
iQEcBAABAgAGBQJXSK5DAAoJEE0XyZXNl3Xyyb4H/Ahy9/8ADDaN5V/O/6kl6gE5
|
||||
amQfm8T10EUD8APnNWYrYKBYruDBVvH0KiEcuAEs7q4xE5BaQatlobSnsHfv4AWW
|
||||
TwInk2lRxYZ++MwwQf3DrqMK5QKfcoVnViZsRpZ8gHMLzsJllRm7R5eaTewO2ViM
|
||||
KM+yDB3UsquLUvE4d3/hgBl2mXAUwsxLeFreZayvpoTcX2ARnzbtKqMaIBYDYWcx
|
||||
DewWtDsPrhKFpb2DY06S6JLmEttysUgv+hbKlaVO0yZ8cCUehkzBIGYoeS4chOLq
|
||||
fonNCzB8u3RtnLEFiPIy0N+A592jbLsqqUkxjammaJq3lH7nitduMLnpvGKt4yc=
|
||||
=ex1J
|
||||
iQEcBAABAgAGBQJXYJmBAAoJEE0XyZXNl3XyyIMH/jtYFb7rl5XXN8hjlKuK5frq
|
||||
z7/jdK7fvI+mtYJ4i2Cy3yMz8T4wscXGkhxNtipbATWlpevPfjYzm4ZGC25coFZx
|
||||
fDX44w0hBBgel7EISXGR1ABXb2rj24TZxIYXwaeClylsK9n5CxcWBocn8tDlfr8t
|
||||
7VQUJEL3l1IlrnKnvpoL4Eq11sxlIPtitDPJ5c98ZM1293ZbWzIqyZKoXLIUkKHg
|
||||
pkaa80j/QMmFumxzXFenU91JusLdeoblvjjg+kzjGonjslAYIuH4wEEjz2VJuUYe
|
||||
P2+2ZyW4eLA6rRZhZ3CMtV79HzTPTWiELCYbXezb+yXJJEqzCYtIXkmbNQ3jUEY=
|
||||
=86lB
|
||||
-----END PGP SIGNATURE-----
|
||||
|
||||
@@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
|
||||
VENV_NAME="letsencrypt"
|
||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="0.8.0.dev0"
|
||||
LE_AUTO_VERSION="0.9.0.dev0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
@@ -172,7 +172,7 @@ BootstrapDebCommon() {
|
||||
# distro version (#346)
|
||||
|
||||
virtualenv=
|
||||
if apt-cache show virtualenv > /dev/null 2>&1; then
|
||||
if apt-cache show virtualenv > /dev/null 2>&1 && ! apt-cache --quiet=0 show virtualenv 2>&1 | grep -q 'No packages found'; then
|
||||
virtualenv="virtualenv"
|
||||
fi
|
||||
|
||||
@@ -458,12 +458,39 @@ BootstrapSmartOS() {
|
||||
pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv'
|
||||
}
|
||||
|
||||
BootstrapMageiaCommon() {
|
||||
if ! $SUDO urpmi --force \
|
||||
python \
|
||||
libpython-devel \
|
||||
python-virtualenv
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! $SUDO urpmi --force \
|
||||
git \
|
||||
gcc \
|
||||
cdialog \
|
||||
python-augeas \
|
||||
libopenssl-devel \
|
||||
libffi-devel \
|
||||
rootcerts
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Install required OS packages:
|
||||
Bootstrap() {
|
||||
if [ -f /etc/debian_version ]; then
|
||||
echo "Bootstrapping dependencies for Debian-based OSes..."
|
||||
BootstrapDebCommon
|
||||
elif [ -f /etc/mageia-release ] ; then
|
||||
# Mageia has both /etc/mageia-release and /etc/redhat-release
|
||||
ExperimentalBootstrap "Mageia" BootstrapMageiaCommon
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
||||
BootstrapRpmCommon
|
||||
@@ -476,7 +503,7 @@ Bootstrap() {
|
||||
BootstrapArchCommon
|
||||
else
|
||||
echo "Please use pacman to install letsencrypt packages:"
|
||||
echo "# pacman -S letsencrypt letsencrypt-apache"
|
||||
echo "# pacman -S certbot certbot-apache"
|
||||
echo
|
||||
echo "If you would like to use the virtualenv way, please run the script again with the"
|
||||
echo "--debug flag."
|
||||
@@ -500,6 +527,7 @@ Bootstrap() {
|
||||
echo "You will need to bootstrap, configure virtualenv, and run pip install manually."
|
||||
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
||||
echo "for more info."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -719,15 +747,15 @@ letsencrypt==0.7.0 \
|
||||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.7.0 \
|
||||
--hash=sha256:6e61dba343806ad4cb27af84628152abc9e83a0fa24be6065587d2b46f340d7a \
|
||||
--hash=sha256:9f75a1947978402026b741bdee8a18fc5a1cfd539b78e523b7e5f279bf18eeb9
|
||||
certbot==0.7.0 \
|
||||
--hash=sha256:55604e43d231ac226edefed8dc110d792052095c3d75ad0e4a228ae0989fe5fd \
|
||||
--hash=sha256:ad5083d75e16d1ab806802d3a32f34973b6d7adaf083aee87e07a6c1359efe88
|
||||
certbot-apache==0.7.0 \
|
||||
--hash=sha256:5ab5ed9b2af6c7db9495ce1491122798e9d0764e3df8f0843d11d89690bf7f88 \
|
||||
--hash=sha256:1ddbfaf01bcb0b05c0dcc8b2ebd37637f080cf798151e8140c20c9f5fe7bae75
|
||||
acme==0.8.1 \
|
||||
--hash=sha256:ccd7883772efbf933f91713b8241455993834f3620c8fbd459d9ed5e50bbaaca \
|
||||
--hash=sha256:d3ea4acf280bf6253ad7d641cb0970f230a19805acfed809e7a8ddcf62157d9f
|
||||
certbot==0.8.1 \
|
||||
--hash=sha256:89805d9f70249ae859ec4d7a99c00b4bb7083ca90cd12d4d202b76dfc284f7c5 \
|
||||
--hash=sha256:6ca8df3d310ced6687d38aac17c0fb8c1b2ec7a3bea156a254e4cc2a1c132771
|
||||
certbot-apache==0.8.1 \
|
||||
--hash=sha256:c9e3fdc15e65589c2e39eb0e6b1f61f0c0a1db3c17b00bb337f0ff636cc61cb3 \
|
||||
--hash=sha256:0faf2879884d3b7a58b071902fba37d4b8b58a50e2c3b8ac262c0a74134045ed
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
Binary file not shown.
@@ -155,12 +155,16 @@ DeterminePythonVersion() {
|
||||
{{ bootstrappers/free_bsd.sh }}
|
||||
{{ bootstrappers/mac.sh }}
|
||||
{{ bootstrappers/smartos.sh }}
|
||||
{{ bootstrappers/mageia_common.sh }}
|
||||
|
||||
# Install required OS packages:
|
||||
Bootstrap() {
|
||||
if [ -f /etc/debian_version ]; then
|
||||
echo "Bootstrapping dependencies for Debian-based OSes..."
|
||||
BootstrapDebCommon
|
||||
elif [ -f /etc/mageia-release ] ; then
|
||||
# Mageia has both /etc/mageia-release and /etc/redhat-release
|
||||
ExperimentalBootstrap "Mageia" BootstrapMageiaCommon
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
||||
BootstrapRpmCommon
|
||||
@@ -173,7 +177,7 @@ Bootstrap() {
|
||||
BootstrapArchCommon
|
||||
else
|
||||
echo "Please use pacman to install letsencrypt packages:"
|
||||
echo "# pacman -S letsencrypt letsencrypt-apache"
|
||||
echo "# pacman -S certbot certbot-apache"
|
||||
echo
|
||||
echo "If you would like to use the virtualenv way, please run the script again with the"
|
||||
echo "--debug flag."
|
||||
@@ -197,6 +201,7 @@ Bootstrap() {
|
||||
echo "You will need to bootstrap, configure virtualenv, and run pip install manually."
|
||||
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
||||
echo "for more info."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ BootstrapDebCommon() {
|
||||
# distro version (#346)
|
||||
|
||||
virtualenv=
|
||||
if apt-cache show virtualenv > /dev/null 2>&1; then
|
||||
if apt-cache show virtualenv > /dev/null 2>&1 && ! apt-cache --quiet=0 show virtualenv 2>&1 | grep -q 'No packages found'; then
|
||||
virtualenv="virtualenv"
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
BootstrapMageiaCommon() {
|
||||
if ! $SUDO urpmi --force \
|
||||
python \
|
||||
libpython-devel \
|
||||
python-virtualenv
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! $SUDO urpmi --force \
|
||||
git \
|
||||
gcc \
|
||||
cdialog \
|
||||
python-augeas \
|
||||
libopenssl-devel \
|
||||
libffi-devel \
|
||||
rootcerts
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@@ -181,12 +181,12 @@ letsencrypt==0.7.0 \
|
||||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.7.0 \
|
||||
--hash=sha256:6e61dba343806ad4cb27af84628152abc9e83a0fa24be6065587d2b46f340d7a \
|
||||
--hash=sha256:9f75a1947978402026b741bdee8a18fc5a1cfd539b78e523b7e5f279bf18eeb9
|
||||
certbot==0.7.0 \
|
||||
--hash=sha256:55604e43d231ac226edefed8dc110d792052095c3d75ad0e4a228ae0989fe5fd \
|
||||
--hash=sha256:ad5083d75e16d1ab806802d3a32f34973b6d7adaf083aee87e07a6c1359efe88
|
||||
certbot-apache==0.7.0 \
|
||||
--hash=sha256:5ab5ed9b2af6c7db9495ce1491122798e9d0764e3df8f0843d11d89690bf7f88 \
|
||||
--hash=sha256:1ddbfaf01bcb0b05c0dcc8b2ebd37637f080cf798151e8140c20c9f5fe7bae75
|
||||
acme==0.8.1 \
|
||||
--hash=sha256:ccd7883772efbf933f91713b8241455993834f3620c8fbd459d9ed5e50bbaaca \
|
||||
--hash=sha256:d3ea4acf280bf6253ad7d641cb0970f230a19805acfed809e7a8ddcf62157d9f
|
||||
certbot==0.8.1 \
|
||||
--hash=sha256:89805d9f70249ae859ec4d7a99c00b4bb7083ca90cd12d4d202b76dfc284f7c5 \
|
||||
--hash=sha256:6ca8df3d310ced6687d38aac17c0fb8c1b2ec7a3bea156a254e4cc2a1c132771
|
||||
certbot-apache==0.8.1 \
|
||||
--hash=sha256:c9e3fdc15e65589c2e39eb0e6b1f61f0c0a1db3c17b00bb337f0ff636cc61cb3 \
|
||||
--hash=sha256:0faf2879884d3b7a58b071902fba37d4b8b58a50e2c3b8ac262c0a74134045ed
|
||||
|
||||
@@ -2,16 +2,4 @@
|
||||
|
||||
set -e # Fail fast
|
||||
|
||||
# PEP8 is not ignored in ACME
|
||||
pep8 --config=acme/.pep8 acme
|
||||
|
||||
pep8 \
|
||||
setup.py \
|
||||
certbot \
|
||||
certbot-apache \
|
||||
certbot-nginx \
|
||||
certbot-compatibility-test \
|
||||
letshelp-certbot \
|
||||
|| echo "PEP8 checking failed, but it's ignored in Travis"
|
||||
|
||||
# echo exits with 0
|
||||
|
||||
@@ -84,6 +84,24 @@ if [ "$size1" -lt 3000 ] || [ "$size2" -lt 3000 ] || [ "$size3" -gt 1800 ] ; the
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ECDSA
|
||||
openssl ecparam -genkey -name secp384r1 -out "${root}/privkey-p384.pem"
|
||||
SAN="DNS:ecdsa.le.wtf" openssl req -new -sha256 \
|
||||
-config "${OPENSSL_CNF:-openssl.cnf}" \
|
||||
-key "${root}/privkey-p384.pem" \
|
||||
-subj "/" \
|
||||
-reqexts san \
|
||||
-outform der \
|
||||
-out "${root}/csr-p384.der"
|
||||
common auth --csr "${root}/csr-p384.der" \
|
||||
--cert-path "${root}/csr/cert-p384.pem" \
|
||||
--chain-path "${root}/csr/chain-p384.pem"
|
||||
openssl x509 -in "${root}/csr/cert-p384.pem" -text | grep 'ASN1 OID: secp384r1'
|
||||
|
||||
# OCSP Must Staple
|
||||
common auth --must-staple --domains "must-staple.le.wtf"
|
||||
openssl x509 -in "${root}/conf/live/must-staple.le.wtf/cert.pem" -text | grep '1.3.6.1.5.5.7.1.24'
|
||||
|
||||
# revoke by account key
|
||||
common revoke --cert-path "$root/conf/live/le.wtf/cert.pem"
|
||||
# revoke renewed
|
||||
|
||||
@@ -18,8 +18,7 @@ virtualenv --no-site-packages $VENV_NAME $VENV_ARGS
|
||||
# Separately install setuptools and pip to make sure following
|
||||
# invocations use latest
|
||||
pip install -U setuptools
|
||||
# --force-reinstall used to fix broken pip installation on some systems
|
||||
pip install --force-reinstall -U pip
|
||||
pip install -U pip
|
||||
pip install "$@"
|
||||
|
||||
set +x
|
||||
|
||||
16
tox.ini
16
tox.ini
@@ -4,7 +4,7 @@
|
||||
|
||||
[tox]
|
||||
skipsdist = true
|
||||
envlist = py{26,27,33,34,35},py{26,27}-oldest,cover,lint
|
||||
envlist = py{26,33,34,35},cover,lint
|
||||
|
||||
# nosetest -v => more verbose output, allows to detect busy waiting
|
||||
# loops, especially on Travis
|
||||
@@ -64,14 +64,14 @@ basepython = python2.7
|
||||
# duplicate code checking; if one of the commands fails, others will
|
||||
# continue, but tox return code will reflect previous error
|
||||
commands =
|
||||
pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-certbot
|
||||
pip install -q -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-certbot
|
||||
./pep8.travis.sh
|
||||
pylint --rcfile=.pylintrc certbot
|
||||
pylint --rcfile=acme/.pylintrc acme/acme
|
||||
pylint --rcfile=.pylintrc certbot-apache/certbot_apache
|
||||
pylint --rcfile=.pylintrc certbot-nginx/certbot_nginx
|
||||
pylint --rcfile=.pylintrc certbot-compatibility-test/certbot_compatibility_test
|
||||
pylint --rcfile=.pylintrc letshelp-certbot/letshelp_certbot
|
||||
pylint --reports=n --rcfile=.pylintrc certbot
|
||||
pylint --reports=n --rcfile=acme/.pylintrc acme/acme
|
||||
pylint --reports=n --rcfile=.pylintrc certbot-apache/certbot_apache
|
||||
pylint --reports=n --rcfile=.pylintrc certbot-nginx/certbot_nginx
|
||||
pylint --reports=n --rcfile=.pylintrc certbot-compatibility-test/certbot_compatibility_test
|
||||
pylint --reports=n --rcfile=.pylintrc letshelp-certbot/letshelp_certbot
|
||||
|
||||
[testenv:apacheconftest]
|
||||
#basepython = python2.7
|
||||
|
||||
Reference in New Issue
Block a user