Esempio n. 1
0
def _restore_webroot_config(config: configuration.NamespaceConfig,
                            renewalparams: Mapping[str, Any]) -> None:
    """
    webroot_map is, uniquely, a dict, and the general-purpose configuration
    restoring logic is not able to correctly parse it from the serialized
    form.
    """
    if "webroot_map" in renewalparams and not cli.set_by_cli("webroot_map"):
        config.webroot_map = renewalparams["webroot_map"]
    # To understand why webroot_path and webroot_map processing are not mutually exclusive,
    # see https://github.com/certbot/certbot/pull/7095
    if "webroot_path" in renewalparams and not cli.set_by_cli("webroot_path"):
        wp = renewalparams["webroot_path"]
        if isinstance(wp, str):  # prior to 0.1.0, webroot_path was a string
            wp = [wp]
        config.webroot_path = wp
Esempio n. 2
0
    def test_absolute_paths(self):
        from certbot.configuration import NamespaceConfig

        config_base = "foo"
        work_base = "bar"
        logs_base = "baz"
        server = "mock.server"

        mock_namespace = mock.MagicMock(spec=[
            'config_dir', 'work_dir', 'logs_dir', 'http01_port',
            'tls_sni_01_port', 'domains', 'server'
        ])
        mock_namespace.config_dir = config_base
        mock_namespace.work_dir = work_base
        mock_namespace.logs_dir = logs_base
        mock_namespace.server = server
        config = NamespaceConfig(mock_namespace)

        self.assertTrue(os.path.isabs(config.config_dir))
        self.assertEqual(config.config_dir,
                         os.path.join(os.getcwd(), config_base))
        self.assertTrue(os.path.isabs(config.work_dir))
        self.assertEqual(config.work_dir, os.path.join(os.getcwd(), work_base))
        self.assertTrue(os.path.isabs(config.logs_dir))
        self.assertEqual(config.logs_dir, os.path.join(os.getcwd(), logs_base))
        self.assertTrue(os.path.isabs(config.accounts_dir))
        self.assertTrue(os.path.isabs(config.backup_dir))
        self.assertTrue(os.path.isabs(config.csr_dir))
        self.assertTrue(os.path.isabs(config.in_progress_dir))
        self.assertTrue(os.path.isabs(config.key_dir))
        self.assertTrue(os.path.isabs(config.temp_checkpoint_dir))
Esempio n. 3
0
def record_chosen_plugins(config: configuration.NamespaceConfig,
                          plugins: disco.PluginsRegistry,
                          auth: Optional[interfaces.Authenticator],
                          inst: Optional[interfaces.Installer]) -> None:
    """Update the config entries to reflect the plugins we actually selected."""
    config.authenticator = None
    if auth:
        auth_ep = plugins.find_init(auth)
        if auth_ep:
            config.authenticator = auth_ep.name
    config.installer = None
    if inst:
        inst_ep = plugins.find_init(inst)
        if inst_ep:
            config.installer = inst_ep.name
    logger.info("Plugins selected: Authenticator %s, Installer %s",
                config.authenticator, config.installer)
Esempio n. 4
0
 def setUp(self):
     from certbot.plugins.standalone import ServerManager
     from certbot.configuration import NamespaceConfig
     self.certs = {}
     self.http_01_resources = {}
     self.mgr = ServerManager(
         self.certs, self.http_01_resources,
         NamespaceConfig(mock.MagicMock(http01_use_tls=False)))
 def setUp(self):
     self.namespace = mock.MagicMock(
         config_dir='/tmp/config', work_dir='/tmp/foo',
         logs_dir="/tmp/bar", foo='bar',
         server='https://acme-server.org:443/new',
         tls_sni_01_port=1234, http01_port=4321)
     from certbot.configuration import NamespaceConfig
     self.config = NamespaceConfig(self.namespace)
