mirror of
https://github.com/certbot/certbot.git
synced 2026-01-24 19:22:07 +03:00
Merge branch 'master' into dry-run
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
# Baseline setting to Include for SSL sites
|
||||
|
||||
SSLEngine on
|
||||
|
||||
# Intermediate configuration, tweak to your needs
|
||||
SSLProtocol all -SSLv2 -SSLv3
|
||||
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
|
||||
SSLHonorCipherOrder on
|
||||
|
||||
SSLOptions +StrictRequire
|
||||
|
||||
# Add vhost name to log entries:
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
|
||||
LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
|
||||
|
||||
#CustomLog /var/log/apache2/access.log vhost_combined
|
||||
#LogLevel warn
|
||||
#ErrorLog /var/log/apache2/error.log
|
||||
|
||||
# Always ensure Cookies have "Secure" set (JAH 2012/1)
|
||||
#Header edit Set-Cookie (?i)^(.*)(;\s*secure)??((\s*;)?(.*)) "$1; Secure$3$4"
|
||||
@@ -155,7 +155,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# Set Version
|
||||
if self.version is None:
|
||||
self.version = self.get_version()
|
||||
if self.version < (2, 4):
|
||||
if self.version < (2, 2):
|
||||
raise errors.NotSupportedError(
|
||||
"Apache Version %s not supported.", str(self.version))
|
||||
|
||||
@@ -305,6 +305,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
if not vhost.ssl:
|
||||
vhost = self.make_vhost_ssl(vhost)
|
||||
|
||||
self._add_servername_alias(target_name, vhost)
|
||||
self.assoc[target_name] = vhost
|
||||
return vhost
|
||||
|
||||
@@ -335,6 +336,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
raise errors.PluginError(
|
||||
"VirtualHost not able to be selected.")
|
||||
|
||||
self._add_servername_alias(target_name, vhost)
|
||||
self.assoc[target_name] = vhost
|
||||
return vhost
|
||||
|
||||
@@ -353,7 +355,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# Points 1 - Address name with no SSL
|
||||
best_candidate = None
|
||||
best_points = 0
|
||||
|
||||
for vhost in self.vhosts:
|
||||
if vhost.modmacro is True:
|
||||
continue
|
||||
@@ -692,7 +693,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
# Reload augeas to take into account the new vhost
|
||||
self.aug.load()
|
||||
|
||||
# Get Vhost augeas path for new vhost
|
||||
vh_p = self.aug.match("/files%s//* [label()=~regexp('%s')]" %
|
||||
(ssl_fp, parser.case_i("VirtualHost")))
|
||||
@@ -709,6 +709,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
# Add directives
|
||||
self._add_dummy_ssl_directives(vh_p)
|
||||
self.save()
|
||||
|
||||
# Log actions and create save notes
|
||||
logger.info("Created an SSL vhost at %s", ssl_fp)
|
||||
@@ -859,6 +860,22 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"insert_key_file_path")
|
||||
self.parser.add_dir(vh_path, "Include", self.mod_ssl_conf)
|
||||
|
||||
def _add_servername_alias(self, target_name, vhost):
|
||||
fp = vhost.filep
|
||||
vh_p = self.aug.match("/files%s//* [label()=~regexp('%s')]" %
|
||||
(fp, parser.case_i("VirtualHost")))
|
||||
if not vh_p:
|
||||
return
|
||||
vh_path = vh_p[0]
|
||||
if (self.parser.find_dir("ServerName", target_name, start=vh_path, exclude=False)
|
||||
or self.parser.find_dir("ServerAlias", target_name, start=vh_path, exclude=False)):
|
||||
return
|
||||
if not self.parser.find_dir("ServerName", None, start=vh_path, exclude=False):
|
||||
self.parser.add_dir(vh_path, "ServerName", target_name)
|
||||
else:
|
||||
self.parser.add_dir(vh_path, "ServerAlias", target_name)
|
||||
self._add_servernames(vhost)
|
||||
|
||||
def _add_name_vhost_if_necessary(self, vhost):
|
||||
"""Add NameVirtualHost Directives if necessary for new vhost.
|
||||
|
||||
@@ -874,9 +891,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# See if the exact address appears in any other vhost
|
||||
# Remember 1.1.1.1:* == 1.1.1.1 -> hence any()
|
||||
for addr in vhost.addrs:
|
||||
# In Apache 2.2, when a NameVirtualHost directive is not
|
||||
# set, "*" and "_default_" will conflict when sharing a port
|
||||
if addr.get_addr() in ("*", "_default_"):
|
||||
addrs = [obj.Addr((a, addr.get_port(),))
|
||||
for a in ("*", "_default_")]
|
||||
|
||||
for test_vh in self.vhosts:
|
||||
if (vhost.filep != test_vh.filep and
|
||||
any(test_addr == addr for
|
||||
any(test_addr in addrs for
|
||||
test_addr in test_vh.addrs) and
|
||||
not self.is_name_vhost(addr)):
|
||||
self.add_name_vhost(addr)
|
||||
@@ -1587,4 +1610,4 @@ def install_ssl_options_conf(options_ssl):
|
||||
|
||||
# Check to make sure options-ssl.conf is installed
|
||||
if not os.path.isfile(options_ssl):
|
||||
shutil.copyfile(constants.MOD_SSL_CONF_SRC, options_ssl)
|
||||
shutil.copyfile(constants.os_constant("MOD_SSL_CONF_SRC"), options_ssl)
|
||||
|
||||
@@ -16,7 +16,9 @@ CLI_DEFAULTS_DEBIAN = dict(
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_mods=True,
|
||||
handle_sites=True,
|
||||
challenge_location="/etc/apache2"
|
||||
challenge_location="/etc/apache2",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "options-ssl-apache.conf")
|
||||
)
|
||||
CLI_DEFAULTS_CENTOS = dict(
|
||||
server_root="/etc/httpd",
|
||||
@@ -31,7 +33,9 @@ CLI_DEFAULTS_CENTOS = dict(
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/httpd/conf.d"
|
||||
challenge_location="/etc/httpd/conf.d",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "centos-options-ssl-apache.conf")
|
||||
)
|
||||
CLI_DEFAULTS_GENTOO = dict(
|
||||
server_root="/etc/apache2",
|
||||
@@ -46,7 +50,9 @@ CLI_DEFAULTS_GENTOO = dict(
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/apache2/vhosts.d"
|
||||
challenge_location="/etc/apache2/vhosts.d",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "options-ssl-apache.conf")
|
||||
)
|
||||
CLI_DEFAULTS = {
|
||||
"debian": CLI_DEFAULTS_DEBIAN,
|
||||
@@ -62,11 +68,6 @@ CLI_DEFAULTS = {
|
||||
MOD_SSL_CONF_DEST = "options-ssl-apache.conf"
|
||||
"""Name of the mod_ssl config file as saved in `IConfig.config_dir`."""
|
||||
|
||||
MOD_SSL_CONF_SRC = pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "options-ssl-apache.conf")
|
||||
"""Path to the Apache mod_ssl config file found in the Let's Encrypt
|
||||
distribution."""
|
||||
|
||||
AUGEAS_LENS_DIR = pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "augeas_lens")
|
||||
"""Path to the Augeas lens directory"""
|
||||
|
||||
@@ -597,7 +597,7 @@ class ApacheParser(object):
|
||||
.. todo:: Make sure that files are included
|
||||
|
||||
"""
|
||||
default = self._set_user_config_file()
|
||||
default = self.loc["root"]
|
||||
|
||||
temp = os.path.join(self.root, "ports.conf")
|
||||
if os.path.isfile(temp):
|
||||
@@ -618,23 +618,6 @@ class ApacheParser(object):
|
||||
|
||||
raise errors.NoInstallationError("Could not find configuration root")
|
||||
|
||||
def _set_user_config_file(self):
|
||||
"""Set the appropriate user configuration file
|
||||
|
||||
.. todo:: This will have to be updated for other distros versions
|
||||
|
||||
:param str root: pathname which contains the user config
|
||||
|
||||
"""
|
||||
# Basic check to see if httpd.conf exists and
|
||||
# in hierarchy via direct include
|
||||
# httpd.conf was very common as a user file in Apache 2.2
|
||||
if (os.path.isfile(os.path.join(self.root, "httpd.conf")) and
|
||||
self.find_dir("Include", "httpd.conf", self.loc["root"])):
|
||||
return os.path.join(self.root, "httpd.conf")
|
||||
else:
|
||||
return os.path.join(self.root, "apache2.conf")
|
||||
|
||||
|
||||
def case_i(string):
|
||||
"""Returns case insensitive regex.
|
||||
|
||||
@@ -161,6 +161,7 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
def test_choose_vhost_select_vhost_non_ssl(self, mock_select):
|
||||
mock_select.return_value = self.vh_truth[0]
|
||||
chosen_vhost = self.config.choose_vhost("none.com")
|
||||
self.vh_truth[0].aliases.add("none.com")
|
||||
self.assertEqual(
|
||||
self.vh_truth[0].get_names(), chosen_vhost.get_names())
|
||||
|
||||
@@ -192,8 +193,8 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
self.assertEqual(
|
||||
self.vh_truth[0],
|
||||
self.config._find_best_vhost("encryption-example.demo"))
|
||||
self.assertTrue(
|
||||
self.config._find_best_vhost("does-not-exist.com") is None)
|
||||
self.assertEqual(
|
||||
self.config._find_best_vhost("does-not-exist.com"), None)
|
||||
|
||||
def test_find_best_vhost_variety(self):
|
||||
# pylint: disable=protected-access
|
||||
@@ -606,6 +607,14 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
self.config._add_name_vhost_if_necessary(self.vh_truth[0])
|
||||
self.assertTrue(self.config.save.called)
|
||||
|
||||
new_addrs = set()
|
||||
for addr in self.vh_truth[0].addrs:
|
||||
new_addrs.add(obj.Addr(("_default_", addr.get_port(),)))
|
||||
|
||||
self.vh_truth[0].addrs = new_addrs
|
||||
self.config._add_name_vhost_if_necessary(self.vh_truth[0])
|
||||
self.assertEqual(self.config.save.call_count, 2)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.tls_sni_01.ApacheTlsSni01.perform")
|
||||
@mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_perform(self, mock_restart, mock_perform):
|
||||
|
||||
@@ -106,7 +106,7 @@ class BasicParserTest(util.ParserTest):
|
||||
def test_set_locations(self):
|
||||
with mock.patch("letsencrypt_apache.parser.os.path") as mock_path:
|
||||
|
||||
mock_path.isfile.side_effect = [True, False, False]
|
||||
mock_path.isfile.side_effect = [False, False]
|
||||
|
||||
# pylint: disable=protected-access
|
||||
results = self.parser._set_locations()
|
||||
@@ -114,16 +114,6 @@ class BasicParserTest(util.ParserTest):
|
||||
self.assertEqual(results["default"], results["listen"])
|
||||
self.assertEqual(results["default"], results["name"])
|
||||
|
||||
def test_set_user_config_file(self):
|
||||
# pylint: disable=protected-access
|
||||
path = os.path.join(self.parser.root, "httpd.conf")
|
||||
open(path, 'w').close()
|
||||
self.parser.add_dir(self.parser.loc["default"], "Include",
|
||||
"httpd.conf")
|
||||
|
||||
self.assertEqual(
|
||||
path, self.parser._set_user_config_file())
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg")
|
||||
def test_update_runtime_variables(self, mock_cfg):
|
||||
mock_cfg.return_value = (
|
||||
|
||||
@@ -33,7 +33,7 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods
|
||||
pkg="letsencrypt_apache.tests")
|
||||
|
||||
self.ssl_options = common.setup_ssl_options(
|
||||
self.config_dir, constants.MOD_SSL_CONF_SRC,
|
||||
self.config_dir, constants.os_constant("MOD_SSL_CONF_SRC"),
|
||||
constants.MOD_SSL_CONF_DEST)
|
||||
|
||||
self.config_path = os.path.join(self.temp_dir, config_root)
|
||||
@@ -150,7 +150,7 @@ def get_vh_truth(temp_dir, config_name):
|
||||
os.path.join(prefix, "default-ssl-port-only.conf"),
|
||||
os.path.join(aug_pre, ("default-ssl-port-only.conf/"
|
||||
"IfModule/VirtualHost")),
|
||||
set([obj.Addr.fromstring("_default_:443")]), True, False),
|
||||
set([obj.Addr.fromstring("_default_:443")]), True, False)
|
||||
]
|
||||
return vh_truth
|
||||
|
||||
|
||||
@@ -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.3.0"
|
||||
LE_AUTO_VERSION="0.4.0.dev0"
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
@@ -374,7 +374,7 @@ Bootstrap() {
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
||||
BootstrapRpmCommon
|
||||
elif `grep -q openSUSE /etc/os-release` ; then
|
||||
elif [ -f /etc/os-release] && `grep -q openSUSE /etc/os-release` ; then
|
||||
echo "Bootstrapping dependencies for openSUSE-based OSes..."
|
||||
BootstrapSuseCommon
|
||||
elif [ -f /etc/arch-release ]; then
|
||||
|
||||
@@ -130,7 +130,7 @@ Bootstrap() {
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
||||
BootstrapRpmCommon
|
||||
elif `grep -q openSUSE /etc/os-release` ; then
|
||||
elif [ -f /etc/os-release] && `grep -q openSUSE /etc/os-release` ; then
|
||||
echo "Bootstrapping dependencies for openSUSE-based OSes..."
|
||||
BootstrapSuseCommon
|
||||
elif [ -f /etc/arch-release ]; then
|
||||
|
||||
@@ -310,10 +310,10 @@ class NginxConfigurator(common.Plugin):
|
||||
key = OpenSSL.crypto.load_privatekey(
|
||||
OpenSSL.crypto.FILETYPE_PEM, le_key.pem)
|
||||
cert = acme_crypto_util.gen_ss_cert(key, domains=[socket.gethostname()])
|
||||
cert_path = os.path.join(tmp_dir, "cert.pem")
|
||||
cert_pem = OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||
with open(cert_path, 'w') as cert_file:
|
||||
cert_file, cert_path = le_util.unique_file(os.path.join(tmp_dir, "cert.pem"))
|
||||
with cert_file:
|
||||
cert_file.write(cert_pem)
|
||||
return cert_path, le_key.file
|
||||
|
||||
|
||||
@@ -112,11 +112,15 @@ def usage_strings(plugins):
|
||||
return USAGE % (apache_doc, nginx_doc), SHORT_USAGE
|
||||
|
||||
|
||||
def _find_domains(args, installer):
|
||||
if not args.domains:
|
||||
def _find_domains(config, installer):
|
||||
if not config.domains:
|
||||
domains = display_ops.choose_names(installer)
|
||||
# record in config.domains (so that it can be serialised in renewal config files),
|
||||
# and set webroot_map entries if applicable
|
||||
for d in domains:
|
||||
_process_domain(config, d)
|
||||
else:
|
||||
domains = args.domains
|
||||
domains = config.domains
|
||||
|
||||
if not domains:
|
||||
raise errors.Error("Please specify --domains, or --installer that "
|
||||
@@ -608,7 +612,7 @@ def run(args, config, plugins): # pylint: disable=too-many-branches,too-many-lo
|
||||
except errors.PluginSelectionError, e:
|
||||
return e.message
|
||||
|
||||
domains = _find_domains(args, installer)
|
||||
domains = _find_domains(config, installer)
|
||||
|
||||
# TODO: Handle errors from _init_le_client?
|
||||
le_client = _init_le_client(args, config, authenticator, installer)
|
||||
@@ -630,7 +634,8 @@ def run(args, config, plugins): # pylint: disable=too-many-branches,too-many-lo
|
||||
|
||||
|
||||
def obtain_cert(args, config, plugins):
|
||||
"""Authenticate & obtain cert, but do not install it."""
|
||||
"""Implements "certonly": authenticate & obtain cert, but do not install it."""
|
||||
|
||||
if args.domains and args.csr is not None:
|
||||
# TODO: --csr could have a priority, when --domains is
|
||||
# supplied, check if CSR matches given domains?
|
||||
@@ -657,7 +662,7 @@ def obtain_cert(args, config, plugins):
|
||||
certr, chain, args.cert_path, args.chain_path, args.fullchain_path)
|
||||
_report_new_cert(cert_path, cert_fullchain)
|
||||
else:
|
||||
domains = _find_domains(args, installer)
|
||||
domains = _find_domains(config, installer)
|
||||
_auth_from_domains(le_client, config, domains)
|
||||
|
||||
if args.dry_run:
|
||||
@@ -677,7 +682,7 @@ def install(args, config, plugins):
|
||||
except errors.PluginSelectionError, e:
|
||||
return e.message
|
||||
|
||||
domains = _find_domains(args, installer)
|
||||
domains = _find_domains(config, installer)
|
||||
le_client = _init_le_client(
|
||||
args, config, authenticator=None, installer=installer)
|
||||
assert args.cert_path is not None # required=True in the subparser
|
||||
@@ -851,6 +856,13 @@ class HelpfulArgumentParser(object):
|
||||
parsed_args.verb = self.verb
|
||||
|
||||
# Do any post-parsing homework here
|
||||
|
||||
# we get domains from -d, but also from the webroot map...
|
||||
if parsed_args.webroot_map:
|
||||
for domain in parsed_args.webroot_map.keys():
|
||||
if domain not in parsed_args.domains:
|
||||
parsed_args.domains.append(domain)
|
||||
|
||||
if parsed_args.staging or parsed_args.dry_run:
|
||||
if (parsed_args.server not in
|
||||
(flag_default("server"), constants.STAGING_URI)):
|
||||
@@ -1286,11 +1298,12 @@ def _plugins_parsing(helpful, plugins):
|
||||
"handle different domains; each domain will have the webroot path that"
|
||||
" preceded it. For instance: `-w /var/www/example -d example.com -d "
|
||||
"www.example.com -w /var/www/thing -d thing.net -d m.thing.net`")
|
||||
parse_dict = lambda s: dict(json.loads(s))
|
||||
# --webroot-map still has some awkward properties, so it is undocumented
|
||||
helpful.add("webroot", "--webroot-map", default={}, type=parse_dict,
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
helpful.add("webroot", "--webroot-map", default={}, action=WebrootMapProcessor,
|
||||
help="JSON dictionary mapping domains to webroot paths; this implies -d "
|
||||
"for each entry. You may need to escape this from your shell. "
|
||||
"""Eg: --webroot-map '{"eg1.is,m.eg1.is":"/www/eg1/", "eg2.is":"/www/eg2"}' """
|
||||
"This option is merged with, but takes precedence over, -w / -d entries")
|
||||
|
||||
class WebrootPathProcessor(argparse.Action): # pylint: disable=missing-docstring
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -1319,18 +1332,36 @@ class WebrootPathProcessor(argparse.Action): # pylint: disable=missing-docstring
|
||||
config.webroot_path.append(webroot)
|
||||
|
||||
|
||||
_undot = lambda domain: domain[:-1] if domain.endswith('.') else domain
|
||||
|
||||
def _process_domain(config, domain_arg, webroot_path=None):
|
||||
"""
|
||||
Process a new -d flag, helping the webroot plugin construct a map of
|
||||
{domain : webrootpath} if -w / --webroot-path is in use
|
||||
"""
|
||||
webroot_path = webroot_path if webroot_path else config.webroot_path
|
||||
|
||||
for domain in (d.strip() for d in domain_arg.split(",")):
|
||||
if domain not in config.domains:
|
||||
domain = _undot(domain)
|
||||
config.domains.append(domain)
|
||||
# Each domain has a webroot_path of the most recent -w flag
|
||||
# unless it was explicitly included in webroot_map
|
||||
if webroot_path:
|
||||
config.webroot_map.setdefault(domain, webroot_path[-1])
|
||||
|
||||
|
||||
class WebrootMapProcessor(argparse.Action): # pylint: disable=missing-docstring
|
||||
def __call__(self, parser, config, webroot_map_arg, option_string=None):
|
||||
webroot_map = json.loads(webroot_map_arg)
|
||||
for domains, webroot_path in webroot_map.iteritems():
|
||||
_process_domain(config, domains, [webroot_path])
|
||||
|
||||
|
||||
class DomainFlagProcessor(argparse.Action): # pylint: disable=missing-docstring
|
||||
def __call__(self, parser, config, domain_arg, option_string=None):
|
||||
"""
|
||||
Process a new -d flag, helping the webroot plugin construct a map of
|
||||
{domain : webrootpath} if -w / --webroot-path is in use
|
||||
"""
|
||||
for domain in (d.strip() for d in domain_arg.split(",")):
|
||||
if domain not in config.domains:
|
||||
config.domains.append(domain)
|
||||
# Each domain has a webroot_path of the most recent -w flag
|
||||
if config.webroot_path:
|
||||
config.webroot_map[domain] = config.webroot_path[-1]
|
||||
"""Just wrap _process_domain in argparseese."""
|
||||
_process_domain(config, domain_arg)
|
||||
|
||||
|
||||
def setup_log_file_handler(args, logfile, fmt):
|
||||
|
||||
@@ -53,8 +53,8 @@ def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"):
|
||||
config.strict_permissions)
|
||||
key_f, key_path = le_util.unique_file(
|
||||
os.path.join(key_dir, keyname), 0o600)
|
||||
key_f.write(key_pem)
|
||||
key_f.close()
|
||||
with key_f:
|
||||
key_f.write(key_pem)
|
||||
|
||||
logger.info("Generating key (%d bits): %s", key_size, key_path)
|
||||
|
||||
|
||||
@@ -298,18 +298,19 @@ def check_domain_sanity(domain):
|
||||
# Check if there's a wildcard domain
|
||||
if domain.startswith("*."):
|
||||
raise errors.ConfigurationError(
|
||||
"Wildcard domains are not supported")
|
||||
"Wildcard domains are not supported: {0}".format(domain))
|
||||
# Punycode
|
||||
if "xn--" in domain:
|
||||
raise errors.ConfigurationError(
|
||||
"Punycode domains are not presently supported")
|
||||
"Punycode domains are not presently supported: {0}".format(domain))
|
||||
|
||||
# Unicode
|
||||
try:
|
||||
domain.encode('ascii')
|
||||
except UnicodeDecodeError:
|
||||
raise errors.ConfigurationError(
|
||||
"Internationalized domain names are not presently supported")
|
||||
"Internationalized domain names are not presently supported: {0}"
|
||||
.format(domain))
|
||||
|
||||
# FQDN checks from
|
||||
# http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/
|
||||
@@ -317,4 +318,4 @@ def check_domain_sanity(domain):
|
||||
# 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 is not a FQDN")
|
||||
raise errors.ConfigurationError("Requested domain {0} is not a FQDN".format(domain))
|
||||
|
||||
@@ -361,6 +361,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
namespace = parse(short_args)
|
||||
self.assertEqual(namespace.domains, ['example.com'])
|
||||
|
||||
short_args = ['-d', 'trailing.period.com.']
|
||||
namespace = parse(short_args)
|
||||
self.assertEqual(namespace.domains, ['trailing.period.com'])
|
||||
|
||||
short_args = ['-d', 'example.com,another.net,third.org,example.com']
|
||||
namespace = parse(short_args)
|
||||
self.assertEqual(namespace.domains, ['example.com', 'another.net',
|
||||
@@ -370,6 +374,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
namespace = parse(long_args)
|
||||
self.assertEqual(namespace.domains, ['example.com'])
|
||||
|
||||
long_args = ['--domains', 'trailing.period.com.']
|
||||
namespace = parse(long_args)
|
||||
self.assertEqual(namespace.domains, ['trailing.period.com'])
|
||||
|
||||
long_args = ['--domains', 'example.com,another.net,example.com']
|
||||
namespace = parse(long_args)
|
||||
self.assertEqual(namespace.domains, ['example.com', 'another.net'])
|
||||
@@ -424,6 +432,21 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
conflicts += ['--staging']
|
||||
self._check_server_conflict_message(short_args, conflicts)
|
||||
|
||||
def _webroot_map_test(self, map_arg, path_arg, domains_arg, # pylint: disable=too-many-arguments
|
||||
expected_map, expectect_domains, extra_args=None):
|
||||
parse = self._get_argument_parser()
|
||||
webroot_map_args = extra_args if extra_args else []
|
||||
if map_arg:
|
||||
webroot_map_args.extend(["--webroot-map", map_arg])
|
||||
if path_arg:
|
||||
webroot_map_args.extend(["-w", path_arg])
|
||||
if domains_arg:
|
||||
webroot_map_args.extend(["-d", domains_arg])
|
||||
namespace = parse(webroot_map_args)
|
||||
domains = cli._find_domains(namespace, mock.MagicMock()) # pylint: disable=protected-access
|
||||
self.assertEqual(namespace.webroot_map, expected_map)
|
||||
self.assertEqual(set(domains), set(expectect_domains))
|
||||
|
||||
def test_parse_webroot(self):
|
||||
parse = self._get_argument_parser()
|
||||
webroot_args = ['--webroot', '-w', '/var/www/example',
|
||||
@@ -439,9 +462,29 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
webroot_args = ['-d', 'stray.example.com'] + webroot_args
|
||||
self.assertRaises(errors.Error, parse, webroot_args)
|
||||
|
||||
webroot_map_args = ['--webroot-map', '{"eg.com" : "/tmp"}']
|
||||
simple_map = '{"eg.com" : "/tmp"}'
|
||||
expected_map = {"eg.com": "/tmp"}
|
||||
self._webroot_map_test(simple_map, None, None, expected_map, ["eg.com"])
|
||||
|
||||
# test merging webroot maps from the cli and a webroot map
|
||||
expected_map["eg2.com"] = "/tmp2"
|
||||
domains = ["eg.com", "eg2.com"]
|
||||
self._webroot_map_test(simple_map, "/tmp2", "eg2.com,eg.com", expected_map, domains)
|
||||
|
||||
# test inclusion of interactively specified domains in the webroot map
|
||||
with mock.patch('letsencrypt.cli.display_ops.choose_names') as mock_choose:
|
||||
mock_choose.return_value = domains
|
||||
expected_map["eg2.com"] = "/tmp"
|
||||
self._webroot_map_test(None, "/tmp", None, expected_map, domains)
|
||||
|
||||
extra_args = ['-c', test_util.vector_path('webrootconftest.ini')]
|
||||
self._webroot_map_test(None, None, None, expected_map, domains, extra_args)
|
||||
|
||||
webroot_map_args = ['--webroot-map',
|
||||
'{"eg.com.,www.eg.com": "/tmp", "eg.is.": "/tmp2"}']
|
||||
namespace = parse(webroot_map_args)
|
||||
self.assertEqual(namespace.webroot_map, {u"eg.com": u"/tmp"})
|
||||
self.assertEqual(namespace.webroot_map,
|
||||
{"eg.com": "/tmp", "www.eg.com": "/tmp", "eg.is": "/tmp2"})
|
||||
|
||||
def _certonly_new_request_common(self, mock_client, args=None):
|
||||
with mock.patch('letsencrypt.cli._treat_as_renewal') as mock_renewal:
|
||||
|
||||
3
letsencrypt/tests/testdata/webrootconftest.ini
vendored
Normal file
3
letsencrypt/tests/testdata/webrootconftest.ini
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
webroot
|
||||
webroot-path = /tmp
|
||||
domains = eg.com, eg2.com
|
||||
Reference in New Issue
Block a user