Ejemplo n.º 1
0
    def obtain_and_enroll_certificate(self, domains):
        """Obtain and enroll certificate.

        Get a new certificate for the specified domains using the specified
        authenticator and installer, and then create a new renewable lineage
        containing it.

        :param list domains: Domains to request.
        :param plugins: A PluginsFactory object.

        :returns: A new :class:`certbot.storage.RenewableCert` instance
            referred to the enrolled cert lineage, False if the cert could not
            be obtained, or None if doing a successful dry run.

        """
        certr, chain, key, _ = self.obtain_certificate(domains)

        if (self.config.config_dir != constants.CLI_DEFAULTS["config_dir"]
                or self.config.work_dir != constants.CLI_DEFAULTS["work_dir"]):
            logger.warning(
                "Non-standard path(s), might not work with crontab installed "
                "by your operating system package manager")

        if self.config.dry_run:
            logger.debug("Dry run: Skipping creating new lineage for %s",
                         domains[0])
            return None
        else:
            return storage.RenewableCert.new_lineage(
                domains[0],
                OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
                                                certr.body.wrapped), key.pem,
                crypto_util.dump_pyopenssl_chain(chain),
                configuration.RenewerConfiguration(self.config.namespace))
Ejemplo n.º 2
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
Ejemplo n.º 3
0
    def setUp(self):
        self.tempdir = tempfile.mkdtemp()

        os.makedirs(os.path.join(self.tempdir, "renewal"))

        mock_namespace = mock.MagicMock(
            config_dir=self.tempdir,
            work_dir=self.tempdir,
            logs_dir=self.tempdir,
        )

        self.cli_config = configuration.RenewerConfiguration(
            namespace=mock_namespace)

        self.domains = {
            "example.org": None,
            "other.com": os.path.join(self.tempdir, "specialarchive")
        }
        self.configs = dict(
            (domain, self._set_up_config(domain, self.domains[domain]))
            for domain in self.domains)

        # 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()
Ejemplo n.º 4
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.interfaces.IConfig`

    """
    renewer_config = configuration.RenewerConfiguration(config)
    for renewal_file in renewal.renewal_conf_files(renewer_config):
        storage.RenewableCert(renewal_file,
            configuration.RenewerConfiguration(renewer_config),
            update_symlinks=True)
Ejemplo n.º 5
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, configuration.RenewerConfiguration(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, error.message)
        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
Ejemplo n.º 6
0
 def setUp(self):
     super(RenameLineageTest, self).setUp()
     self.mock_config = configuration.RenewerConfiguration(
         namespace=mock.MagicMock(
             config_dir=self.tempdir,
             work_dir=self.tempdir,
             logs_dir=self.tempdir,
             certname="example.org",
             new_certname="after",
         ))
Ejemplo n.º 7
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/"])
Ejemplo n.º 8
0
def certificates(config):
    """Display information about certs configured with Certbot

    :param config: Configuration.
    :type config: :class:`certbot.interfaces.IConfig`
    """
    renewer_config = configuration.RenewerConfiguration(config)
    parsed_certs = []
    parse_failures = []
    for renewal_file in renewal.renewal_conf_files(renewer_config):
        try:
            renewal_candidate = storage.RenewableCert(
                renewal_file, configuration.RenewerConfiguration(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(parsed_certs, parse_failures)
Ejemplo n.º 9
0
def rename_lineage(config):
    """Rename the specified lineage to the new name.

    :param config: Configuration.
    :type config: :class:`certbot.interfaces.IConfig`

    """
    disp = zope.component.getUtility(interfaces.IDisplay)
    renewer_config = configuration.RenewerConfiguration(config)

    certname = config.certname
    if not certname:
        filenames = renewal.renewal_conf_files(renewer_config)
        choices = [
            storage.lineagename_for_filename(name) for name in filenames
        ]
        if not choices:
            raise errors.Error("No existing certificates found.")
        code, index = disp.menu("Which certificate would you like to rename?",
                                choices,
                                ok_label="Select",
                                flag="--cert-name")
        if code != display_util.OK or not index in range(0, len(choices)):
            raise errors.Error("User ended interaction.")
        certname = choices[index]

    new_certname = config.new_certname
    if not new_certname:
        code, new_certname = disp.input(
            "Enter the new name for certificate {0}".format(certname),
            flag="--updated-cert-name")
        if code != display_util.OK or not new_certname:
            raise errors.Error("User ended interaction.")

    lineage = lineage_for_certname(config, certname)
    if not lineage:
        raise errors.ConfigurationError("No existing certificate with name "
                                        "{0} found.".format(certname))
    storage.rename_renewal_config(certname, new_certname, renewer_config)
    disp.notification("Successfully renamed {0} to {1}.".format(
        certname, new_certname),
                      pause=False)
Ejemplo n.º 10
0
def renew_cert(config, le_client, lineage):
    "Renew a certificate lineage."
    renewal_params = lineage.configuration["renewalparams"]
    original_server = renewal_params.get("server", cli.flag_default("server"))
    _avoid_invalidating_lineage(config, lineage, original_server)
    new_certr, new_chain, new_key, _ = le_client.obtain_certificate(lineage.names())
    if config.dry_run:
        logger.debug("Dry run: skipping updating lineage at %s",
                    os.path.dirname(lineage.cert))
    else:
        prior_version = lineage.latest_common_version()
        new_cert = OpenSSL.crypto.dump_certificate(
            OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped)
        new_chain = crypto_util.dump_pyopenssl_chain(new_chain)
        renewal_conf = configuration.RenewerConfiguration(config.namespace)
        # TODO: Check return value of save_successor
        lineage.save_successor(prior_version, new_cert, new_key.pem, new_chain, renewal_conf)
        lineage.update_all_links_to(lineage.latest_common_version())

    hooks.renew_hook(config, lineage.names(), lineage.live_dir)
Ejemplo n.º 11
0
    def setUp(self):
        from certbot import storage
        self.tempdir = tempfile.mkdtemp()

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

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

        config = configobj.ConfigObj()
        for kind in ALL_FOUR:
            config[kind] = os.path.join(self.tempdir, "live", "example.org",
                                        kind + ".pem")
        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)
Ejemplo n.º 12
0
def _search_lineages(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.
    """
    cli_config = configuration.RenewerConfiguration(config)
    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 renewal.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
Ejemplo n.º 13
0
def renew_all_lineages(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 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.")
    renewer_config = configuration.RenewerConfiguration(config)
    renew_successes = []
    renew_failures = []
    renew_skipped = []
    parse_failures = []
    for renewal_file in renewal_conf_files(renewer_config):
        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)
                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")