mirror of
https://github.com/certbot/certbot.git
synced 2026-06-07 15:52:08 -04:00
Merge remote-tracking branch 'upstream/master' into errors
This commit is contained in:
commit
f35d8a5228
20 changed files with 246 additions and 153 deletions
|
|
@ -38,7 +38,7 @@ load-plugins=linter_plugin
|
|||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||
# --disable=W"
|
||||
disable=fixme,locally-disabled,abstract-class-not-used,bad-continuation,too-few-public-methods
|
||||
disable=fixme,locally-disabled,abstract-class-not-used
|
||||
# abstract-class-not-used cannot be disabled locally (at least in pylint 1.4.1)
|
||||
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ function-rgx=[a-z_][a-z0-9_]{2,40}$
|
|||
function-name-hint=[a-z_][a-z0-9_]{2,40}$
|
||||
|
||||
# Regular expression matching correct variable names
|
||||
variable-rgx=[a-z_][a-z0-9_]{1,30}$
|
||||
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Naming hint for variable names
|
||||
variable-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
|
@ -228,8 +228,7 @@ max-module-lines=1250
|
|||
indent-string=' '
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
# This does something silly/broken...
|
||||
#indent-after-paren=4
|
||||
indent-after-paren=4
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
|
|
|||
|
|
@ -9,11 +9,14 @@
|
|||
# - 6.0.10 "squeeze" (x64)
|
||||
# - 7.8 "wheezy" (x64)
|
||||
# - 8.0 "jessie" (x64)
|
||||
# - Raspbian:
|
||||
# - 7.8 (armhf)
|
||||
|
||||
|
||||
# virtualenv binary can be found in different packages depending on
|
||||
# distro version (#346)
|
||||
newer () {
|
||||
apt-get install -y lsb-release --no-install-recommends
|
||||
distro=$(lsb_release -si)
|
||||
# 6.0.10 => 60, 14.04 => 1404
|
||||
# TODO: in sid version==unstable
|
||||
|
|
@ -29,6 +32,8 @@ newer () {
|
|||
fi
|
||||
}
|
||||
|
||||
apt-get update
|
||||
|
||||
# you can force newer if lsb_release is not available (e.g. Docker
|
||||
# debian:jessie base image)
|
||||
if [ "$1" = "newer" ] || newer
|
||||
|
|
@ -43,7 +48,6 @@ fi
|
|||
# #276, https://github.com/martinpaljak/M2Crypto/issues/62,
|
||||
# M2Crypto setup.py:add_multiarch_paths
|
||||
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
git-core \
|
||||
python \
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ def run(args, config, plugins):
|
|||
|
||||
|
||||
def auth(args, config, plugins):
|
||||
"""Authenticate & obtain cert, but do not install it"""
|
||||
"""Authenticate & obtain cert, but do not install it."""
|
||||
# XXX: Update for renewer / RenewableCert
|
||||
acc = _account_init(args, config)
|
||||
if acc is None:
|
||||
|
|
@ -169,13 +169,13 @@ def auth(args, config, plugins):
|
|||
# TODO: Handle errors from _common_run?
|
||||
acme, doms = _common_run(
|
||||
args, config, acc, authenticator=authenticator, installer=installer)
|
||||
if not acme.obtain_and_enroll_certificate(doms, authenticator, installer,
|
||||
plugins):
|
||||
if not acme.obtain_and_enroll_certificate(
|
||||
doms, authenticator, installer, plugins):
|
||||
return "Certificate could not be obtained"
|
||||
|
||||
|
||||
def install(args, config, plugins):
|
||||
"""Install a previously obtained cert in a server"""
|
||||
"""Install a previously obtained cert in a server."""
|
||||
# XXX: Update for renewer/RenewableCert
|
||||
acc = _account_init(args, config)
|
||||
if acc is None:
|
||||
|
|
@ -192,7 +192,7 @@ def install(args, config, plugins):
|
|||
|
||||
|
||||
def revoke(args, unused_config, unused_plugins):
|
||||
"""Revoke a previously obtained certificate"""
|
||||
"""Revoke a previously obtained certificate."""
|
||||
if args.rev_cert is None and args.rev_key is None:
|
||||
return "At least one of --certificate or --key is required"
|
||||
|
||||
|
|
@ -204,7 +204,7 @@ def revoke(args, unused_config, unused_plugins):
|
|||
|
||||
|
||||
def rollback(args, config, plugins):
|
||||
"""Rollback server configuration changes made during install"""
|
||||
"""Rollback server configuration changes made during install."""
|
||||
client.rollback(args.installer, args.checkpoints, config, plugins)
|
||||
|
||||
|
||||
|
|
@ -218,7 +218,7 @@ def config_changes(unused_args, config, unused_plugins):
|
|||
|
||||
|
||||
def plugins_cmd(args, config, plugins): # TODO: Use IDiplay rathern than print
|
||||
"""List server software plugins"""
|
||||
"""List server software plugins."""
|
||||
logging.debug("Expected interfaces: %s", args.ifaces)
|
||||
|
||||
ifaces = [] if args.ifaces is None else args.ifaces
|
||||
|
|
@ -264,6 +264,7 @@ def flag_default(name):
|
|||
"""Default value for CLI flag."""
|
||||
return constants.CLI_DEFAULTS[name]
|
||||
|
||||
|
||||
def config_help(name, hidden=False):
|
||||
"""Help message for `.IConfig` attribute."""
|
||||
if hidden:
|
||||
|
|
@ -271,10 +272,15 @@ def config_help(name, hidden=False):
|
|||
else:
|
||||
return interfaces.IConfig[name].__doc__
|
||||
|
||||
class SilentParser(object):
|
||||
"""An a mini parser wrapper that doesn't print help for its
|
||||
arguments... this one is just needed to the use of callbacks to define
|
||||
arguments within plugins"""
|
||||
|
||||
class SilentParser(object): # pylint: disable=too-few-public-methods
|
||||
"""Silent wrapper around argparse.
|
||||
|
||||
A mini parser wrapper that doesn't print help for its
|
||||
arguments. This is needed for the use of callbacks to define
|
||||
arguments within plugins.
|
||||
|
||||
"""
|
||||
def __init__(self, parser):
|
||||
self.parser = parser
|
||||
def add_argument(self, *args, **kwargs):
|
||||
|
|
@ -282,12 +288,18 @@ class SilentParser(object):
|
|||
kwargs["help"] = argparse.SUPPRESS
|
||||
self.parser.add_argument(*args, **kwargs)
|
||||
|
||||
HELP_TOPICS = ["all", "security", "paths", "automation", "testing"]
|
||||
class HelpfulArgumentParser(object):
|
||||
"""This class wraps argparse, adding the ability to make --help less
|
||||
verbose, and request help on specific subcategories at a time, eg
|
||||
'letsencrypt --help security' for security options."""
|
||||
|
||||
HELP_TOPICS = ["all", "security", "paths", "automation", "testing"]
|
||||
|
||||
|
||||
class HelpfulArgumentParser(object):
|
||||
"""Argparse Wrapper.
|
||||
|
||||
This class wraps argparse, adding the ability to make --help less
|
||||
verbose, and request help on specific subcategories at a time, eg
|
||||
'letsencrypt --help security' for security options.
|
||||
|
||||
"""
|
||||
def __init__(self, args, plugins):
|
||||
self.args = args
|
||||
plugin_names = [name for name, _p in plugins.iteritems()]
|
||||
|
|
@ -316,11 +328,15 @@ class HelpfulArgumentParser(object):
|
|||
self.add_plugin_args(plugins)
|
||||
|
||||
def prescan_for_flag(self, flag, possible_arguments):
|
||||
"""check for a flag, which accepts a fixed set of possible arguments, in
|
||||
"""Checks cli input for flags.
|
||||
|
||||
Check for a flag, which accepts a fixed set of possible arguments, in
|
||||
the command line; we will use this information to configure argparse's
|
||||
help correctly. Return the flag's argument, if it has one that matches
|
||||
the sequence @possible_arguments; otherwise return whether the flag is
|
||||
present"""
|
||||
present.
|
||||
|
||||
"""
|
||||
if flag not in self.args:
|
||||
return False
|
||||
pos = self.args.index(flag)
|
||||
|
|
@ -333,10 +349,12 @@ class HelpfulArgumentParser(object):
|
|||
return True
|
||||
|
||||
def add(self, topic, *args, **kwargs):
|
||||
"""Add a new command line argument. @topic is required, to indicate
|
||||
which part of the help will document it, but can be None for `always
|
||||
documented'."""
|
||||
"""Add a new command line argument.
|
||||
|
||||
@topic is required, to indicate which part of the help will document
|
||||
it, but can be None for `always documented'.
|
||||
|
||||
"""
|
||||
if topic and self.visible_topics[topic]:
|
||||
group = self.groups[topic]
|
||||
group.add_argument(*args, **kwargs)
|
||||
|
|
@ -345,10 +363,14 @@ class HelpfulArgumentParser(object):
|
|||
self.parser.add_argument(*args, **kwargs)
|
||||
|
||||
def add_group(self, topic, **kwargs):
|
||||
"""This has to be called once for every topic; but we leave those calls
|
||||
"""
|
||||
|
||||
This has to be called once for every topic; but we leave those calls
|
||||
next to the argument definitions for clarity. Return something
|
||||
arguments can be added to if necessary, either the parser or an argument
|
||||
group."""
|
||||
group.
|
||||
|
||||
"""
|
||||
if self.visible_topics[topic]:
|
||||
#print "Adding visible group " + topic
|
||||
group = self.parser.add_argument_group(topic, **kwargs)
|
||||
|
|
@ -359,8 +381,12 @@ class HelpfulArgumentParser(object):
|
|||
return self.silent_parser
|
||||
|
||||
def add_plugin_args(self, plugins):
|
||||
"""Let each of the plugins add its own command line arguments, which
|
||||
may or may not be displayed as help topics."""
|
||||
"""
|
||||
|
||||
Let each of the plugins add its own command line arguments, which
|
||||
may or may not be displayed as help topics.
|
||||
|
||||
"""
|
||||
# TODO: plugin_parser should be called for every detected plugin
|
||||
for name, plugin_ep in plugins.iteritems():
|
||||
parser_or_group = self.add_group(name, description=plugin_ep.description)
|
||||
|
|
@ -368,9 +394,14 @@ class HelpfulArgumentParser(object):
|
|||
plugin_ep.plugin_cls.inject_parser_options(parser_or_group, name)
|
||||
|
||||
def determine_help_topics(self, chosen_topic):
|
||||
"""The user may have requested help on a topic, return a dict of which
|
||||
topics to dislpay. @chosen_topic has prescan_for_flag's return type"""
|
||||
"""
|
||||
|
||||
The user may have requested help on a topic, return a dict of which
|
||||
topics to display. @chosen_topic has prescan_for_flag's return type
|
||||
|
||||
:returns: dict
|
||||
|
||||
"""
|
||||
# topics maps each topic to whether it should be documented by
|
||||
# argparse on the command line
|
||||
if chosen_topic == "all":
|
||||
|
|
@ -392,7 +423,8 @@ def create_parser(plugins, args):
|
|||
"e.g. -vvv.")
|
||||
# --help is automatically provided by argparse
|
||||
|
||||
helpful.add_group("automation",
|
||||
helpful.add_group(
|
||||
"automation",
|
||||
description="Arguments for automating execution & other tweaks")
|
||||
helpful.add(
|
||||
"automation", "--version", action="version",
|
||||
|
|
@ -423,9 +455,10 @@ def create_parser(plugins, args):
|
|||
help=config_help("dvsni_port"))
|
||||
|
||||
helpful.add("testing", "--no-simple-http-tls", action="store_true",
|
||||
help=config_help("no_simple_http_tls"))
|
||||
help=config_help("no_simple_http_tls"))
|
||||
|
||||
subparsers = helpful.parser.add_subparsers(metavar="SUBCOMMAND")
|
||||
|
||||
def add_subparser(name, func): # pylint: disable=missing-docstring
|
||||
subparser = subparsers.add_parser(
|
||||
name, help=func.__doc__.splitlines()[0], description=func.__doc__)
|
||||
|
|
@ -460,16 +493,17 @@ def create_parser(plugins, args):
|
|||
|
||||
helpful.add(None, "-d", "--domains", metavar="DOMAIN", action="append")
|
||||
helpful.add(None, "-k", "--accountkey", type=read_file,
|
||||
help="Path to the account key file")
|
||||
help="Path to the account key file")
|
||||
helpful.add(None, "-m", "--email", help=config_help("email"))
|
||||
|
||||
helpful.add_group(
|
||||
"security", description="Security parameters & server settings")
|
||||
helpful.add("security", "-B", "--rsa-key-size", type=int, metavar="N",
|
||||
default=flag_default("rsa_key_size"),
|
||||
help=config_help("rsa_key_size"))
|
||||
helpful.add(
|
||||
"security", "-B", "--rsa-key-size", type=int, metavar="N",
|
||||
default=flag_default("rsa_key_size"), help=config_help("rsa_key_size"))
|
||||
# TODO: resolve - assumes binary logic while client.py assumes ternary.
|
||||
helpful.add("security", "-r", "--redirect", action="store_true",
|
||||
helpful.add(
|
||||
"security", "-r", "--redirect", action="store_true",
|
||||
help="Automatically redirect all HTTP traffic to HTTPS for the newly "
|
||||
"authenticated vhost.")
|
||||
|
||||
|
|
@ -487,7 +521,6 @@ def create_parser(plugins, args):
|
|||
|
||||
_paths_parser(helpful)
|
||||
|
||||
|
||||
return helpful.parser
|
||||
|
||||
|
||||
|
|
@ -515,6 +548,7 @@ def _paths_parser(helpful):
|
|||
add("paths", "-s", "--server", default=flag_default("server"),
|
||||
help=config_help("server"))
|
||||
|
||||
|
||||
def main(args=sys.argv[1:]):
|
||||
"""Command line argument parsing and main script execution."""
|
||||
# note: arg parser internally handles --help (and exits afterwards)
|
||||
|
|
|
|||
|
|
@ -257,8 +257,8 @@ class Client(object):
|
|||
:param certr: ACME "certificate" resource.
|
||||
:type certr: :class:`acme.messages.Certificate`
|
||||
|
||||
:param str cert_path: Path to attempt to save the cert file
|
||||
:param str chain_path: Path to attempt to save the chain file
|
||||
:param str cert_path: Candidate path to a certificate.
|
||||
:param str chain_path: Candidate path to a certificate chain.
|
||||
|
||||
:returns: cert_path, chain_path (absolute paths to the actual files)
|
||||
:rtype: `tuple` of `str`
|
||||
|
|
|
|||
|
|
@ -17,10 +17,15 @@ class NamespaceConfig(object):
|
|||
:attr:`~letsencrypt.interfaces.IConfig.work_dir` and relative
|
||||
paths defined in :py:mod:`letsencrypt.constants`:
|
||||
|
||||
- ``temp_checkpoint_dir``
|
||||
- ``in_progress_dir``
|
||||
- ``cert_key_backup``
|
||||
- ``rec_token_dir``
|
||||
- `accounts_dir`
|
||||
- `account_keys_dir`
|
||||
- `cert_dir`
|
||||
- `cert_key_backup`
|
||||
- `in_progress_dir`
|
||||
- `key_dir`
|
||||
- `rec_token_dir`
|
||||
- `renewer_config_file`
|
||||
- `temp_checkpoint_dir`
|
||||
|
||||
:ivar namespace: Namespace typically produced by
|
||||
:meth:`argparse.ArgumentParser.parse_args`.
|
||||
|
|
@ -35,27 +40,12 @@ class NamespaceConfig(object):
|
|||
def __getattr__(self, name):
|
||||
return getattr(self.namespace, name)
|
||||
|
||||
@property
|
||||
def temp_checkpoint_dir(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(
|
||||
self.namespace.work_dir, constants.TEMP_CHECKPOINT_DIR)
|
||||
|
||||
@property
|
||||
def in_progress_dir(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(self.namespace.work_dir, constants.IN_PROGRESS_DIR)
|
||||
|
||||
@property
|
||||
def server_path(self):
|
||||
"""File path based on ``server``."""
|
||||
parsed = urlparse.urlparse(self.namespace.server)
|
||||
return (parsed.netloc + parsed.path).replace('/', os.path.sep)
|
||||
|
||||
@property
|
||||
def cert_key_backup(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(
|
||||
self.namespace.work_dir, constants.CERT_KEY_BACKUP_DIR,
|
||||
self.server_path)
|
||||
|
||||
@property
|
||||
def accounts_dir(self): #pylint: disable=missing-docstring
|
||||
return os.path.join(
|
||||
|
|
@ -63,11 +53,40 @@ class NamespaceConfig(object):
|
|||
|
||||
@property
|
||||
def account_keys_dir(self): #pylint: disable=missing-docstring
|
||||
return os.path.join(
|
||||
self.namespace.config_dir, constants.ACCOUNTS_DIR,
|
||||
self.server_path, constants.ACCOUNT_KEYS_DIR)
|
||||
return os.path.join(self.accounts_dir, constants.ACCOUNT_KEYS_DIR)
|
||||
|
||||
@property
|
||||
def backup_dir(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(self.namespace.work_dir, constants.BACKUP_DIR)
|
||||
|
||||
@property
|
||||
def cert_key_backup(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(self.namespace.work_dir,
|
||||
constants.CERT_KEY_BACKUP_DIR, self.server_path)
|
||||
|
||||
@property
|
||||
def cert_dir(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(self.namespace.config_dir, constants.CERT_DIR)
|
||||
|
||||
@property
|
||||
def in_progress_dir(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(self.namespace.work_dir, constants.IN_PROGRESS_DIR)
|
||||
|
||||
@property
|
||||
def key_dir(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(self.namespace.config_dir, constants.KEY_DIR)
|
||||
|
||||
# TODO: This should probably include the server name
|
||||
@property
|
||||
def rec_token_dir(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(self.namespace.work_dir, constants.REC_TOKEN_DIR)
|
||||
|
||||
@property
|
||||
def renewer_config_file(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(
|
||||
self.namespace.config_dir, constants.RENEWER_CONFIG_FILENAME)
|
||||
|
||||
@property
|
||||
def temp_checkpoint_dir(self): # pylint: disable=missing-docstring
|
||||
return os.path.join(
|
||||
self.namespace.work_dir, constants.TEMP_CHECKPOINT_DIR)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ from acme import challenges
|
|||
SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins"
|
||||
"""Setuptools entry point group name for plugins."""
|
||||
|
||||
|
||||
CLI_DEFAULTS = dict(
|
||||
config_files=["/etc/letsencrypt/cli.ini"],
|
||||
verbose_count=-(logging.WARNING / 10),
|
||||
|
|
@ -16,14 +15,16 @@ CLI_DEFAULTS = dict(
|
|||
rollback_checkpoints=0,
|
||||
config_dir="/etc/letsencrypt",
|
||||
work_dir="/var/lib/letsencrypt",
|
||||
backup_dir="/var/lib/letsencrypt/backups",
|
||||
key_dir="/etc/letsencrypt/keys",
|
||||
certs_dir="/etc/letsencrypt/certs",
|
||||
cert_path="/etc/letsencrypt/certs/cert-letsencrypt.pem",
|
||||
chain_path="/etc/letsencrypt/certs/chain-letsencrypt.pem",
|
||||
renewer_config_file="/etc/letsencrypt/renewer.conf",
|
||||
no_verify_ssl=False,
|
||||
dvsni_port=challenges.DVSNI.PORT,
|
||||
|
||||
# TODO: blocked by #485, values ignored
|
||||
backup_dir="not used",
|
||||
key_dir="not used",
|
||||
certs_dir="not used",
|
||||
cert_path="not used",
|
||||
chain_path="not used",
|
||||
renewer_config_file="not used",
|
||||
)
|
||||
"""Defaults for CLI flags and `.IConfig` attributes."""
|
||||
|
||||
|
|
@ -61,23 +62,36 @@ List of expected options parameters:
|
|||
CONFIG_DIRS_MODE = 0o755
|
||||
"""Directory mode for ``.IConfig.config_dir`` et al."""
|
||||
|
||||
TEMP_CHECKPOINT_DIR = "temp_checkpoint"
|
||||
"""Temporary checkpoint directory (relative to IConfig.work_dir)."""
|
||||
|
||||
IN_PROGRESS_DIR = "IN_PROGRESS"
|
||||
"""Directory used before a permanent checkpoint is finalized (relative to
|
||||
IConfig.work_dir)."""
|
||||
|
||||
CERT_KEY_BACKUP_DIR = "keys-certs"
|
||||
"""Directory where all certificates and keys are stored (relative to
|
||||
IConfig.work_dir. Used for easy revocation."""
|
||||
|
||||
ACCOUNTS_DIR = "accounts"
|
||||
"""Directory where all accounts are saved."""
|
||||
|
||||
ACCOUNT_KEYS_DIR = "keys"
|
||||
"""Directory where account keys are saved. Relative to ACCOUNTS_DIR."""
|
||||
"""Directory where account keys are saved. Relative to `ACCOUNTS_DIR`."""
|
||||
|
||||
BACKUP_DIR = "backups"
|
||||
"""Directory (relative to `IConfig.work_dir`) where backups are kept."""
|
||||
|
||||
CERT_KEY_BACKUP_DIR = "keys-certs"
|
||||
"""Directory where all certificates and keys are stored (relative to
|
||||
`IConfig.work_dir`). Used for easy revocation."""
|
||||
|
||||
CERT_DIR = "certs"
|
||||
"""See `.IConfig.cert_dir`."""
|
||||
|
||||
IN_PROGRESS_DIR = "IN_PROGRESS"
|
||||
"""Directory used before a permanent checkpoint is finalized (relative to
|
||||
`IConfig.work_dir`)."""
|
||||
|
||||
KEY_DIR = "keys"
|
||||
"""Directory (relative to `IConfig.config_dir`) where keys are saved."""
|
||||
|
||||
TEMP_CHECKPOINT_DIR = "temp_checkpoint"
|
||||
"""Temporary checkpoint directory (relative to `IConfig.work_dir`)."""
|
||||
|
||||
REC_TOKEN_DIR = "recovery_tokens"
|
||||
"""Directory where all recovery tokens are saved (relative to
|
||||
IConfig.work_dir)."""
|
||||
`IConfig.work_dir`)."""
|
||||
|
||||
|
||||
RENEWER_CONFIG_FILENAME = "renewer.conf"
|
||||
"""Renewer config file name (relative to `IConfig.config_dir`)."""
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"):
|
|||
return le_util.Key(key_path, key_pem)
|
||||
|
||||
|
||||
def init_save_csr(privkey, names, cert_dir, csrname="csr-letsencrypt.pem"):
|
||||
def init_save_csr(privkey, names, path, csrname="csr-letsencrypt.pem"):
|
||||
"""Initialize a CSR with the given private key.
|
||||
|
||||
:param privkey: Key to include in the CSR
|
||||
|
|
@ -63,7 +63,7 @@ def init_save_csr(privkey, names, cert_dir, csrname="csr-letsencrypt.pem"):
|
|||
|
||||
:param set names: `str` names to include in the CSR
|
||||
|
||||
:param str cert_dir: Certificate save directory.
|
||||
:param str path: Certificate save directory.
|
||||
|
||||
:returns: CSR
|
||||
:rtype: :class:`letsencrypt.le_util.CSR`
|
||||
|
|
@ -72,9 +72,9 @@ def init_save_csr(privkey, names, cert_dir, csrname="csr-letsencrypt.pem"):
|
|||
csr_pem, csr_der = make_csr(privkey.pem, names)
|
||||
|
||||
# Save CSR
|
||||
le_util.make_or_verify_dir(cert_dir, 0o755, os.geteuid())
|
||||
le_util.make_or_verify_dir(path, 0o755, os.geteuid())
|
||||
csr_f, csr_filename = le_util.unique_file(
|
||||
os.path.join(cert_dir, csrname), 0o644)
|
||||
os.path.join(path, csrname), 0o644)
|
||||
csr_f.write(csr_pem)
|
||||
csr_f.close()
|
||||
|
||||
|
|
|
|||
|
|
@ -155,32 +155,29 @@ class IConfig(zope.interface.Interface):
|
|||
|
||||
config_dir = zope.interface.Attribute("Configuration directory.")
|
||||
work_dir = zope.interface.Attribute("Working directory.")
|
||||
backup_dir = zope.interface.Attribute("Configuration backups directory.")
|
||||
temp_checkpoint_dir = zope.interface.Attribute(
|
||||
"Temporary checkpoint directory.")
|
||||
in_progress_dir = zope.interface.Attribute(
|
||||
"Directory used before a permanent checkpoint is finalized.")
|
||||
cert_key_backup = zope.interface.Attribute(
|
||||
"Directory where all certificates and keys are stored. "
|
||||
"Used for easy revocation.")
|
||||
|
||||
accounts_dir = zope.interface.Attribute(
|
||||
"Directory where all account information is stored.")
|
||||
account_keys_dir = zope.interface.Attribute(
|
||||
"Directory where all account keys are stored.")
|
||||
backup_dir = zope.interface.Attribute("Configuration backups directory.")
|
||||
cert_dir = zope.interface.Attribute(
|
||||
"Directory where newly generated Certificate Signing Requests "
|
||||
"(CSRs) and certificates not enrolled in the renewer are saved.")
|
||||
cert_key_backup = zope.interface.Attribute(
|
||||
"Directory where all certificates and keys are stored. "
|
||||
"Used for easy revocation.")
|
||||
in_progress_dir = zope.interface.Attribute(
|
||||
"Directory used before a permanent checkpoint is finalized.")
|
||||
key_dir = zope.interface.Attribute("Keys storage.")
|
||||
rec_token_dir = zope.interface.Attribute(
|
||||
"Directory where all recovery tokens are saved.")
|
||||
key_dir = zope.interface.Attribute("Keys storage.")
|
||||
cert_dir = zope.interface.Attribute("Certificates storage.")
|
||||
|
||||
le_vhost_ext = zope.interface.Attribute(
|
||||
"SSL vhost configuration extension.")
|
||||
temp_checkpoint_dir = zope.interface.Attribute(
|
||||
"Temporary checkpoint directory.")
|
||||
|
||||
renewer_config_file = zope.interface.Attribute(
|
||||
"Location of renewal configuration file.")
|
||||
|
||||
cert_path = zope.interface.Attribute("Let's Encrypt certificate file path.")
|
||||
chain_path = zope.interface.Attribute("Let's Encrypt chain file path.")
|
||||
|
||||
no_verify_ssl = zope.interface.Attribute(
|
||||
"Disable SSL certificate verification.")
|
||||
dvsni_port = zope.interface.Attribute(
|
||||
|
|
@ -191,6 +188,11 @@ class IConfig(zope.interface.Interface):
|
|||
no_simple_http_tls = zope.interface.Attribute(
|
||||
"Do not use TLS when solving SimpleHTTP challenges.")
|
||||
|
||||
# TODO: the following are not used, but blocked by #485
|
||||
le_vhost_ext = zope.interface.Attribute("not used")
|
||||
cert_path = zope.interface.Attribute("not used")
|
||||
chain_path = zope.interface.Attribute("not used")
|
||||
|
||||
|
||||
class IInstaller(IPlugin):
|
||||
"""Generic Let's Encrypt Installer Interface.
|
||||
|
|
|
|||
|
|
@ -175,10 +175,10 @@ class Dvsni(object):
|
|||
|
||||
# test utils
|
||||
|
||||
def setup_ssl_options(config_dir, mod_ssl_conf):
|
||||
def setup_ssl_options(config_dir, src, dest):
|
||||
"""Move the ssl_options into position and return the path."""
|
||||
option_path = os.path.join(config_dir, "options-ssl.conf")
|
||||
shutil.copyfile(mod_ssl_conf, option_path)
|
||||
option_path = os.path.join(config_dir, dest)
|
||||
shutil.copyfile(src, option_path)
|
||||
return option_path
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -30,23 +30,31 @@ class NamespaceConfigTest(unittest.TestCase):
|
|||
|
||||
@mock.patch('letsencrypt.configuration.constants')
|
||||
def test_dynamic_dirs(self, constants):
|
||||
constants.TEMP_CHECKPOINT_DIR = 't'
|
||||
constants.IN_PROGRESS_DIR = '../p'
|
||||
constants.CERT_KEY_BACKUP_DIR = 'c/'
|
||||
constants.REC_TOKEN_DIR = '/r'
|
||||
constants.ACCOUNTS_DIR = 'acc'
|
||||
constants.ACCOUNT_KEYS_DIR = 'keys'
|
||||
constants.BACKUP_DIR = 'backups'
|
||||
constants.CERT_KEY_BACKUP_DIR = 'c/'
|
||||
constants.CERT_DIR = 'certs'
|
||||
constants.IN_PROGRESS_DIR = '../p'
|
||||
constants.KEY_DIR = 'keys'
|
||||
constants.REC_TOKEN_DIR = '/r'
|
||||
constants.RENEWER_CONFIG_FILENAME = 'r.conf'
|
||||
constants.TEMP_CHECKPOINT_DIR = 't'
|
||||
|
||||
self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t')
|
||||
self.assertEqual(self.config.in_progress_dir, '/tmp/foo/../p')
|
||||
self.assertEqual(
|
||||
self.config.cert_key_backup, '/tmp/foo/c/acme-server.org:443/new')
|
||||
self.assertEqual(self.config.rec_token_dir, '/r')
|
||||
self.assertEqual(
|
||||
self.config.accounts_dir, '/tmp/config/acc/acme-server.org:443/new')
|
||||
self.assertEqual(
|
||||
self.config.account_keys_dir,
|
||||
'/tmp/config/acc/acme-server.org:443/new/keys')
|
||||
self.assertEqual(self.config.backup_dir, '/tmp/foo/backups')
|
||||
self.assertEqual(self.config.cert_dir, '/tmp/config/certs')
|
||||
self.assertEqual(
|
||||
self.config.cert_key_backup, '/tmp/foo/c/acme-server.org:443/new')
|
||||
self.assertEqual(self.config.in_progress_dir, '/tmp/foo/../p')
|
||||
self.assertEqual(self.config.key_dir, '/tmp/config/keys')
|
||||
self.assertEqual(self.config.rec_token_dir, '/r')
|
||||
self.assertEqual(self.config.renewer_config_file, '/tmp/config/r.conf')
|
||||
self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -89,8 +89,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
def add_parser_arguments(cls, add):
|
||||
add("server-root", default=constants.CLI_DEFAULTS["server_root"],
|
||||
help="Apache server root directory.")
|
||||
add("mod-ssl-conf", default=constants.CLI_DEFAULTS["mod_ssl_conf"],
|
||||
help="Contains standard Apache SSL directives.")
|
||||
add("ctl", default=constants.CLI_DEFAULTS["ctl"],
|
||||
help="Path to the 'apache2ctl' binary, used for 'configtest' and "
|
||||
"retrieving Apache2 version number.")
|
||||
|
|
@ -99,6 +97,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
add("init-script", default=constants.CLI_DEFAULTS["init_script"],
|
||||
help="Path to the Apache init script (used for server "
|
||||
"reload/restart).")
|
||||
add("le-vhost-ext", default=constants.CLI_DEFAULTS["le_vhost_ext"],
|
||||
help="SSL vhost configuration extension.")
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize an Apache Configurator.
|
||||
|
|
@ -125,10 +126,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
self.vhosts = None
|
||||
self._enhance_func = {"redirect": self._enable_redirect}
|
||||
|
||||
@property
|
||||
def mod_ssl_conf(self):
|
||||
"""Full absolute path to SSL configuration file."""
|
||||
return os.path.join(self.config.config_dir, constants.MOD_SSL_CONF_DEST)
|
||||
|
||||
def prepare(self):
|
||||
"""Prepare the authenticator/installer."""
|
||||
self.parser = parser.ApacheParser(
|
||||
self.aug, self.conf('server-root'), self.conf('mod-ssl-conf'))
|
||||
self.aug, self.conf('server-root'), self.mod_ssl_conf)
|
||||
# Check for errors in parsing files with Augeas
|
||||
self.check_parsing_errors("httpd.aug")
|
||||
|
||||
|
|
@ -146,7 +152,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
# on initialization
|
||||
self._prepare_server_https()
|
||||
|
||||
temp_install(self.conf('mod-ssl-conf'))
|
||||
temp_install(self.mod_ssl_conf)
|
||||
|
||||
def deploy_cert(self, domain, cert_path, key_path, chain_path=None):
|
||||
"""Deploys certificate to specified virtual host.
|
||||
|
|
@ -445,7 +451,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"""Makes an ssl_vhost version of a nonssl_vhost.
|
||||
|
||||
Duplicates vhost and adds default ssl options
|
||||
New vhost will reside as (nonssl_vhost.path) + ``IConfig.le_vhost_ext``
|
||||
New vhost will reside as (nonssl_vhost.path) +
|
||||
``letsencrypt_apache.constants.CLI_DEFAULTS["le_vhost_ext"]``
|
||||
|
||||
.. note:: This function saves the configuration
|
||||
|
||||
|
|
@ -459,9 +466,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
avail_fp = nonssl_vhost.filep
|
||||
# Get filepath of new ssl_vhost
|
||||
if avail_fp.endswith(".conf"):
|
||||
ssl_fp = avail_fp[:-(len(".conf"))] + self.config.le_vhost_ext
|
||||
ssl_fp = avail_fp[:-(len(".conf"))] + self.conf("le_vhost_ext")
|
||||
else:
|
||||
ssl_fp = avail_fp + self.config.le_vhost_ext
|
||||
ssl_fp = avail_fp + self.conf("le_vhost_ext")
|
||||
|
||||
# First register the creation so that it is properly removed if
|
||||
# configuration is rolled back
|
||||
|
|
@ -1161,4 +1168,4 @@ def temp_install(options_ssl):
|
|||
|
||||
# Check to make sure options-ssl.conf is installed
|
||||
if not os.path.isfile(options_ssl):
|
||||
shutil.copyfile(constants.MOD_SSL_CONF, options_ssl)
|
||||
shutil.copyfile(constants.MOD_SSL_CONF_SRC, options_ssl)
|
||||
|
|
|
|||
|
|
@ -4,15 +4,17 @@ import pkg_resources
|
|||
|
||||
CLI_DEFAULTS = dict(
|
||||
server_root="/etc/apache2",
|
||||
mod_ssl_conf="/etc/letsencrypt/options-ssl-apache.conf",
|
||||
ctl="apache2ctl",
|
||||
enmod="a2enmod",
|
||||
init_script="/etc/init.d/apache2",
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
)
|
||||
"""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 = pkg_resources.resource_filename(
|
||||
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."""
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
"mod_loaded") as mock_load:
|
||||
mock_load.return_value = True
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir,
|
||||
self.ssl_options)
|
||||
self.config_path, self.config_dir, self.work_dir)
|
||||
|
||||
self.vh_truth = util.get_vh_truth(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80")
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ class DvsniPerformTest(util.ApacheTest):
|
|||
"mod_loaded") as mock_load:
|
||||
mock_load.return_value = True
|
||||
config = util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir,
|
||||
self.ssl_options)
|
||||
self.config_path, self.config_dir, self.work_dir)
|
||||
|
||||
from letsencrypt_apache import dvsni
|
||||
self.sni = dvsni.ApacheDvsni(config)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ 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)
|
||||
self.config_dir, constants.MOD_SSL_CONF_SRC,
|
||||
constants.MOD_SSL_CONF_DEST)
|
||||
|
||||
self.config_path = os.path.join(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80/apache2")
|
||||
|
|
@ -34,7 +35,7 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods
|
|||
|
||||
|
||||
def get_apache_configurator(
|
||||
config_path, config_dir, work_dir, ssl_options, version=(2, 4, 7)):
|
||||
config_path, config_dir, work_dir, version=(2, 4, 7)):
|
||||
"""Create an Apache Configurator with the specified options."""
|
||||
|
||||
backups = os.path.join(work_dir, "backups")
|
||||
|
|
@ -46,8 +47,7 @@ def get_apache_configurator(
|
|||
config = configurator.ApacheConfigurator(
|
||||
config=mock.MagicMock(
|
||||
apache_server_root=config_path,
|
||||
apache_mod_ssl_conf=ssl_options,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
apache_le_vhost_ext=constants.CLI_DEFAULTS["le_vhost_ext"],
|
||||
backup_dir=backups,
|
||||
config_dir=config_dir,
|
||||
temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),
|
||||
|
|
|
|||
|
|
@ -56,8 +56,6 @@ class NginxConfigurator(common.Plugin):
|
|||
def add_parser_arguments(cls, add):
|
||||
add("server-root", default=constants.CLI_DEFAULTS["server_root"],
|
||||
help="Nginx server root directory.")
|
||||
add("mod-ssl-conf", default=constants.CLI_DEFAULTS["mod_ssl_conf"],
|
||||
help="Contains standard nginx SSL directives.")
|
||||
add("ctl", default=constants.CLI_DEFAULTS["ctl"], help="Path to the "
|
||||
"'nginx' binary, used for 'configtest' and retrieving nginx "
|
||||
"version number.")
|
||||
|
|
@ -91,18 +89,22 @@ class NginxConfigurator(common.Plugin):
|
|||
self.reverter = reverter.Reverter(self.config)
|
||||
self.reverter.recovery_routine()
|
||||
|
||||
@property
|
||||
def mod_ssl_conf(self):
|
||||
"""Full absolute path to SSL configuration file."""
|
||||
return os.path.join(self.config.config_dir, constants.MOD_SSL_CONF_DEST)
|
||||
|
||||
# This is called in determine_authenticator and determine_installer
|
||||
def prepare(self):
|
||||
"""Prepare the authenticator/installer."""
|
||||
self.parser = parser.NginxParser(
|
||||
self.conf('server-root'),
|
||||
self.conf('mod-ssl-conf'))
|
||||
self.conf('server-root'), self.mod_ssl_conf)
|
||||
|
||||
# Set Version
|
||||
if self.version is None:
|
||||
self.version = self.get_version()
|
||||
|
||||
temp_install(self.conf('mod-ssl-conf'))
|
||||
temp_install(self.mod_ssl_conf)
|
||||
|
||||
# Entry point in main.py for installing cert
|
||||
def deploy_cert(self, domain, cert_path, key_path, chain_path=None):
|
||||
|
|
@ -583,4 +585,4 @@ def temp_install(options_ssl):
|
|||
|
||||
# Check to make sure options-ssl.conf is installed
|
||||
if not os.path.isfile(options_ssl):
|
||||
shutil.copyfile(constants.MOD_SSL_CONF, options_ssl)
|
||||
shutil.copyfile(constants.MOD_SSL_CONF_SRC, options_ssl)
|
||||
|
|
|
|||
|
|
@ -4,13 +4,15 @@ import pkg_resources
|
|||
|
||||
CLI_DEFAULTS = dict(
|
||||
server_root="/etc/nginx",
|
||||
mod_ssl_conf="/etc/letsencrypt/options-ssl-nginx.conf",
|
||||
ctl="nginx",
|
||||
)
|
||||
"""CLI defaults."""
|
||||
|
||||
|
||||
MOD_SSL_CONF = pkg_resources.resource_filename(
|
||||
MOD_SSL_CONF_DEST = "options-ssl-nginx.conf"
|
||||
"""Name of the mod_ssl config file as saved in `IConfig.config_dir`."""
|
||||
|
||||
MOD_SSL_CONF_SRC = pkg_resources.resource_filename(
|
||||
"letsencrypt_nginx", "options-ssl-nginx.conf")
|
||||
"""Path to the Nginx mod_ssl config file found in the Let's Encrypt
|
||||
"""Path to the nginx mod_ssl config file found in the Let's Encrypt
|
||||
distribution."""
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
super(NginxConfiguratorTest, self).setUp()
|
||||
|
||||
self.config = util.get_nginx_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir,
|
||||
self.ssl_options)
|
||||
self.config_path, self.config_dir, self.work_dir)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
|
|
|||
|
|
@ -51,8 +51,7 @@ class DvsniPerformTest(util.NginxTest):
|
|||
super(DvsniPerformTest, self).setUp()
|
||||
|
||||
config = util.get_nginx_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir,
|
||||
self.ssl_options)
|
||||
self.config_path, self.config_dir, self.work_dir)
|
||||
|
||||
from letsencrypt_nginx import dvsni
|
||||
self.sni = dvsni.NginxDvsni(config)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods
|
|||
"etc_nginx", "letsencrypt_nginx.tests")
|
||||
|
||||
self.ssl_options = common.setup_ssl_options(
|
||||
self.config_dir, constants.MOD_SSL_CONF)
|
||||
self.config_dir, constants.MOD_SSL_CONF_SRC,
|
||||
constants.MOD_SSL_CONF_DEST)
|
||||
|
||||
self.config_path = os.path.join(self.temp_dir, "etc_nginx")
|
||||
|
||||
|
|
@ -38,18 +39,21 @@ def get_data_filename(filename):
|
|||
|
||||
|
||||
def get_nginx_configurator(
|
||||
config_path, config_dir, work_dir, ssl_options, version=(1, 6, 2)):
|
||||
config_path, config_dir, work_dir, version=(1, 6, 2)):
|
||||
"""Create an Nginx Configurator with the specified options."""
|
||||
|
||||
backups = os.path.join(work_dir, "backups")
|
||||
|
||||
config = configurator.NginxConfigurator(
|
||||
config=mock.MagicMock(
|
||||
nginx_server_root=config_path, nginx_mod_ssl_conf=ssl_options,
|
||||
le_vhost_ext="-le-ssl.conf", backup_dir=backups,
|
||||
config_dir=config_dir, work_dir=work_dir,
|
||||
nginx_server_root=config_path,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
config_dir=config_dir,
|
||||
work_dir=work_dir,
|
||||
backup_dir=backups,
|
||||
temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),
|
||||
in_progress_dir=os.path.join(backups, "IN_PROGRESS")),
|
||||
in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
|
||||
),
|
||||
name="nginx",
|
||||
version=version)
|
||||
config.prepare()
|
||||
|
|
|
|||
Loading…
Reference in a new issue