Esempio n. 6
0
 def test_run_stop_http_01_use_tls(self):
     from certbot.plugins.standalone import ServerManager
     from certbot.configuration import NamespaceConfig
     old_mgr = self.mgr
     self.mgr = ServerManager(
         self.certs, self.http_01_resources,
         NamespaceConfig(mock.MagicMock(http01_use_tls=True)))
     self._test_run_stop(challenges.HTTP01)
     self.mgr = old_mgr
Esempio n. 7
0
def perform_registration(
        acme: acme_client.ClientV2, config: configuration.NamespaceConfig,
        tos_cb: Optional[Callable[[str],
                                  None]]) -> messages.RegistrationResource:
    """
    Actually register new account, trying repeatedly if there are email
    problems

    :param acme.client.Client acme: ACME client object.
    :param certbot.configuration.NamespaceConfig config: Client configuration.
    :param Callable tos_cb: a callback to handle Term of Service agreement.

    :returns: Registration Resource.
    :rtype: `acme.messages.RegistrationResource`
    """

    eab_credentials_supplied = config.eab_kid and config.eab_hmac_key
    eab: Optional[Dict[str, Any]]
    if eab_credentials_supplied:
        account_public_key = acme.net.key.public_key()
        eab = messages.ExternalAccountBinding.from_data(
            account_public_key=account_public_key,
            kid=config.eab_kid,
            hmac_key=config.eab_hmac_key,
            directory=acme.directory)
    else:
        eab = None

    if acme.external_account_required():
        if not eab_credentials_supplied:
            msg = ("Server requires external account binding."
                   " Please use --eab-kid and --eab-hmac-key.")
            raise errors.Error(msg)

    try:
        newreg = messages.NewRegistration.from_data(
            email=config.email, external_account_binding=eab)
        # Until ACME v1 support is removed from Certbot, we actually need the provided
        # ACME client to be a wrapper of type BackwardsCompatibleClientV2.
        # TODO: Remove this cast and rewrite the logic when the client is actually a ClientV2
        try:
            return cast(acme_client.BackwardsCompatibleClientV2,
                        acme).new_account_and_tos(newreg, tos_cb)
        except AttributeError:
            raise errors.Error("The ACME client must be an instance of "
                               "acme.client.BackwardsCompatibleClientV2")
    except messages.Error as e:
        if e.code in ("invalidEmail", "invalidContact"):
            if config.noninteractive_mode:
                msg = (
                    "The ACME server believes %s is an invalid email address. "
                    "Please ensure it is a valid email and attempt "
                    "registration again." % config.email)
                raise errors.Error(msg)
            config.email = display_ops.get_email(invalid=True)
            return perform_registration(acme, config, tos_cb)
        raise
Esempio n. 8
0
    def test_renewal_absolute_paths(self):
        from certbot.configuration import NamespaceConfig

        config_base = "foo"
        work_base = "bar"
        logs_base = "baz"

        mock_namespace = mock.MagicMock(spec=[
            'config_dir', 'work_dir', 'logs_dir', 'http01_port',
            'tls_sni_01_port', 'domains', 'server'
        ])
        mock_namespace.config_dir = config_base
        mock_namespace.work_dir = work_base
        mock_namespace.logs_dir = logs_base
        config = NamespaceConfig(mock_namespace)

        self.assertTrue(os.path.isabs(config.default_archive_dir))
        self.assertTrue(os.path.isabs(config.live_dir))
        self.assertTrue(os.path.isabs(config.renewal_configs_dir))
Esempio n. 9
0
def _main(domains=[], email=None, instance_name="", consul_manager=None):
    ns = ConfigNamespace(email, domains)
    config = NamespaceConfig(ns)
    zope.component.provideUtility(config)

    ams = AccountMemoryStorage()
    acc, acme = register(config, ams)

    authenticator = RpaasLeAuthenticator(instance_name,
                                         config=config,
                                         name='',
                                         consul_manager=consul_manager)
    installer = None
    lec = Client(config, acc, authenticator, installer, acme)
    certr, chain, key, _ = lec.obtain_certificate(domains)
    return (
        OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
                                        certr.body),
        crypto_util.dump_pyopenssl_chain(chain),
        key.pem,
    )
