Example #1
0
def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-branches
    """Does the user want to delete their now-revoked certs? If run in non-interactive mode,
    deleting happens automatically.

    :param config: parsed command line arguments
    :type config: interfaces.IConfig

    :returns: `None`
    :rtype: None

    :raises errors.Error: If anything goes wrong, including bad user input, if an overlapping
        archive dir is found for the specified lineage, etc ...
    """
    display = zope.component.getUtility(interfaces.IDisplay)
    reporter_util = zope.component.getUtility(interfaces.IReporter)

    attempt_deletion = config.delete_after_revoke
    if attempt_deletion is None:
        msg = ("Would you like to delete the cert(s) you just revoked, along with all earlier and "
            "later versions of the cert?")
        attempt_deletion = display.yesno(msg, yes_label="Yes (recommended)", no_label="No",
                force_interactive=True, default=True)

    if not attempt_deletion:
        reporter_util.add_message("Not deleting revoked certs.", reporter_util.LOW_PRIORITY)
        return

    # config.cert_path must have been set
    # config.certname may have been set
    assert config.cert_path

    if not config.certname:
        config.certname = cert_manager.cert_path_to_lineage(config)

    # don't delete if the archive_dir is used by some other lineage
    archive_dir = storage.full_archive_path(
            configobj.ConfigObj(storage.renewal_file_for_certname(config, config.certname)),
            config, config.certname)
    try:
        cert_manager.match_and_check_overlaps(config, [lambda x: archive_dir],
            lambda x: x.archive_dir, lambda x: x)
    except errors.OverlappingMatchFound:
        msg = ('Not deleting revoked certs due to overlapping archive dirs. More than '
                'one lineage is using {0}'.format(archive_dir))
        reporter_util.add_message(''.join(msg), reporter_util.MEDIUM_PRIORITY)
        return
    except Exception as e:
        msg = ('config.default_archive_dir: {0}, config.live_dir: {1}, archive_dir: {2},'
        'original exception: {3}')
        msg = msg.format(config.default_archive_dir, config.live_dir, archive_dir, e)
        raise errors.Error(msg)

    cert_manager.delete(config)
Example #2
0
def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-branches
    """Does the user want to delete their now-revoked certs? If run in non-interactive mode,
    deleting happens automatically.

    :param config: parsed command line arguments
    :type config: interfaces.IConfig

    :returns: `None`
    :rtype: None

    :raises errors.Error: If anything goes wrong, including bad user input, if an overlapping
        archive dir is found for the specified lineage, etc ...
    """
    display = zope.component.getUtility(interfaces.IDisplay)
    reporter_util = zope.component.getUtility(interfaces.IReporter)

    attempt_deletion = config.delete_after_revoke
    if attempt_deletion is None:
        msg = ("Would you like to delete the cert(s) you just revoked, along with all earlier and "
            "later versions of the cert?")
        attempt_deletion = display.yesno(msg, yes_label="Yes (recommended)", no_label="No",
                force_interactive=True, default=True)

    if not attempt_deletion:
        reporter_util.add_message("Not deleting revoked certs.", reporter_util.LOW_PRIORITY)
        return

    # config.cert_path must have been set
    # config.certname may have been set
    assert config.cert_path

    if not config.certname:
        config.certname = cert_manager.cert_path_to_lineage(config)

    # don't delete if the archive_dir is used by some other lineage
    archive_dir = storage.full_archive_path(
            configobj.ConfigObj(storage.renewal_file_for_certname(config, config.certname)),
            config, config.certname)
    try:
        cert_manager.match_and_check_overlaps(config, [lambda x: archive_dir],
            lambda x: x.archive_dir, lambda x: x)
    except errors.OverlappingMatchFound:
        msg = ('Not deleting revoked certs due to overlapping archive dirs. More than '
                'one lineage is using {0}'.format(archive_dir))
        reporter_util.add_message(''.join(msg), reporter_util.MEDIUM_PRIORITY)
        return
    except Exception as e:
        msg = ('config.default_archive_dir: {0}, config.live_dir: {1}, archive_dir: {2},'
        'original exception: {3}')
        msg = msg.format(config.default_archive_dir, config.live_dir, archive_dir, e)
        raise errors.Error(msg)

    cert_manager.delete(config)
Example #3
0
def lineage_for_certname(cli_config, certname):
    """Find a lineage object with name certname."""
    configs_dir = cli_config.renewal_configs_dir
    # Verify the directory is there
    util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())
    renewal_file = storage.renewal_file_for_certname(cli_config, certname)
    try:
        return storage.RenewableCert(renewal_file, cli_config)
    except (errors.CertStorageError, IOError):
        logger.debug("Renewal conf file %s is broken.", renewal_file)
        logger.debug("Traceback was:\n%s", traceback.format_exc())
        return None
