mirror of
https://github.com/certbot/certbot.git
synced 2026-01-24 19:22:07 +03:00
@@ -6,7 +6,9 @@ from __future__ import print_function
|
||||
# (TODO: split this file into main.py and cli.py)
|
||||
import argparse
|
||||
import atexit
|
||||
import copy
|
||||
import functools
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import logging.handlers
|
||||
@@ -44,6 +46,19 @@ from letsencrypt.plugins import disco as plugins_disco
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# This is global scope in order to be able to extract type information from
|
||||
# it later
|
||||
_parser = None
|
||||
|
||||
# These are the items which get pulled out of a renewal configuration
|
||||
# file's renewalparams and actually used in the client configuration
|
||||
# during the renewal process. We have to record their types here because
|
||||
# the renewal configuration process loses this information.
|
||||
STR_CONFIG_ITEMS = ["config_dir", "logs_dir", "work_dir", "user_agent",
|
||||
"server", "account", "authenticator", "installer",
|
||||
"standalone_supported_challenges"]
|
||||
INT_CONFIG_ITEMS = ["rsa_key_size", "tls_sni_01_port", "http01_port"]
|
||||
|
||||
# For help strings, figure out how the user ran us.
|
||||
# When invoked from letsencrypt-auto, sys.argv[0] is something like:
|
||||
# "/home/user/.local/share/letsencrypt/bin/letsencrypt"
|
||||
@@ -69,6 +84,7 @@ the cert. Major SUBCOMMANDS are:
|
||||
(default) run Obtain & install a cert in your current webserver
|
||||
certonly Obtain cert, but do not install it (aka "auth")
|
||||
install Install a previously obtained cert in a server
|
||||
renew Renew previously obtained certs that are near expiry
|
||||
revoke Revoke a previously obtained certificate
|
||||
rollback Rollback server configuration changes made during install
|
||||
config_changes Show changes made to server config during installation
|
||||
@@ -208,15 +224,12 @@ def _find_duplicative_certs(config, domains):
|
||||
# Verify the directory is there
|
||||
le_util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())
|
||||
|
||||
for renewal_file in os.listdir(configs_dir):
|
||||
if not renewal_file.endswith(".conf"):
|
||||
continue
|
||||
for renewal_file in _renewal_conf_files(cli_config):
|
||||
try:
|
||||
full_path = os.path.join(configs_dir, renewal_file)
|
||||
candidate_lineage = storage.RenewableCert(full_path, cli_config)
|
||||
candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
|
||||
except (errors.CertStorageError, IOError):
|
||||
logger.warning("Renewal configuration file %s is broken. "
|
||||
"Skipping.", full_path)
|
||||
logger.warning("Renewal conf file %s is broken. Skipping.", renewal_file)
|
||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||
continue
|
||||
# TODO: Handle these differently depending on whether they are
|
||||
# expired or still valid?
|
||||
@@ -235,7 +248,10 @@ def _find_duplicative_certs(config, domains):
|
||||
|
||||
|
||||
def _treat_as_renewal(config, domains):
|
||||
"""Determine whether there are duplicated names and how to handle them.
|
||||
"""Determine whether there are duplicated names and how to handle
|
||||
them (renew, reinstall, newcert, or raising an error to stop
|
||||
the client run if the user chooses to cancel the operation when
|
||||
prompted).
|
||||
|
||||
:returns: Two-element tuple containing desired new-certificate behavior as
|
||||
a string token ("reinstall", "renew", or "newcert"), plus either
|
||||
@@ -262,8 +278,24 @@ def _treat_as_renewal(config, domains):
|
||||
elif subset_names_cert is not None:
|
||||
return _handle_subset_cert_request(config, domains, subset_names_cert)
|
||||
|
||||
|
||||
def _should_renew(config, lineage):
|
||||
"Return true if any of the circumstances for automatic renewal apply."
|
||||
if config.renew_by_default:
|
||||
logger.info("Auto-renewal forced with --renew-by-default...")
|
||||
return True
|
||||
if lineage.should_autorenew(interactive=True):
|
||||
logger.info("Cert is due for renewal, auto-renewing...")
|
||||
return True
|
||||
if config.dry_run:
|
||||
logger.info("Cert not due for renewal, but simulating renewal for dry run")
|
||||
return True
|
||||
logger.info("Cert not yet due for renewal")
|
||||
return False
|
||||
|
||||
|
||||
def _handle_identical_cert_request(config, cert):
|
||||
"""Figure out what to do if a cert has the same names as a perviously obtained one
|
||||
"""Figure out what to do if a cert has the same names as a previously obtained one
|
||||
|
||||
:param storage.RenewableCert cert:
|
||||
|
||||
@@ -271,17 +303,12 @@ def _handle_identical_cert_request(config, cert):
|
||||
:rtype: tuple
|
||||
|
||||
"""
|
||||
if config.renew_by_default:
|
||||
logger.info("Auto-renewal forced with --renew-by-default...")
|
||||
return "renew", cert
|
||||
if cert.should_autorenew(interactive=True):
|
||||
logger.info("Cert is due for renewal, auto-renewing...")
|
||||
if _should_renew(config, cert):
|
||||
return "renew", cert
|
||||
if config.reinstall:
|
||||
# Set with --reinstall, force an identical certificate to be
|
||||
# reinstalled without further prompting.
|
||||
return "reinstall", cert
|
||||
|
||||
question = (
|
||||
"You have an existing certificate that contains exactly the same "
|
||||
"domains you requested and isn't close to expiry."
|
||||
@@ -381,7 +408,7 @@ def _report_new_cert(cert_path, fullchain_path):
|
||||
|
||||
def _suggest_donation_if_appropriate(config):
|
||||
"""Potentially suggest a donation to support Let's Encrypt."""
|
||||
if not config.staging: # --dry-run implies --staging
|
||||
if not config.staging and not config.verb == "renew": # --dry-run implies --staging
|
||||
reporter_util = zope.component.getUtility(interfaces.IReporter)
|
||||
msg = ("If you like Let's Encrypt, please consider supporting our work by:\n\n"
|
||||
"Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n"
|
||||
@@ -395,7 +422,7 @@ def _report_successful_dry_run():
|
||||
reporter_util.HIGH_PRIORITY, on_crash=False)
|
||||
|
||||
|
||||
def _auth_from_domains(le_client, config, domains):
|
||||
def _auth_from_domains(le_client, config, domains, lineage=None):
|
||||
"""Authenticate and enroll certificate."""
|
||||
# Note: This can raise errors... caught above us though. This is now
|
||||
# a three-way case: reinstall (which results in a no-op here because
|
||||
@@ -404,11 +431,15 @@ def _auth_from_domains(le_client, config, domains):
|
||||
# (which results in treating the request as a renewal), or newcert
|
||||
# (which results in treating the request as a new certificate request).
|
||||
|
||||
action, lineage = _treat_as_renewal(config, domains)
|
||||
if config.dry_run and action == "reinstall":
|
||||
logger.info(
|
||||
"Cert not due for renewal, but simulating renewal for dry run")
|
||||
action = "renew"
|
||||
# If lineage is specified, use that one instead of looking around for
|
||||
# a matching one.
|
||||
if lineage is None:
|
||||
# This will find a relevant matching lineage that exists
|
||||
action, lineage = _treat_as_renewal(config, domains)
|
||||
else:
|
||||
# Renewal, where we already know the specific lineage we're
|
||||
# interested in
|
||||
action = "renew" if _should_renew(config, lineage) else "reinstall"
|
||||
|
||||
if action == "reinstall":
|
||||
# The lineage already exists; allow the caller to try installing
|
||||
@@ -638,7 +669,7 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals
|
||||
_suggest_donation_if_appropriate(config)
|
||||
|
||||
|
||||
def obtain_cert(config, plugins):
|
||||
def obtain_cert(config, plugins, lineage=None):
|
||||
"""Implements "certonly": authenticate & obtain cert, but do not install it."""
|
||||
|
||||
if config.domains and config.csr is not None:
|
||||
@@ -657,6 +688,7 @@ def obtain_cert(config, plugins):
|
||||
|
||||
# This is a special case; cert and chain are simply saved
|
||||
if config.csr is not None:
|
||||
assert lineage is None, "Did not expect a CSR with a RenewableCert"
|
||||
certr, chain = le_client.obtain_certificate_from_csr(le_util.CSR(
|
||||
file=config.csr[0], data=config.csr[1], form="der"))
|
||||
if config.dry_run:
|
||||
@@ -668,10 +700,16 @@ def obtain_cert(config, plugins):
|
||||
_report_new_cert(cert_path, cert_fullchain)
|
||||
else:
|
||||
domains = _find_domains(config, installer)
|
||||
_auth_from_domains(le_client, config, domains)
|
||||
_auth_from_domains(le_client, config, domains, lineage)
|
||||
|
||||
if config.dry_run:
|
||||
_report_successful_dry_run()
|
||||
elif config.verb == "renew" and installer is not None:
|
||||
# In case of a renewal, reload server to pick up new certificate.
|
||||
# In principle we could have a configuration option to inhibit this
|
||||
# from happening.
|
||||
installer.restart()
|
||||
print("reloaded")
|
||||
_suggest_donation_if_appropriate(config)
|
||||
|
||||
|
||||
@@ -695,6 +733,198 @@ def install(config, plugins):
|
||||
le_client.enhance_config(domains, config)
|
||||
|
||||
|
||||
def _restore_required_config_elements(config, renewalparams):
|
||||
"""Sets non-plugin specific values in config from renewalparams
|
||||
|
||||
:param configuration.NamespaceConfig config: configuration for the
|
||||
current lineage
|
||||
:param configobj.Section renewalparams: parameters from the renewal
|
||||
configuration file that defines this lineage
|
||||
|
||||
"""
|
||||
# string-valued items to add if they're present
|
||||
for config_item in STR_CONFIG_ITEMS:
|
||||
if config_item in renewalparams:
|
||||
value = renewalparams[config_item]
|
||||
# Unfortunately, we've lost type information from ConfigObj,
|
||||
# so we don't know if the original was NoneType or str!
|
||||
if value == "None":
|
||||
value = None
|
||||
setattr(config.namespace, config_item, value)
|
||||
# int-valued items to add if they're present
|
||||
for config_item in INT_CONFIG_ITEMS:
|
||||
if config_item in renewalparams:
|
||||
try:
|
||||
value = int(renewalparams[config_item])
|
||||
setattr(config.namespace, config_item, value)
|
||||
except ValueError:
|
||||
raise errors.Error(
|
||||
"Expected a numeric value for {0}".format(config_item))
|
||||
|
||||
def _restore_plugin_configs(config, renewalparams):
|
||||
"""Sets plugin specific values in config from renewalparams
|
||||
|
||||
:param configuration.NamespaceConfig config: configuration for the
|
||||
current lineage
|
||||
:param configobj.Section renewalparams: Parameters from the renewal
|
||||
configuration file that defines this lineage
|
||||
|
||||
"""
|
||||
# Now use parser to get plugin-prefixed items with correct types
|
||||
# XXX: the current approach of extracting only prefixed items
|
||||
# related to the actually-used installer and authenticator
|
||||
# works as long as plugins don't need to read plugin-specific
|
||||
# variables set by someone else (e.g., assuming Apache
|
||||
# configurator doesn't need to read webroot_ variables).
|
||||
# Note: if a parameter that used to be defined in the parser is no
|
||||
# longer defined, stored copies of that parameter will be
|
||||
# deserialized as strings by this logic even if they were
|
||||
# originally meant to be some other type.
|
||||
plugin_prefixes = [renewalparams["authenticator"]]
|
||||
if renewalparams.get("installer", None) is not None:
|
||||
plugin_prefixes.append(renewalparams["installer"])
|
||||
for plugin_prefix in set(plugin_prefixes):
|
||||
for config_item, config_value in renewalparams.iteritems():
|
||||
if config_item.startswith(plugin_prefix + "_"):
|
||||
# Avoid confusion when, for example, "csr = None" (avoid
|
||||
# trying to read the file called "None")
|
||||
# Should we omit the item entirely rather than setting
|
||||
# its value to None?
|
||||
if config_value == "None":
|
||||
setattr(config.namespace, config_item, None)
|
||||
continue
|
||||
for action in _parser.parser._actions: # pylint: disable=protected-access
|
||||
if action.type is not None and action.dest == config_item:
|
||||
setattr(config.namespace, config_item,
|
||||
action.type(config_value))
|
||||
break
|
||||
else:
|
||||
setattr(config.namespace, config_item, str(config_value))
|
||||
|
||||
|
||||
def _reconstitute(config, full_path):
|
||||
"""Try to instantiate a RenewableCert, updating config with relevant items.
|
||||
|
||||
This is specifically for use in renewal and enforces several checks
|
||||
and policies to ensure that we can try to proceed with the renwal
|
||||
request. The config argument is modified by including relevant options
|
||||
read from the renewal configuration file.
|
||||
|
||||
:param configuration.NamespaceConfig config: configuration for the
|
||||
current lineage
|
||||
:param str full_path: Absolute path to the configuration file that
|
||||
defines this lineage
|
||||
|
||||
:returns: the RenewableCert object or None if a fatal error occurred
|
||||
:rtype: `storage.RenewableCert` or NoneType
|
||||
|
||||
"""
|
||||
try:
|
||||
renewal_candidate = storage.RenewableCert(
|
||||
full_path, configuration.RenewerConfiguration(config))
|
||||
except (errors.CertStorageError, IOError):
|
||||
logger.warning("Renewal configuration file %s is broken. Skipping.", full_path)
|
||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||
return None
|
||||
if "renewalparams" not in renewal_candidate.configuration:
|
||||
logger.warning("Renewal configuration file %s lacks "
|
||||
"renewalparams. Skipping.", full_path)
|
||||
return None
|
||||
renewalparams = renewal_candidate.configuration["renewalparams"]
|
||||
if "authenticator" not in renewalparams:
|
||||
logger.warning("Renewal configuration file %s does not specify "
|
||||
"an authenticator. Skipping.", full_path)
|
||||
return None
|
||||
# Now restore specific values along with their data types, if
|
||||
# those elements are present.
|
||||
try:
|
||||
_restore_required_config_elements(config, renewalparams)
|
||||
_restore_plugin_configs(config, renewalparams)
|
||||
except (ValueError, errors.Error) as error:
|
||||
logger.warning(
|
||||
"An error occured while parsing %s. The error was %s. "
|
||||
"Skipping the file.", full_path, error.message)
|
||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||
return None
|
||||
|
||||
# webroot_map is, uniquely, a dict, and the general-purpose
|
||||
# configuration restoring logic is not able to correctly parse it
|
||||
# from the serialized form.
|
||||
if "webroot_map" in renewalparams:
|
||||
setattr(config.namespace, "webroot_map", renewalparams["webroot_map"])
|
||||
|
||||
try:
|
||||
domains = [le_util.enforce_domain_sanity(x) for x in
|
||||
renewal_candidate.names()]
|
||||
except (UnicodeError, ValueError):
|
||||
logger.warning("Renewal configuration file %s references a cert "
|
||||
"that mentions a domain name that we regarded as "
|
||||
"invalid. Skipping.", full_path)
|
||||
return None
|
||||
|
||||
setattr(config.namespace, "domains", domains)
|
||||
return renewal_candidate
|
||||
|
||||
|
||||
def _renewal_conf_files(config):
|
||||
"""Return /path/to/*.conf in the renewal conf directory"""
|
||||
return glob.glob(os.path.join(config.renewal_configs_dir, "*.conf"))
|
||||
|
||||
|
||||
def renew(config, unused_plugins):
|
||||
"""Renew previously-obtained certificates."""
|
||||
if config.domains != []:
|
||||
raise errors.Error("Currently, the renew verb is only capable of "
|
||||
"renewing all installed certificates that are due "
|
||||
"to be renewed; individual domains cannot be "
|
||||
"specified with this action. If you would like to "
|
||||
"renew specific certificates, use the certonly "
|
||||
"command. The renew verb may provide other options "
|
||||
"for selecting certificates to renew in the future.")
|
||||
if config.csr is not None:
|
||||
raise errors.Error("Currently, the renew verb cannot be used when "
|
||||
"specifying a CSR file. Please try the certonly "
|
||||
"command instead.")
|
||||
renewer_config = configuration.RenewerConfiguration(config)
|
||||
for renewal_file in _renewal_conf_files(renewer_config):
|
||||
print("Processing " + renewal_file)
|
||||
# XXX: does this succeed in making a fully independent config object
|
||||
# each time?
|
||||
lineage_config = copy.deepcopy(config)
|
||||
|
||||
# Note that this modifies config (to add back the configuration
|
||||
# elements from within the renewal configuration file).
|
||||
try:
|
||||
renewal_candidate = _reconstitute(lineage_config, renewal_file)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
# reconstitute encountered an unanticipated problem.
|
||||
logger.warning("Renewal configuration file %s produced an "
|
||||
"unexpected error: %s. Skipping.", renewal_file, e)
|
||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||
continue
|
||||
|
||||
try:
|
||||
if renewal_candidate is not None:
|
||||
# _reconstitute succeeded in producing a RenewableCert, so we
|
||||
# have something to work with from this particular config file.
|
||||
|
||||
# XXX: ensure that each call here replaces the previous one
|
||||
zope.component.provideUtility(lineage_config)
|
||||
print("Trying...")
|
||||
# Because obtain_cert itself indirectly decides whether to renew
|
||||
# or not, we couldn't currently make a UI/logging distinction at
|
||||
# this stage to indicate whether renewal was actually attempted
|
||||
# (or successful).
|
||||
obtain_cert(lineage_config,
|
||||
plugins_disco.PluginsRegistry.find_all(),
|
||||
renewal_candidate)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
# obtain_cert (presumably) encountered an unanticipated problem.
|
||||
logger.warning("Attempting to renew cert from %s produced an "
|
||||
"unexpected error: %s. Skipping.", renewal_file, e)
|
||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||
|
||||
|
||||
def revoke(config, unused_plugins): # TODO: coop with renewal config
|
||||
"""Revoke a previously obtained certificate."""
|
||||
# For user-agent construction
|
||||
@@ -813,7 +1043,7 @@ class HelpfulArgumentParser(object):
|
||||
# Maps verbs/subcommands to the functions that implement them
|
||||
VERBS = {"auth": obtain_cert, "certonly": obtain_cert,
|
||||
"config_changes": config_changes, "everything": run,
|
||||
"install": install, "plugins": plugins_cmd,
|
||||
"install": install, "plugins": plugins_cmd, "renew": renew,
|
||||
"revoke": revoke, "rollback": rollback, "run": run}
|
||||
|
||||
# List of topics for which additional help can be provided
|
||||
@@ -877,9 +1107,9 @@ class HelpfulArgumentParser(object):
|
||||
parsed_args.server = constants.STAGING_URI
|
||||
|
||||
if parsed_args.dry_run:
|
||||
if self.verb != "certonly":
|
||||
if self.verb not in ["certonly", "renew"]:
|
||||
raise errors.Error("--dry-run currently only works with the "
|
||||
"'certonly' subcommand")
|
||||
"'certonly' or 'renew' subcommands")
|
||||
parsed_args.break_my_certs = parsed_args.staging = True
|
||||
|
||||
return parsed_args
|
||||
@@ -1168,6 +1398,8 @@ def prepare_and_parse_args(plugins, args):
|
||||
# parser (--help should display plugin-specific options last)
|
||||
_plugins_parsing(helpful, plugins)
|
||||
|
||||
global _parser # pylint: disable=global-statement
|
||||
_parser = helpful
|
||||
return helpful.parse_args()
|
||||
|
||||
|
||||
@@ -1389,7 +1621,7 @@ def setup_log_file_handler(config, logfile, fmt):
|
||||
|
||||
|
||||
def _cli_log_handler(config, level, fmt):
|
||||
if config.text_mode or config.noninteractive_mode:
|
||||
if config.text_mode or config.noninteractive_mode or config.verb == "renew":
|
||||
handler = colored_logging.StreamHandler()
|
||||
handler.setFormatter(logging.Formatter(fmt))
|
||||
else:
|
||||
@@ -1506,6 +1738,9 @@ def main(cli_args=sys.argv[1:]):
|
||||
displayer = display_util.NoninteractiveDisplay(sys.stdout)
|
||||
elif config.text_mode:
|
||||
displayer = display_util.FileDisplay(sys.stdout)
|
||||
elif config.verb == "renew":
|
||||
config.noninteractive_mode = True
|
||||
displayer = display_util.NoninteractiveDisplay(sys.stdout)
|
||||
else:
|
||||
displayer = display_util.NcursesDisplay()
|
||||
zope.component.provideUtility(displayer)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Let's Encrypt user-supplied configuration."""
|
||||
import copy
|
||||
import os
|
||||
import urlparse
|
||||
|
||||
@@ -78,6 +79,12 @@ class NamespaceConfig(object):
|
||||
return os.path.join(
|
||||
self.namespace.work_dir, constants.TEMP_CHECKPOINT_DIR)
|
||||
|
||||
def __deepcopy__(self, _memo):
|
||||
# Work around https://bugs.python.org/issue1515 for py26 tests :( :(
|
||||
# https://travis-ci.org/letsencrypt/letsencrypt/jobs/106900743#L3276
|
||||
new_ns = copy.deepcopy(self.namespace)
|
||||
return type(self)(new_ns)
|
||||
|
||||
|
||||
class RenewerConfiguration(object):
|
||||
"""Configuration wrapper for renewer."""
|
||||
|
||||
@@ -37,7 +37,9 @@ STAGING_URI = "https://acme-staging.api.letsencrypt.org/directory"
|
||||
RENEWER_DEFAULTS = dict(
|
||||
renewer_enabled="yes",
|
||||
renew_before_expiry="30 days",
|
||||
deploy_before_expiry="20 days",
|
||||
# This value should ensure that there is never a deployment delay by
|
||||
# default.
|
||||
deploy_before_expiry="99 years",
|
||||
)
|
||||
"""Defaults for renewer script."""
|
||||
|
||||
|
||||
@@ -91,7 +91,8 @@ s.serve_forever()" """
|
||||
help="Automatically allows public IP logging.")
|
||||
|
||||
def prepare(self): # pylint: disable=missing-docstring,no-self-use
|
||||
pass # pragma: no cover
|
||||
if self.config.noninteractive_mode:
|
||||
raise errors.PluginError("Running manual mode non-interactively is not supported")
|
||||
|
||||
def more_info(self): # pylint: disable=missing-docstring,no-self-use
|
||||
return ("This plugin requires user's manual intervention in setting "
|
||||
|
||||
@@ -769,6 +769,9 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
|
||||
self.cli_config = cli_config
|
||||
target_version = self.next_free_version()
|
||||
archive = self.cli_config.archive_dir
|
||||
# XXX if anyone ever moves a renewal configuration file, this will
|
||||
# break... perhaps prefix should be the dirname of the previous
|
||||
# cert.pem?
|
||||
prefix = os.path.join(archive, self.lineagename)
|
||||
target = dict(
|
||||
[(kind,
|
||||
|
||||
@@ -530,35 +530,52 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
self.assertRaises(errors.Error,
|
||||
self._certonly_new_request_common, mock_client)
|
||||
|
||||
def _test_certonly_renewal_common(self, renewal_verb, extra_args=None):
|
||||
def _test_renewal_common(self, due_for_renewal, extra_args, log_out=None,
|
||||
args=None, renew=True):
|
||||
# pylint: disable=too-many-locals
|
||||
cert_path = 'letsencrypt/tests/testdata/cert.pem'
|
||||
chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem'
|
||||
mock_lineage = mock.MagicMock(cert=cert_path, fullchain=chain_path)
|
||||
mock_lineage.should_autorenew.return_value = due_for_renewal
|
||||
mock_certr = mock.MagicMock()
|
||||
mock_key = mock.MagicMock(pem='pem_key')
|
||||
with mock.patch('letsencrypt.cli._treat_as_renewal') as mock_renewal:
|
||||
mock_renewal.return_value = (renewal_verb, mock_lineage)
|
||||
mock_client = mock.MagicMock()
|
||||
mock_client.obtain_certificate.return_value = (mock_certr, 'chain',
|
||||
mock_key, 'csr')
|
||||
mock_client = mock.MagicMock()
|
||||
mock_client.obtain_certificate.return_value = (mock_certr, 'chain',
|
||||
mock_key, 'csr')
|
||||
with mock.patch('letsencrypt.cli._find_duplicative_certs') as mock_fdc:
|
||||
mock_fdc.return_value = (mock_lineage, None)
|
||||
with mock.patch('letsencrypt.cli._init_le_client') as mock_init:
|
||||
mock_init.return_value = mock_client
|
||||
get_utility_path = 'letsencrypt.cli.zope.component.getUtility'
|
||||
with mock.patch(get_utility_path) as mock_get_utility:
|
||||
with mock.patch('letsencrypt.cli.OpenSSL'):
|
||||
with mock.patch('letsencrypt.cli.OpenSSL') as mock_ssl:
|
||||
mock_latest = mock.MagicMock()
|
||||
mock_latest.get_issuer.return_value = "Fake fake"
|
||||
mock_ssl.crypto.load_certificate.return_value = mock_latest
|
||||
with mock.patch('letsencrypt.cli.crypto_util'):
|
||||
args = ['-d', 'foo.bar', '-a',
|
||||
'standalone', 'certonly']
|
||||
if not args:
|
||||
args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly']
|
||||
if extra_args:
|
||||
args += extra_args
|
||||
self._call(args)
|
||||
|
||||
mock_client.obtain_certificate.assert_called_once_with(['foo.bar'])
|
||||
try:
|
||||
if log_out:
|
||||
with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf:
|
||||
self.assertTrue(log_out in lf.read())
|
||||
|
||||
if renew:
|
||||
mock_client.obtain_certificate.assert_called_once_with(['isnot.org'])
|
||||
else:
|
||||
self.assertEqual(mock_client.obtain_certificate.call_count, 0)
|
||||
except:
|
||||
self._dump_log()
|
||||
raise
|
||||
|
||||
return mock_lineage, mock_get_utility
|
||||
|
||||
def test_certonly_renewal(self):
|
||||
lineage, get_utility = self._test_certonly_renewal_common('renew')
|
||||
lineage, get_utility = self._test_renewal_common(True, [])
|
||||
self.assertEqual(lineage.save_successor.call_count, 1)
|
||||
lineage.update_all_links_to.assert_called_once_with(
|
||||
lineage.latest_common_version())
|
||||
@@ -566,12 +583,38 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
self.assertTrue('fullchain.pem' in cert_msg)
|
||||
self.assertTrue('donate' in get_utility().add_message.call_args[0][0])
|
||||
|
||||
def test_certonly_dry_run_reinstall_is_renewal(self):
|
||||
_, get_utility = self._test_certonly_renewal_common('reinstall',
|
||||
['--dry-run'])
|
||||
def test_certonly_renewal_triggers(self):
|
||||
# --dry-run should force renewal
|
||||
_, get_utility = self._test_renewal_common(False, ['--dry-run', '--keep'],
|
||||
log_out="simulating renewal")
|
||||
self.assertEqual(get_utility().add_message.call_count, 1)
|
||||
self.assertTrue('dry run' in get_utility().add_message.call_args[0][0])
|
||||
|
||||
_, _ = self._test_renewal_common(False, ['--renew-by-default', '-tvv', '--debug'],
|
||||
log_out="Auto-renewal forced")
|
||||
self.assertEqual(get_utility().add_message.call_count, 1)
|
||||
|
||||
_, _ = self._test_renewal_common(False, ['-tvv', '--debug', '--keep'],
|
||||
log_out="not yet due", renew=False)
|
||||
|
||||
def _dump_log(self):
|
||||
with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf:
|
||||
print "Logs:"
|
||||
print lf.read()
|
||||
|
||||
|
||||
def test_renewal_verb(self):
|
||||
with open(test_util.vector_path('sample-renewal.conf')) as src:
|
||||
# put the correct path for cert.pem, chain.pem etc in the renewal conf
|
||||
renewal_conf = src.read().replace("MAGICDIR", test_util.vector_path())
|
||||
rd = os.path.join(self.config_dir, "renewal")
|
||||
os.makedirs(rd)
|
||||
rc = os.path.join(rd, "sample-renewal.conf")
|
||||
with open(rc, "w") as dest:
|
||||
dest.write(renewal_conf)
|
||||
args = ["renew", "--dry-run", "-tvv"]
|
||||
self._test_renewal_common(True, [], args=args, renew=True)
|
||||
|
||||
@mock.patch('letsencrypt.cli.zope.component.getUtility')
|
||||
@mock.patch('letsencrypt.cli._treat_as_renewal')
|
||||
@mock.patch('letsencrypt.cli._init_le_client')
|
||||
|
||||
28
letsencrypt/tests/testdata/archive/sample-renewal/cert1.pem
vendored
Normal file
28
letsencrypt/tests/testdata/archive/sample-renewal/cert1.pem
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE1DCCA7ygAwIBAgITAPoz/CBluNQV/Eh9F+CS6dSxEDANBgkqhkiG9w0BAQsF
|
||||
ADAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTAeFw0xNjAyMDIyMzQ5
|
||||
MDBaFw0xNjA1MDIyMzQ5MDBaMBQxEjAQBgNVBAMTCWlzbm90Lm9yZzCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALyudqLKcIdWZ5VaK1fuhlEDbZtvs2E+
|
||||
slm4dmSS1nFve7MdlZ69K0gdtnhkiPQ0wGQTligeDZ8fY8iL87GZO0tp5f7S+QJN
|
||||
NYCiYw6j4qp5JBy/zG22kJz1Quu7/vXMYLzLvK6x6YixiWAWyqqvlUVBLS1r4W3h
|
||||
A5Z+F1EIsXeyz7TJe3lAzIWAAxpfH9OviIz2rEDotuCdU771USLLNSw4qJojNlTx
|
||||
UpZG6lGFs8KGb8tqROXknaMKE4PvN3SITixSUTFbktt1Wz60moWbNdLMKvgkzuUP
|
||||
r4viO2P4SO5slNAY0ZeEssPpVAelN3EvrAcEZtoKmG5fnQDVo8uVag0CAwEAAaOC
|
||||
AhIwggIOMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
|
||||
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUqhI4u6aaPrcYQnmypxV8Tap8
|
||||
L54wHwYDVR0jBBgwFoAU+3hPEvlgFYMsnxd/NBmzLjbqQYkweAYIKwYBBQUHAQEE
|
||||
bDBqMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5zdGFnaW5nLXgxLmxldHNlbmNy
|
||||
eXB0Lm9yZy8wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9jZXJ0LnN0YWdpbmcteDEubGV0
|
||||
c2VuY3J5cHQub3JnLzAUBgNVHREEDTALgglpc25vdC5vcmcwgf4GA1UdIASB9jCB
|
||||
8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRw
|
||||
Oi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENl
|
||||
cnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFy
|
||||
dGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRl
|
||||
IFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0
|
||||
b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAbAhX6FfQwELayneY4l5RvYSdw/Jj5CRy
|
||||
KzrM7ISld7x9YPpxX6Pmht/YyMhLWrtxvFUR2+RNhSIYB8IjQEjmKjvR7UNeiUve
|
||||
jzPEAuTg/9m3i0FJpPHc2aKGzlLFQCMm5/RrvnXI6ljIcyhocLvMiN46iexcExI2
|
||||
Ese3w8GoH6wARYKxU/QBexfoXQLgtAbYzNRE6EgKWtB+txV+7+d2MgbhCEit5VwU
|
||||
+ydT8inp9URsA7iKM03hDdGOBysddkrm1/yEhVy/Oo6bT9WMAUHVvz61hHekWcSf
|
||||
rAQ6BayubvWOUx06eTowXr1gln/rl+WXOxcsJeag127NuhmHOCXZxQ==
|
||||
-----END CERTIFICATE-----
|
||||
19
letsencrypt/tests/testdata/archive/sample-renewal/chain1.pem
vendored
Normal file
19
letsencrypt/tests/testdata/archive/sample-renewal/chain1.pem
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfmgAwIBAgIJAJzxkS6o1QkIMA0GCSqGSIb3DQEBCwUAMB8xHTAbBgNV
|
||||
BAMMFGhhcHB5IGhhY2tlciBmYWtlIENBMB4XDTE1MDQwNzIzNTAzOFoXDTI1MDQw
|
||||
NDIzNTAzOFowHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCCkd5mgXFErJ3F2M0E9dw+Ta/md5i
|
||||
8TDId01HberAApqmydG7UZYF3zLTSzNjlNSOmtybvrSGUnZ9r9tSQcL8VM6WUOM8
|
||||
tnIpiIjEA2QkBycMwvRmZ/B2ltPdYs/R9BqNwO1g18GDZrHSzUYtNKNeFI6Glamj
|
||||
7GK2Vr0SmiEamlNIR5ktAFsEErzf/d4jCF7sosMsJpMCm1p58QkP4LHLShVLXDa8
|
||||
BMfVoI+ipYcA08iNUFkgW8VWDclIDxcysa0psDDtMjX3+4aPkE/cefmP+1xOfUuD
|
||||
HOGV8XFynsP4EpTfVOZr0/g9gYQ7ZArqXX7GTQkFqduwPm/w5qxSPTarAgMBAAGj
|
||||
UDBOMB0GA1UdDgQWBBT7eE8S+WAVgyyfF380GbMuNupBiTAfBgNVHSMEGDAWgBT7
|
||||
eE8S+WAVgyyfF380GbMuNupBiTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQAd9Da+Zv+TjMv7NTAmliqnWHY6d3UxEZN3hFEJ58IQVHbBZVZdW7zhRktB
|
||||
vR05Kweac0HJeK91TKmzvXl21IXLvh0gcNLU/uweD3no/snfdB4OoFompljThmgl
|
||||
zBqiqWoKBJQrLCA8w5UB+ReomRYd/EYXF/6TAfzm6hr//Xt5mPiUHPdvYt75lMAo
|
||||
vRxLSbF8TSQ6b7BYxISWjPgFASNNqJNHEItWsmQMtAjjwzb9cs01XH9pChVAWn9L
|
||||
oeMKa+SlHSYrWG93+EcrIH/dGU76uNOiaDzBSKvaehG53h25MHuO1anNICJvZovW
|
||||
rFo4Uv1EnkKJm3vJFe50eJGhEKlx
|
||||
-----END CERTIFICATE-----
|
||||
47
letsencrypt/tests/testdata/archive/sample-renewal/fullchain1.pem
vendored
Normal file
47
letsencrypt/tests/testdata/archive/sample-renewal/fullchain1.pem
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE1DCCA7ygAwIBAgITAPoz/CBluNQV/Eh9F+CS6dSxEDANBgkqhkiG9w0BAQsF
|
||||
ADAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTAeFw0xNjAyMDIyMzQ5
|
||||
MDBaFw0xNjA1MDIyMzQ5MDBaMBQxEjAQBgNVBAMTCWlzbm90Lm9yZzCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALyudqLKcIdWZ5VaK1fuhlEDbZtvs2E+
|
||||
slm4dmSS1nFve7MdlZ69K0gdtnhkiPQ0wGQTligeDZ8fY8iL87GZO0tp5f7S+QJN
|
||||
NYCiYw6j4qp5JBy/zG22kJz1Quu7/vXMYLzLvK6x6YixiWAWyqqvlUVBLS1r4W3h
|
||||
A5Z+F1EIsXeyz7TJe3lAzIWAAxpfH9OviIz2rEDotuCdU771USLLNSw4qJojNlTx
|
||||
UpZG6lGFs8KGb8tqROXknaMKE4PvN3SITixSUTFbktt1Wz60moWbNdLMKvgkzuUP
|
||||
r4viO2P4SO5slNAY0ZeEssPpVAelN3EvrAcEZtoKmG5fnQDVo8uVag0CAwEAAaOC
|
||||
AhIwggIOMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
|
||||
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUqhI4u6aaPrcYQnmypxV8Tap8
|
||||
L54wHwYDVR0jBBgwFoAU+3hPEvlgFYMsnxd/NBmzLjbqQYkweAYIKwYBBQUHAQEE
|
||||
bDBqMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5zdGFnaW5nLXgxLmxldHNlbmNy
|
||||
eXB0Lm9yZy8wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9jZXJ0LnN0YWdpbmcteDEubGV0
|
||||
c2VuY3J5cHQub3JnLzAUBgNVHREEDTALgglpc25vdC5vcmcwgf4GA1UdIASB9jCB
|
||||
8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRw
|
||||
Oi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENl
|
||||
cnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFy
|
||||
dGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRl
|
||||
IFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0
|
||||
b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAbAhX6FfQwELayneY4l5RvYSdw/Jj5CRy
|
||||
KzrM7ISld7x9YPpxX6Pmht/YyMhLWrtxvFUR2+RNhSIYB8IjQEjmKjvR7UNeiUve
|
||||
jzPEAuTg/9m3i0FJpPHc2aKGzlLFQCMm5/RrvnXI6ljIcyhocLvMiN46iexcExI2
|
||||
Ese3w8GoH6wARYKxU/QBexfoXQLgtAbYzNRE6EgKWtB+txV+7+d2MgbhCEit5VwU
|
||||
+ydT8inp9URsA7iKM03hDdGOBysddkrm1/yEhVy/Oo6bT9WMAUHVvz61hHekWcSf
|
||||
rAQ6BayubvWOUx06eTowXr1gln/rl+WXOxcsJeag127NuhmHOCXZxQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfmgAwIBAgIJAJzxkS6o1QkIMA0GCSqGSIb3DQEBCwUAMB8xHTAbBgNV
|
||||
BAMMFGhhcHB5IGhhY2tlciBmYWtlIENBMB4XDTE1MDQwNzIzNTAzOFoXDTI1MDQw
|
||||
NDIzNTAzOFowHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCCkd5mgXFErJ3F2M0E9dw+Ta/md5i
|
||||
8TDId01HberAApqmydG7UZYF3zLTSzNjlNSOmtybvrSGUnZ9r9tSQcL8VM6WUOM8
|
||||
tnIpiIjEA2QkBycMwvRmZ/B2ltPdYs/R9BqNwO1g18GDZrHSzUYtNKNeFI6Glamj
|
||||
7GK2Vr0SmiEamlNIR5ktAFsEErzf/d4jCF7sosMsJpMCm1p58QkP4LHLShVLXDa8
|
||||
BMfVoI+ipYcA08iNUFkgW8VWDclIDxcysa0psDDtMjX3+4aPkE/cefmP+1xOfUuD
|
||||
HOGV8XFynsP4EpTfVOZr0/g9gYQ7ZArqXX7GTQkFqduwPm/w5qxSPTarAgMBAAGj
|
||||
UDBOMB0GA1UdDgQWBBT7eE8S+WAVgyyfF380GbMuNupBiTAfBgNVHSMEGDAWgBT7
|
||||
eE8S+WAVgyyfF380GbMuNupBiTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQAd9Da+Zv+TjMv7NTAmliqnWHY6d3UxEZN3hFEJ58IQVHbBZVZdW7zhRktB
|
||||
vR05Kweac0HJeK91TKmzvXl21IXLvh0gcNLU/uweD3no/snfdB4OoFompljThmgl
|
||||
zBqiqWoKBJQrLCA8w5UB+ReomRYd/EYXF/6TAfzm6hr//Xt5mPiUHPdvYt75lMAo
|
||||
vRxLSbF8TSQ6b7BYxISWjPgFASNNqJNHEItWsmQMtAjjwzb9cs01XH9pChVAWn9L
|
||||
oeMKa+SlHSYrWG93+EcrIH/dGU76uNOiaDzBSKvaehG53h25MHuO1anNICJvZovW
|
||||
rFo4Uv1EnkKJm3vJFe50eJGhEKlx
|
||||
-----END CERTIFICATE-----
|
||||
28
letsencrypt/tests/testdata/archive/sample-renewal/privkey1.pem
vendored
Normal file
28
letsencrypt/tests/testdata/archive/sample-renewal/privkey1.pem
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8rnaiynCHVmeV
|
||||
WitX7oZRA22bb7NhPrJZuHZkktZxb3uzHZWevStIHbZ4ZIj0NMBkE5YoHg2fH2PI
|
||||
i/OxmTtLaeX+0vkCTTWAomMOo+KqeSQcv8xttpCc9ULru/71zGC8y7yusemIsYlg
|
||||
Fsqqr5VFQS0ta+Ft4QOWfhdRCLF3ss+0yXt5QMyFgAMaXx/Tr4iM9qxA6LbgnVO+
|
||||
9VEiyzUsOKiaIzZU8VKWRupRhbPChm/LakTl5J2jChOD7zd0iE4sUlExW5LbdVs+
|
||||
tJqFmzXSzCr4JM7lD6+L4jtj+EjubJTQGNGXhLLD6VQHpTdxL6wHBGbaCphuX50A
|
||||
1aPLlWoNAgMBAAECggEAfKKWFWS6PnwSAnNErFoQeZVVItb/XB5JO8EA2+CvLNFi
|
||||
mefR/MCixYlzDkYCvaXW7ISPrMJlZxYaGNBx0oAQzfkPB2wfNqj/zY/29SXGxast
|
||||
8puzk0mEb1oHsaZGfeFaiXvfkFpPlI8J2uJTT7qaVNv/1sArciSv9QonpsyiRhlB
|
||||
yqT49juNVoR1tJHyXzkkRfHKTG8OlJd4kuFOl3fM9dTFPQ/ft0kTNAQ/B4SFvSwF
|
||||
RJsbLbsbFGsUdV9ekE6UX6oWD/Ah707rvgtCyS0Bc+0O3t2EKwmm3RXPRUMHCVxE
|
||||
bKdTxRB4etbjMVXMuVhB8Y4GbfrtMCy+qxZQ6znCAQKBgQDr7bcYAZVZp/nBMVB+
|
||||
lBO9w73J6lnEWm6bZ9728KlGAKETaRhxZQSi6TN6MWwNwnk6rinyz4uVwVr9ZRCs
|
||||
WkB1TbvW0JNcWdr3YClwsKXAt8X22bjGe0LagDJHG6r1TPS+MdovOS2M6IMaxlbT
|
||||
rzFhSJ8ojLX3tqnOsmc7YAFLjQKBgQDMu8E9hoJt82lQzOGrjHmGzGEu2GLx9WKO
|
||||
e4nkj335kX6fIhMMqSXBFbTJZwXoYvk5J8ZnaARbYG0m5nxDCwRjX5HWa8q0B2Po
|
||||
ta53w01sKKznzlPjUhsdhEthun7MCFfLZpgvcZ9xVzOXo3/Zfn2+RrsPSjrVDqBy
|
||||
hj+k5mW4gQKBgHFWKf3LTO7cBdvsD8ou4mjn7nVgMi1kb/wR4wdnxzmMtdR4STi4
|
||||
GYkVVBhgQ5M8mDY7UoWFdH3FfCt8cI0Lcimn5ROl8RSNSeZKeL3c7lNtNRmHr/8R
|
||||
WaVTrlOAlBjxFiWEF1dWNW6ah9jF7RIV+DfOxj6ZkhTk2CAmjfb1AMpFAoGABf96
|
||||
KdNG/vGipDtcYSo8ZTaXoke0nmISARqdb5TEnAsnKoJVDInoEUARi9T411YO9x2z
|
||||
MlRZzFOG3xzhhxVLi53BKAcAaUXOJ4MrGVcfbYvDhQcGbiJ5qOO3UaWlEVUtPUhE
|
||||
LR+nDCsB1+9yT2zlQi3QTSJflt5W1QQZ2TrmwAECgYEAvQ7+sTcHs1K9yKj7koEu
|
||||
A19FbMA0IwvrVRcV/VqmlsoW6e6wW2YND+GtaDbKdD0aBPivqLJwpNFrsRA+W0iB
|
||||
vzmML6sKhhL+j7tjSgq+iQdBkKz0j9PyReuhe9CRnljMmyun+4qKEk0KUvxBrjPY
|
||||
Skn+ML18qyUoEPnmbpfHxCs=
|
||||
-----END PRIVATE KEY-----
|
||||
1
letsencrypt/tests/testdata/live/sample-renewal/cert.pem
vendored
Symbolic link
1
letsencrypt/tests/testdata/live/sample-renewal/cert.pem
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../archive/sample-renewal/cert1.pem
|
||||
1
letsencrypt/tests/testdata/live/sample-renewal/chain.pem
vendored
Symbolic link
1
letsencrypt/tests/testdata/live/sample-renewal/chain.pem
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../archive/sample-renewal/chain1.pem
|
||||
1
letsencrypt/tests/testdata/live/sample-renewal/fullchain.pem
vendored
Symbolic link
1
letsencrypt/tests/testdata/live/sample-renewal/fullchain.pem
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../archive/sample-renewal/fullchain1.pem
|
||||
1
letsencrypt/tests/testdata/live/sample-renewal/privkey.pem
vendored
Symbolic link
1
letsencrypt/tests/testdata/live/sample-renewal/privkey.pem
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../archive/sample-renewal/privkey1.pem
|
||||
76
letsencrypt/tests/testdata/sample-renewal.conf
vendored
Executable file
76
letsencrypt/tests/testdata/sample-renewal.conf
vendored
Executable file
@@ -0,0 +1,76 @@
|
||||
cert = MAGICDIR/live/sample-renewal/cert.pem
|
||||
privkey = MAGICDIR/live/sample-renewal/privkey.pem
|
||||
chain = MAGICDIR/live/sample-renewal/chain.pem
|
||||
fullchain = MAGICDIR/live/sample-renewal/fullchain.pem
|
||||
renew_before_expiry = 1 year
|
||||
|
||||
# Options and defaults used in the renewal process
|
||||
[renewalparams]
|
||||
no_self_upgrade = False
|
||||
apache_enmod = a2enmod
|
||||
no_verify_ssl = False
|
||||
ifaces = None
|
||||
apache_dismod = a2dismod
|
||||
register_unsafely_without_email = False
|
||||
apache_handle_modules = True
|
||||
uir = None
|
||||
installer = none
|
||||
nginx_ctl = nginx
|
||||
config_dir = MAGICDIR
|
||||
text_mode = False
|
||||
func = <function obtain_cert at 0x7f093a163c08>
|
||||
staging = True
|
||||
prepare = False
|
||||
work_dir = /var/lib/letsencrypt
|
||||
tos = False
|
||||
init = False
|
||||
http01_port = 80
|
||||
duplicate = False
|
||||
noninteractive_mode = True
|
||||
key_path = None
|
||||
nginx = False
|
||||
nginx_server_root = /etc/nginx
|
||||
fullchain_path = /home/ubuntu/letsencrypt/chain.pem
|
||||
email = None
|
||||
csr = None
|
||||
agree_dev_preview = None
|
||||
redirect = None
|
||||
verb = certonly
|
||||
verbose_count = -3
|
||||
config_file = None
|
||||
renew_by_default = False
|
||||
hsts = False
|
||||
apache_handle_sites = True
|
||||
authenticator = standalone
|
||||
domains = isnot.org,
|
||||
rsa_key_size = 2048
|
||||
apache_challenge_location = /etc/apache2
|
||||
checkpoints = 1
|
||||
manual_test_mode = False
|
||||
apache = False
|
||||
cert_path = /home/ubuntu/letsencrypt/cert.pem
|
||||
webroot_path = None
|
||||
reinstall = False
|
||||
expand = False
|
||||
strict_permissions = False
|
||||
apache_server_root = /etc/apache2
|
||||
account = None
|
||||
dry_run = False
|
||||
manual_public_ip_logging_ok = False
|
||||
chain_path = /home/ubuntu/letsencrypt/chain.pem
|
||||
break_my_certs = False
|
||||
standalone = True
|
||||
manual = False
|
||||
server = https://acme-staging.api.letsencrypt.org/directory
|
||||
standalone_supported_challenges = "tls-sni-01,http-01"
|
||||
webroot = False
|
||||
os_packages_only = False
|
||||
apache_init_script = None
|
||||
user_agent = None
|
||||
apache_le_vhost_ext = -le-ssl.conf
|
||||
debug = False
|
||||
tls_sni_01_port = 443
|
||||
logs_dir = /var/log/letsencrypt
|
||||
apache_vhost_root = /etc/apache2/sites-available
|
||||
configurator = None
|
||||
[[webroot_map]]
|
||||
@@ -27,6 +27,13 @@ common() {
|
||||
"$@"
|
||||
}
|
||||
|
||||
common_no_force_renew() {
|
||||
letsencrypt_test_no_force_renew \
|
||||
--authenticator standalone \
|
||||
--installer null \
|
||||
"$@"
|
||||
}
|
||||
|
||||
common --domains le1.wtf --standalone-supported-challenges tls-sni-01 auth
|
||||
common --domains le2.wtf --standalone-supported-challenges http-01 run
|
||||
common -a manual -d le.wtf auth
|
||||
@@ -44,6 +51,22 @@ common --domains le3.wtf install \
|
||||
--cert-path "${root}/csr/cert.pem" \
|
||||
--key-path "${root}/csr/key.pem"
|
||||
|
||||
# This won't renew (because it's not time yet)
|
||||
letsencrypt_test_no_force_renew --authenticator standalone --installer null renew
|
||||
|
||||
# This will renew because the expiry is less than 10 years from now
|
||||
sed -i "4arenew_before_expiry = 10 years" "$root/conf/renewal/le1.wtf.conf"
|
||||
letsencrypt_test_no_force_renew --authenticator standalone --installer null renew
|
||||
|
||||
ls "$root/conf/archive/le1.wtf"
|
||||
# dir="$root/conf/archive/le1.wtf"
|
||||
# for x in cert chain fullchain privkey;
|
||||
# do
|
||||
# latest="$(ls -1t $dir/ | grep -e "^${x}" | head -n1)"
|
||||
# live="$($readlink -f "$root/conf/live/le1.wtf/${x}.pem")"
|
||||
# [ "${dir}/${latest}" = "$live" ] # renewer fails this test
|
||||
# done
|
||||
|
||||
# revoke by account key
|
||||
common revoke --cert-path "$root/conf/live/le.wtf/cert.pem"
|
||||
# revoke renewed
|
||||
|
||||
@@ -28,3 +28,20 @@ letsencrypt_test () {
|
||||
-vvvvvvv \
|
||||
"$@"
|
||||
}
|
||||
|
||||
letsencrypt_test_no_force_renew () {
|
||||
letsencrypt \
|
||||
--server "${SERVER:-http://localhost:4000/directory}" \
|
||||
--no-verify-ssl \
|
||||
--tls-sni-01-port 5001 \
|
||||
--http-01-port 5002 \
|
||||
--manual-test-mode \
|
||||
$store_flags \
|
||||
--text \
|
||||
--no-redirect \
|
||||
--agree-tos \
|
||||
--register-unsafely-without-email \
|
||||
--debug \
|
||||
-vvvvvvv \
|
||||
"$@"
|
||||
}
|
||||
|
||||
55
tests/letstest/scripts/test_renew_standalone.sh
Executable file
55
tests/letstest/scripts/test_renew_standalone.sh
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
# $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL
|
||||
# are dynamically set at execution
|
||||
|
||||
# run letsencrypt-apache2 via letsencrypt-auto
|
||||
cd letsencrypt
|
||||
|
||||
export SUDO=sudo
|
||||
if [ -f /etc/debian_version ] ; then
|
||||
echo "Bootstrapping dependencies for Debian-based OSes..."
|
||||
$SUDO bootstrap/_deb_common.sh
|
||||
elif [ -f /etc/redhat-release ] ; then
|
||||
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
||||
$SUDO bootstrap/_rpm_common.sh
|
||||
else
|
||||
echo "Dont have bootstrapping for this OS!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bootstrap/dev/venv.sh
|
||||
sudo venv/bin/letsencrypt certonly --debug --standalone -t --agree-dev-preview --agree-tos \
|
||||
--renew-by-default --redirect --register-unsafely-without-email \
|
||||
--domain $PUBLIC_HOSTNAME --server $BOULDER_URL -v
|
||||
if [ $? -ne 0 ] ; then
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
if [ "$OS_TYPE" = "ubuntu" ] ; then
|
||||
venv/bin/tox -e apacheconftest
|
||||
else
|
||||
echo Not running hackish apache tests on $OS_TYPE
|
||||
fi
|
||||
|
||||
if [ $? -ne 0 ] ; then
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
sudo venv/bin/letsencrypt renew --renew-by-default
|
||||
|
||||
if [ $? -ne 0 ] ; then
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
|
||||
ls /etc/letsencrypt/archive/$PUBLIC_HOSTNAME | grep -q 2.pem
|
||||
|
||||
if [ $? -ne 0 ] ; then
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
# return error if any of the subtests failed
|
||||
if [ "$FAIL" = 1 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user