Esempio n. 10
0
def _reconstitute(config: configuration.NamespaceConfig,
                  full_path: str) -> Optional[storage.RenewableCert]:
    """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 error:
        logger.error("Renewal configuration file %s is broken.", full_path)
        logger.error("The error was: %s\nSkipping.", str(error))
        logger.debug("Traceback was:\n%s", traceback.format_exc())
        return None
    if "renewalparams" not in renewal_candidate.configuration:
        logger.error(
            "Renewal configuration file %s lacks "
            "renewalparams. Skipping.", full_path)
        return None
    renewalparams = renewal_candidate.configuration["renewalparams"]
    if "authenticator" not in renewalparams:
        logger.error(
            "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.
    renewalparams = _remove_deprecated_config_elements(renewalparams)
    try:
        restore_required_config_elements(config, renewalparams)
        _restore_plugin_configs(config, renewalparams)
    except (ValueError, errors.Error) as error:
        logger.error(
            "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.error(
            "Renewal configuration file %s references a certificate "
            "that contains an invalid domain name. The problem "
            "was: %s. Skipping.", full_path, error)
        return None

    return renewal_candidate
Esempio n. 11
0
def register(
    config: configuration.NamespaceConfig,
    account_storage: AccountStorage,
    tos_cb: Optional[Callable[[str], None]] = None
) -> Tuple[account.Account, acme_client.ClientV2]:
    """Register new account with an ACME CA.

    This function takes care of generating fresh private key,
    registering the account, optionally accepting CA Terms of Service
    and finally saving the account. It should be called prior to
    initialization of `Client`, unless account has already been created.

    :param certbot.configuration.NamespaceConfig config: Client configuration.

    :param .AccountStorage account_storage: Account storage where newly
        registered account will be saved to. Save happens only after TOS
        acceptance step, so any account private keys or
        `.RegistrationResource` will not be persisted if `tos_cb`
        returns ``False``.

    :param tos_cb: If ACME CA requires the user to accept a Terms of
        Service before registering account, client action is
        necessary. For example, a CLI tool would prompt the user
        acceptance. `tos_cb` must be a callable that should accept
        a Term of Service URL as a string, and raise an exception
        if the TOS is not accepted by the client. ``tos_cb`` will be
        called only if the client action is necessary, i.e. when
        ``terms_of_service is not None``. This argument is optional,
        if not supplied it will default to automatic acceptance!

    :raises certbot.errors.Error: In case of any client problems, in
        particular registration failure, or unaccepted Terms of Service.
    :raises acme.errors.Error: In case of any protocol problems.

    :returns: Newly registered and saved account, as well as protocol
        API handle (should be used in `Client` initialization).
    :rtype: `tuple` of `.Account` and `acme.client.Client`

    """
    # Log non-standard actions, potentially wrong API calls
    if account_storage.find_all():
        logger.info("There are already existing accounts for %s",
                    config.server)
    if config.email is None:
        if not config.register_unsafely_without_email:
            msg = ("No email was provided and "
                   "--register-unsafely-without-email was not present.")
            logger.error(msg)
            raise errors.Error(msg)
        if not config.dry_run:
            logger.debug("Registering without email!")

    # If --dry-run is used, and there is no staging account, create one with no email.
    if config.dry_run:
        config.email = None

    # Each new registration shall use a fresh new key
    rsa_key = generate_private_key(public_exponent=65537,
                                   key_size=config.rsa_key_size,
                                   backend=default_backend())
    key = jose.JWKRSA(key=jose.ComparableRSAKey(rsa_key))
    acme = acme_from_config_key(config, key)
    # TODO: add phone?
    regr = perform_registration(acme, config, tos_cb)

    acc = account.Account(regr, key)
    account_storage.save(acc, acme)

    eff.prepare_subscription(config, acc)

    return acc, acme