Example #4
0
def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-branches
    """Does the user want to delete their now-revoked certs? If run in non-interactive mode,
    deleting happens automatically, unless if both `--cert-name` and `--cert-path` were
    specified with conflicting values.

    :param config: parsed command line arguments
    :type config: interfaces.IConfig

    :returns: `None`
    :rtype: None

    :raises errors.Error: If anything goes wrong, including bad user input, if an overlapping
        archive dir is found for the specified lineage, etc ...
    """
    display = zope.component.getUtility(interfaces.IDisplay)
    reporter_util = zope.component.getUtility(interfaces.IReporter)

    attempt_deletion = config.delete_after_revoke
    if attempt_deletion is None:
        msg = ("Would you like to delete the cert(s) you just revoked?")
        attempt_deletion = display.yesno(msg, yes_label="Yes (recommended)", no_label="No",
                force_interactive=True, default=True)

    if not attempt_deletion:
        reporter_util.add_message("Not deleting revoked certs.", reporter_util.LOW_PRIORITY)
        return

    if not (config.certname or config.cert_path):
        raise errors.Error('At least one of --cert-path or --cert-name must be specified.')

    if config.certname and config.cert_path:
        # first, check if certname and cert_path imply the same certs
        implied_cert_name = cert_manager.cert_path_to_lineage(config)

        if implied_cert_name != config.certname:
            cert_path_implied_cert_name = cert_manager.cert_path_to_lineage(config)
            cert_path_implied_conf = storage.renewal_file_for_certname(config,
                    cert_path_implied_cert_name)
            cert_path_cert = storage.RenewableCert(cert_path_implied_conf, config)
            cert_path_info = cert_manager.human_readable_cert_info(config, cert_path_cert,
                    skip_filter_checks=True)

            cert_name_implied_conf = storage.renewal_file_for_certname(config, config.certname)
            cert_name_cert = storage.RenewableCert(cert_name_implied_conf, config)
            cert_name_info = cert_manager.human_readable_cert_info(config, cert_name_cert)

            msg = ("You specified conflicting values for --cert-path and --cert-name. "
                    "Which did you mean to select?")
            choices = [cert_path_info, cert_name_info]
            try:
                code, index = display.menu(msg,
                        choices, ok_label="Select", force_interactive=True)
            except errors.MissingCommandlineFlag:
                error_msg = ('To run in non-interactive mode, you must either specify only one of '
                '--cert-path or --cert-name, or both must point to the same certificate lineages.')
                raise errors.Error(error_msg)

            if code != display_util.OK or not index in range(0, len(choices)):
                raise errors.Error("User ended interaction.")

            if index == 0:
                config.certname = cert_path_implied_cert_name
            else:
                config.cert_path = storage.cert_path_for_cert_name(config, config.certname)

    elif config.cert_path:
        config.certname = cert_manager.cert_path_to_lineage(config)

    else: # if only config.certname was specified
        config.cert_path = storage.cert_path_for_cert_name(config, config.certname)

    # don't delete if the archive_dir is used by some other lineage
    archive_dir = storage.full_archive_path(
            configobj.ConfigObj(storage.renewal_file_for_certname(config, config.certname)),
            config, config.certname)
    try:
        cert_manager.match_and_check_overlaps(config, [lambda x: archive_dir],
            lambda x: x.archive_dir, lambda x: x)
    except errors.OverlappingMatchFound:
        msg = ('Not deleting revoked certs due to overlapping archive dirs. More than '
                'one lineage is using {0}'.format(archive_dir))
        reporter_util.add_message(''.join(msg), reporter_util.MEDIUM_PRIORITY)
        return
    except Exception as e:
        msg = ('config.default_archive_dir: {0}, config.live_dir: {1}, archive_dir: {2},'
        'original exception: {3}')
        msg = msg.format(config.default_archive_dir, config.live_dir, archive_dir, e)
        raise errors.Error(msg)

    cert_manager.delete(config)
