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)
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
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")