Пример #1
0
def _find_duplicative_certs(config, domains):
    """Find existing certs that duplicate the request."""

    identical_names_cert, subset_names_cert = None, None

    cli_config = configuration.RenewerConfiguration(config)
    configs_dir = cli_config.renewal_configs_dir
    # Verify the directory is there
    le_util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())

    for renewal_file in renewal.renewal_conf_files(cli_config):
        try:
            candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
        except (errors.CertStorageError, IOError):
            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?
        candidate_names = set(candidate_lineage.names())
        if candidate_names == set(domains):
            identical_names_cert = candidate_lineage
        elif candidate_names.issubset(set(domains)):
            # This logic finds and returns the largest subset-names cert
            # in the case where there are several available.
            if subset_names_cert is None:
                subset_names_cert = candidate_lineage
            elif len(candidate_names) > len(subset_names_cert.names()):
                subset_names_cert = candidate_lineage

    return identical_names_cert, subset_names_cert
Пример #2
0
def _search_lineages(cli_config, func, initial_rv, *args):
    """Iterate func over unbroken lineages, allowing custom return conditions.

    Allows flexible customization of return values, including multiple
    return values and complex checks.

    :param `configuration.NamespaceConfig` cli_config: parsed command line arguments
    :param function func: function used while searching over lineages
    :param initial_rv: initial return value of the function (any type)

    :returns: Whatever was specified by `func` if a match is found.
    """
    configs_dir = cli_config.renewal_configs_dir
    # Verify the directory is there
    util.make_or_verify_dir(configs_dir, mode=0o755)

    rv = initial_rv
    for renewal_file in storage.renewal_conf_files(cli_config):
        try:
            candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
        except (errors.CertStorageError, IOError):
            logger.debug("Renewal conf file %s is broken. Skipping.",
                         renewal_file)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            continue
        rv = func(candidate_lineage, rv, *args)
    return rv
Пример #3
0
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 renewal
    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, config)
    except (errors.CertStorageError, IOError) as exc:
        logger.warning(exc)
        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 occurred while parsing %s. The error was %s. "
            "Skipping the file.", full_path, str(error))
        logger.debug("Traceback was:\n%s", traceback.format_exc())
        return None

    try:
        config.domains = [
            util.enforce_domain_sanity(d) for d in renewal_candidate.names()
        ]
    except errors.ConfigurationError as error:
        logger.warning(
            "Renewal configuration file %s references a cert "
            "that contains an invalid domain name. The problem "
            "was: %s. Skipping.", full_path, error)
        return None

    return renewal_candidate
Пример #4
0
    def test_no_renewal_version(self):
        from certbot import storage

        self._write_out_ex_kinds()
        self.assertTrue("version" not in self.config_file)

        with mock.patch("certbot.storage.logger") as mock_logger:
            storage.RenewableCert(self.config_file.filename, self.config)
        self.assertFalse(mock_logger.warning.called)
Пример #5
0
 def test_ancient_webroot_renewal_conf(self, mock_set_by_cli):
     mock_set_by_cli.return_value = False
     rc_path = self._make_test_renewal_conf('sample-renewal-ancient.conf')
     args = mock.MagicMock(account=None, email=None, webroot_path=None)
     config = configuration.NamespaceConfig(args)
     lineage = storage.RenewableCert(
         rc_path, configuration.RenewerConfiguration(config))
     renewalparams = lineage.configuration["renewalparams"]
     # pylint: disable=protected-access
     renewal._restore_webroot_config(config, renewalparams)
     self.assertEqual(config.webroot_path, ["/var/www/"])
Пример #6
0
    def test_renewal_newer_version(self):
        from certbot import storage

        self._write_out_ex_kinds()
        self.config_file["version"] = "99.99.99"
        self.config_file.write()

        with mock.patch("certbot.storage.logger") as mock_logger:
            storage.RenewableCert(self.config_file.filename, self.config)
        self.assertTrue(mock_logger.info.called)
        self.assertTrue("version" in mock_logger.info.call_args[0][0])
Пример #7
0
 def test_ancient_webroot_renewal_conf(self, mock_set_by_cli):
     mock_set_by_cli.return_value = False
     rc_path = util.make_lineage(self, 'sample-renewal-ancient.conf')
     args = mock.MagicMock(account=None, email=None, webroot_path=None)
     config = configuration.NamespaceConfig(args)
     lineage = storage.RenewableCert(rc_path, config)
     renewalparams = lineage.configuration['renewalparams']
     # pylint: disable=protected-access
     from certbot import renewal
     renewal._restore_webroot_config(config, renewalparams)
     self.assertEqual(config.webroot_path, ['/var/www/'])
