def _search_lineages(cli_config: configuration.NamespaceConfig, func: Callable[..., T], initial_rv: T, *args: Any) -> T: """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
def test_new_key(self): # When renewing with both reuse_key and new_key, the key should be regenerated, # the key type, key parameters and reuse_key should be kept. self.config.reuse_key = True self.config.new_key = True self.config.dry_run = True config = configuration.NamespaceConfig(self.config) rc_path = test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf') lineage = storage.RenewableCert(rc_path, config) le_client = mock.MagicMock() le_client.obtain_certificate.return_value = (None, None, None, None) from certbot._internal import renewal with mock.patch('certbot._internal.renewal.hooks.renew_hook'): renewal.renew_cert(self.config, None, le_client, lineage) self.assertEqual(self.config.rsa_key_size, 2048) self.assertEqual(self.config.key_type, 'rsa') self.assertTrue(self.config.reuse_key) # None is passed as the existing key, i.e. the key is not actually being reused. le_client.obtain_certificate.assert_called_with(mock.ANY, None)
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 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
def list_certificates(): """Display information about certs configured with Certbot :param config: Configuration. :type config: :class:`certbot._internal.configuration.NamespaceConfig` """ plugins = plugins_disco.PluginsRegistry.find_all() args = cli.prepare_and_parse_args(plugins, [ "certificates", "--config-dir=" + constants.CONFIG_DIR, "--work-dir=" + constants.WORK_DIR, "--logs=" + constants.LOGS_DIR ]) config = configuration.NamespaceConfig(args) parsed_certs = [] parse_failures = [] for renewal_file in storage.renewal_conf_files(config): try: renewal_candidate = storage.RenewableCert(renewal_file, config) crypto_util.verify_renewable_cert(renewal_candidate) 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) return (parsed_certs, parse_failures)
def test_no_renewal_version(self): from certbot._internal import storage self._write_out_ex_kinds() self.assertTrue("version" not in self.config_file) with mock.patch("certbot._internal.storage.logger") as mock_logger: storage.RenewableCert(self.config_file.filename, self.config) self.assertFalse(mock_logger.warning.called)
def test_renewal_newer_version(self): from certbot._internal import storage self._write_out_ex_kinds() self.config_file["version"] = "99.99.99" self.config_file.write() with mock.patch("certbot._internal.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])
def update_live_symlinks(config: configuration.NamespaceConfig) -> None: """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._internal.configuration.NamespaceConfig` """ for renewal_file in storage.renewal_conf_files(config): storage.RenewableCert(renewal_file, config, update_symlinks=True)
def test_ancient_webroot_renewal_conf(self, mock_set_by_cli): mock_set_by_cli.return_value = False rc_path = test_util.make_lineage(self.config.config_dir, 'sample-renewal-ancient.conf') self.config.account = None self.config.email = None self.config.webroot_path = None config = configuration.NamespaceConfig(self.config) lineage = storage.RenewableCert(rc_path, config) renewalparams = lineage.configuration['renewalparams'] # pylint: disable=protected-access from certbot._internal import renewal renewal._restore_webroot_config(config, renewalparams) self.assertEqual(config.webroot_path, ['/var/www/'])
def test_update_symlinks(self): from certbot._internal 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)
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 setUp(self): from certbot._internal import storage super().setUp() # TODO: maybe provide NamespaceConfig.make_dirs? # TODO: main() should create those dirs, c.f. #902 filesystem.makedirs( os.path.join(self.config.config_dir, "live", "example.org")) archive_path = os.path.join(self.config.config_dir, "archive", "example.org") filesystem.makedirs(archive_path) filesystem.makedirs(os.path.join(self.config.config_dir, "renewal")) config_file = configobj.ConfigObj() for kind in ALL_FOUR: kind_path = os.path.join(self.config.config_dir, "live", "example.org", kind + ".pem") config_file[kind] = kind_path with open( os.path.join(self.config.config_dir, "live", "example.org", "README"), 'a'): pass config_file["archive"] = archive_path config_file.filename = os.path.join(self.config.config_dir, "renewal", "example.org.conf") config_file.write() self.config_file = config_file # 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. with open( os.path.join(self.config.config_dir, "renewal", "IGNORE.THIS"), "w") as junk: junk.write("This file should be ignored!") self.defaults = configobj.ConfigObj() with mock.patch( "certbot._internal.storage.RenewableCert._check_symlinks" ) as check: check.return_value = True self.test_rc = storage.RenewableCert(config_file.filename, self.config)
def test_reuse_key_renewal_params(self): self.config.rsa_key_size = 'INVALID_VALUE' self.config.reuse_key = True self.config.dry_run = True config = configuration.NamespaceConfig(self.config) rc_path = test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf') lineage = storage.RenewableCert(rc_path, config) le_client = mock.MagicMock() le_client.obtain_certificate.return_value = (None, None, None, None) from certbot._internal import renewal with mock.patch('certbot._internal.renewal.hooks.renew_hook'): renewal.renew_cert(self.config, None, le_client, lineage) assert self.config.rsa_key_size == 2048
def certificates(config: configuration.NamespaceConfig) -> None: """Display information about certs configured with Certbot :param config: Configuration. :type config: :class:`certbot._internal.configuration.NamespaceConfig` """ parsed_certs = [] parse_failures = [] for renewal_file in storage.renewal_conf_files(config): try: renewal_candidate = storage.RenewableCert(renewal_file, config) crypto_util.verify_renewable_cert(renewal_candidate) 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)
def test_reuse_ec_key_renewal_params(self): self.config.elliptic_curve = 'INVALID_CURVE' self.config.reuse_key = True self.config.dry_run = True self.config.key_type = 'ecdsa' config = configuration.NamespaceConfig(self.config) rc_path = test_util.make_lineage( self.config.config_dir, 'sample-renewal-ec.conf', ec=True, ) lineage = storage.RenewableCert(rc_path, config) le_client = mock.MagicMock() le_client.obtain_certificate.return_value = (None, None, None, None) from certbot._internal import renewal with mock.patch('certbot._internal.renewal.hooks.renew_hook'): renewal.renew_cert(self.config, None, le_client, lineage) assert self.config.elliptic_curve == 'secp256r1'
def _cert_iter(self): """An iterator over all renewable certificates on this machine""" for file in storage.renewal_conf_files(self._config()): yield storage.RenewableCert(file, self._config())