Example #5
0
def handle_renewal_request(config):
    """Examine each lineage; renew if due and report results"""

    # This is trivially False if config.domains is empty
    if any(domain not in config.webroot_map for domain in config.domains):
        # If more plugins start using cli.add_domains,
        # we may want to only log a warning here
        raise errors.Error("Currently, the renew verb is capable of either "
                           "renewing all installed certificates that are due "
                           "to be renewed or renewing a single certificate specified "
                           "by its name. If you would like to renew specific "
                           "certificates by their domains, use the certonly "
                           "command. The renew verb may provide other options "
                           "for selecting certificates to renew in the future.")

    if config.certname:
        conf_files = [storage.renewal_file_for_certname(config, config.certname)]
    else:
        conf_files = storage.renewal_conf_files(config)

    renew_successes = []
    renew_failures = []
    renew_skipped = []
    parse_failures = []
    for renewal_file in conf_files:
        disp = zope.component.getUtility(interfaces.IDisplay)
        disp.notification("Processing " + renewal_file, pause=False)
        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
            logger.warning("Renewal configuration file %s produced an "
                           "unexpected error: %s. Skipping.", renewal_file, e)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            parse_failures.append(renewal_file)
            continue

        try:
            if renewal_candidate is None:
                parse_failures.append(renewal_file)
            else:
                # XXX: ensure that each call here replaces the previous one
                zope.component.provideUtility(lineage_config)
                renewal_candidate.ensure_deployed()
                if should_renew(lineage_config, renewal_candidate):
                    plugins = plugins_disco.PluginsRegistry.find_all()
                    from certbot import main
                    main.obtain_cert(lineage_config, plugins, renewal_candidate)
                    renew_successes.append(renewal_candidate.fullchain)
                else:
                    renew_skipped.append(renewal_candidate.fullchain)
        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())
            renew_failures.append(renewal_candidate.fullchain)

    # Describe all the results
    _renew_describe_results(config, renew_successes, renew_failures,
                            renew_skipped, parse_failures)

    if renew_failures or parse_failures:
        raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format(
            len(renew_failures), len(parse_failures)))
    else:
        logger.debug("no renewal failures")
Example #6
0
def handle_renewal_request(config):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    """Examine each lineage; renew if due and report results"""

    # This is trivially False if config.domains is empty
    if any(domain not in config.webroot_map for domain in config.domains):
        # If more plugins start using cli.add_domains,
        # we may want to only log a warning here
        raise errors.Error(
            "Currently, the renew verb is capable of either "
            "renewing all installed certificates that are due "
            "to be renewed or renewing a single certificate specified "
            "by its name. If you would like to renew specific "
            "certificates by their domains, use the certonly command "
            "instead. The renew verb may provide other options "
            "for selecting certificates to renew in the future.")

    if config.certname:
        conf_files = [
            storage.renewal_file_for_certname(config, config.certname)
        ]
    else:
        conf_files = storage.renewal_conf_files(config)

    renew_successes = []
    renew_failures = []
    renew_skipped = []
    parse_failures = []

    # Noninteractive renewals include a random delay in order to spread
    # out the load on the certificate authority servers, even if many
    # users all pick the same time for renewals.  This delay precedes
    # running any hooks, so that side effects of the hooks (such as
    # shutting down a web service) aren't prolonged unnecessarily.
    apply_random_sleep = not sys.stdin.isatty(
    ) and config.random_sleep_on_renew

    for renewal_file in conf_files:
        disp = zope.component.getUtility(interfaces.IDisplay)
        disp.notification("Processing " + renewal_file, pause=False)
        lineage_config = copy.deepcopy(config)
        lineagename = storage.lineagename_for_filename(renewal_file)

        # 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
            logger.warning(
                "Renewal configuration file %s (cert: %s) "
                "produced an unexpected error: %s. Skipping.", renewal_file,
                lineagename, e)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            parse_failures.append(renewal_file)
            continue

        try:
            if renewal_candidate is None:
                parse_failures.append(renewal_file)
            else:
                # XXX: ensure that each call here replaces the previous one
                zope.component.provideUtility(lineage_config)
                renewal_candidate.ensure_deployed()
                from certbot import main
                plugins = plugins_disco.PluginsRegistry.find_all()
                if should_renew(lineage_config, renewal_candidate):
                    # Apply random sleep upon first renewal if needed
                    if apply_random_sleep:
                        sleep_time = random.randint(1, 60 * 8)
                        logger.info(
                            "Non-interactive renewal: random delay of %s seconds",
                            sleep_time)
                        time.sleep(sleep_time)
                        # We will sleep only once this day, folks.
                        apply_random_sleep = False

                    # domains have been restored into lineage_config by reconstitute
                    # but they're unnecessary anyway because renew_cert here
                    # will just grab them from the certificate
                    # we already know it's time to renew based on should_renew
                    # and we have a lineage in renewal_candidate
                    main.renew_cert(lineage_config, plugins, renewal_candidate)
                    renew_successes.append(renewal_candidate.fullchain)
                else:
                    expiry = crypto_util.notAfter(
                        renewal_candidate.version(
                            "cert", renewal_candidate.latest_common_version()))
                    renew_skipped.append("%s expires on %s" %
                                         (renewal_candidate.fullchain,
                                          expiry.strftime("%Y-%m-%d")))
                # Run updater interface methods
                updater.run_generic_updaters(lineage_config, renewal_candidate,
                                             plugins)

        except Exception as e:  # pylint: disable=broad-except
            # obtain_cert (presumably) encountered an unanticipated problem.
            logger.warning(
                "Attempting to renew cert (%s) from %s produced an "
                "unexpected error: %s. Skipping.", lineagename, renewal_file,
                e)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            renew_failures.append(renewal_candidate.fullchain)

    # Describe all the results
    _renew_describe_results(config, renew_successes, renew_failures,
                            renew_skipped, parse_failures)

    if renew_failures or parse_failures:
        raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format(
            len(renew_failures), len(parse_failures)))
    else:
        logger.debug("no renewal failures")