Пример #8
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
Пример #9
0
def update_live_symlinks(config):
    """Update the certificate file family symlinks to use archive_dir.

    Use the information in the config file to make symlinks point to
    the correct archive directory.

    .. note:: This assumes that the installation is using a Reverter object.

    :param config: Configuration.
    :type config: :class:`certbot.configuration.NamespaceConfig`

    """
    for renewal_file in storage.renewal_conf_files(config):
        storage.RenewableCert(renewal_file, config, update_symlinks=True)
Пример #10
0
 def test_update_symlinks(self):
     from certbot import storage
     archive_dir_path = os.path.join(self.config.config_dir, "archive", "example.org")
     for kind in ALL_FOUR:
         live_path = self.config_file[kind]
         basename = kind + "1.pem"
         archive_path = os.path.join(archive_dir_path, basename)
         open(archive_path, 'a').close()
         os.symlink(os.path.join(self.config.config_dir, basename), live_path)
     self.assertRaises(errors.CertStorageError,
                       storage.RenewableCert, self.config_file.filename,
                       self.config)
     storage.RenewableCert(self.config_file.filename, self.config,
         update_symlinks=True)
Пример #11
0
    def setUp(self):
        from certbot import storage

        super(BaseRenewableCertTest, self).setUp()

        self.cli_config = configuration.NamespaceConfig(
            namespace=mock.MagicMock(
                config_dir=self.tempdir,
                work_dir=self.tempdir,
                logs_dir=self.tempdir,
            ))

        # TODO: maybe provide NamespaceConfig.make_dirs?
        # TODO: main() should create those dirs, c.f. #902
        os.makedirs(os.path.join(self.tempdir, "live", "example.org"))
        archive_path = os.path.join(self.tempdir, "archive", "example.org")
        os.makedirs(archive_path)
        os.makedirs(os.path.join(self.tempdir, "renewal"))

        config = configobj.ConfigObj()
        for kind in ALL_FOUR:
            kind_path = os.path.join(self.tempdir, "live", "example.org",
                                     kind + ".pem")
            config[kind] = kind_path
        with open(os.path.join(self.tempdir, "live", "example.org", "README"),
                  'a'):
            pass
        config["archive"] = archive_path
        config.filename = os.path.join(self.tempdir, "renewal",
                                       "example.org.conf")
        config.write()
        self.config = config

        # We also create a file that isn't a renewal config in the same
        # location to test that logic that reads in all-and-only renewal
        # configs will ignore it and NOT attempt to parse it.
        junk = open(os.path.join(self.tempdir, "renewal", "IGNORE.THIS"), "w")
        junk.write("This file should be ignored!")
        junk.close()

        self.defaults = configobj.ConfigObj()

        with mock.patch(
                "certbot.storage.RenewableCert._check_symlinks") as check:
            check.return_value = True
            self.test_rc = storage.RenewableCert(config.filename,
                                                 self.cli_config)
Пример #12
0
def certificates(config):
    """Display information about certs configured with Certbot

    :param config: Configuration.
    :type config: :class:`certbot.configuration.NamespaceConfig`
    """
    parsed_certs = []
    parse_failures = []
    for renewal_file in storage.renewal_conf_files(config):
        try:
            renewal_candidate = storage.RenewableCert(renewal_file, config)
            parsed_certs.append(renewal_candidate)
        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)

    # Describe all the certs
    _describe_certs(config, parsed_certs, parse_failures)
Пример #13
0
def _search_lineages(cli_config, func, initial_rv):
    """Iterate func over unbroken lineages, allowing custom return conditions.

    Allows flexible customization of return values, including multiple
    return values and complex checks.
    """
    configs_dir = cli_config.renewal_configs_dir
    # Verify the directory is there
    util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())

    rv = initial_rv
    for renewal_file in storage.renewal_conf_files(cli_config):
        try:
            candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
        except (errors.CertStorageError, IOError):
            logger.debug("Renewal conf file %s is broken. Skipping.", renewal_file)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            continue
        rv = func(candidate_lineage, rv)
    return rv
Пример #14
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)