Exemple #1
0
def _delete_if_appropriate(config):
    """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)

    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:
        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:
        logger.warning(
            "Not deleting revoked certs due to overlapping archive dirs. More than "
            "one certificate is using %s", archive_dir)
        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)
Exemple #2
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)
    try:
        renewal_file = storage.renewal_file_for_certname(cli_config, certname)
    except errors.CertStorageError:
        return None
    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
Exemple #3
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 "
            "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:
        display_util.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.error(
                "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:
                # This call is done only for retro-compatibility purposes.
                # TODO: Remove this call once zope dependencies are removed from Certbot.
                zope.component.provideUtility(lineage_config,
                                              interfaces.IConfig)
                renewal_candidate.ensure_deployed()
                from certbot._internal 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.uniform(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.error("Failed to renew certificate %s with error: %s",
                         lineagename, 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)))

    # Windows installer integration tests rely on handle_renewal_request behavior here.
    # If the text below changes, these tests will need to be updated accordingly.
    logger.debug("no renewal failures")