Example #7
0
def handle_renewal_request(config):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    """Examine each lineage; renew if due and report results"""

    # This is trivially False if config.domains is empty
    if any(domain not in config.webroot_map for domain in config.domains):
        # If more plugins start using cli.add_domains,
        # we may want to only log a warning here
        raise errors.Error("Currently, the renew verb is capable of either "
                           "renewing all installed certificates that are due "
                           "to be renewed or renewing a single certificate specified "
                           "by its name. If you would like to renew specific "
                           "certificates by their domains, use the certonly command "
                           "instead. The renew verb may provide other options "
                           "for selecting certificates to renew in the future.")

    if config.certname:
        conf_files = [storage.renewal_file_for_certname(config, config.certname)]
    else:
        conf_files = storage.renewal_conf_files(config)

    renew_successes = []
    renew_failures = []
    renew_skipped = []
    parse_failures = []

    # Noninteractive renewals include a random delay in order to spread
    # out the load on the certificate authority servers, even if many
    # users all pick the same time for renewals.  This delay precedes
    # running any hooks, so that side effects of the hooks (such as
    # shutting down a web service) aren't prolonged unnecessarily.
    apply_random_sleep = not sys.stdin.isatty() and config.random_sleep_on_renew

    for renewal_file in conf_files:
        disp = zope.component.getUtility(interfaces.IDisplay)
        disp.notification("Processing " + renewal_file, pause=False)
        lineage_config = copy.deepcopy(config)
        lineagename = storage.lineagename_for_filename(renewal_file)

        # 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
            logger.warning("Renewal configuration file %s (cert: %s) "
                           "produced an unexpected error: %s. Skipping.",
                           renewal_file, lineagename, e)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            parse_failures.append(renewal_file)
            continue

        try:
            if renewal_candidate is None:
                parse_failures.append(renewal_file)
            else:
                # XXX: ensure that each call here replaces the previous one
                zope.component.provideUtility(lineage_config)
                renewal_candidate.ensure_deployed()
                from certbot import main
                plugins = plugins_disco.PluginsRegistry.find_all()
                if should_renew(lineage_config, renewal_candidate):
                    # Apply random sleep upon first renewal if needed
                    if apply_random_sleep:
                        sleep_time = random.randint(1, 60 * 8)
                        logger.info("Non-interactive renewal: random delay of %s seconds",
                                    sleep_time)
                        time.sleep(sleep_time)
                        # We will sleep only once this day, folks.
                        apply_random_sleep = False

                    # domains have been restored into lineage_config by reconstitute
                    # but they're unnecessary anyway because renew_cert here
                    # will just grab them from the certificate
                    # we already know it's time to renew based on should_renew
                    # and we have a lineage in renewal_candidate
                    main.renew_cert(lineage_config, plugins, renewal_candidate)
                    renew_successes.append(renewal_candidate.fullchain)
                else:
                    expiry = crypto_util.notAfter(renewal_candidate.version(
                        "cert", renewal_candidate.latest_common_version()))
                    renew_skipped.append("%s expires on %s" % (renewal_candidate.fullchain,
                                         expiry.strftime("%Y-%m-%d")))
                # Run updater interface methods
                updater.run_generic_updaters(lineage_config, renewal_candidate,
                                             plugins)

        except Exception as e:  # pylint: disable=broad-except
            # obtain_cert (presumably) encountered an unanticipated problem.
            logger.warning("Attempting to renew cert (%s) from %s produced an "
                           "unexpected error: %s. Skipping.", lineagename,
                               renewal_file, e)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            renew_failures.append(renewal_candidate.fullchain)

    # Describe all the results
    _renew_describe_results(config, renew_successes, renew_failures,
                            renew_skipped, parse_failures)

    if renew_failures or parse_failures:
        raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format(
            len(renew_failures), len(parse_failures)))
    else:
        logger.debug("no renewal failures")