def test_dry_run_flag(self): config_dir = tempfile.mkdtemp() short_args = '--dry-run --config-dir {0}'.format(config_dir).split() self.assertRaises(errors.Error, self.parse, short_args) self._assert_dry_run_flag_worked(self.parse(short_args + ['auth']), False) self._assert_dry_run_flag_worked(self.parse(short_args + ['certonly']), False) self._assert_dry_run_flag_worked(self.parse(short_args + ['renew']), False) account_dir = os.path.join(config_dir, constants.ACCOUNTS_DIR) os.mkdir(account_dir) os.mkdir(os.path.join(account_dir, 'fake_account_dir')) self._assert_dry_run_flag_worked(self.parse(short_args + ['auth']), True) self._assert_dry_run_flag_worked(self.parse(short_args + ['renew']), True) short_args += ['certonly'] self._assert_dry_run_flag_worked(self.parse(short_args), True) short_args += '--server example.com'.split() conflicts = ['--dry-run'] self._check_server_conflict_message(short_args, '--dry-run') short_args += ['--staging'] conflicts += ['--staging'] self._check_server_conflict_message(short_args, conflicts)
def setUp(self): super(MakeOrVerifyDirTest, self).setUp() self.path = os.path.join(self.tempdir, "foo") os.mkdir(self.path, 0o600) self.uid = misc.os_geteuid()
def setUp(self): super(PluginStorageTest, self).setUp() self.plugin_cls = common.Installer os.mkdir(self.config.config_dir) with mock.patch("certbot.reverter.util"): self.plugin = self.plugin_cls(config=self.config, name="mockplugin")
def setUp(self): super(InstallerTest, self).setUp() os.mkdir(self.config.config_dir) from certbot.plugins.common import Installer with mock.patch("certbot.plugins.common.reverter.Reverter"): self.installer = Installer(config=self.config, name="Installer") self.reverter = self.installer.reverter
def test_perform_cleanup_existing_dirs(self): os.mkdir(self.partial_root_challenge_path) self.auth.prepare() self.auth.perform([self.achall]) self.auth.cleanup([self.achall]) # Ensure we don't "clean up" directories that previously existed self.assertFalse(os.path.exists(self.validation_path)) self.assertFalse(os.path.exists(self.root_challenge_path))
def test_new_lineage(self, mock_rv): """Test for new_lineage() class method.""" # Mock relevant_values to say everything is relevant here (so we # don't have to mock the parser to help it decide!) mock_rv.side_effect = lambda x: x from certbot import storage result = storage.RenewableCert.new_lineage("the-lineage.com", b"cert", b"privkey", b"chain", self.config) # This consistency check tests most relevant properties about the # newly created cert lineage. # pylint: disable=protected-access self.assertTrue(result._consistent()) self.assertTrue( os.path.exists( os.path.join(self.config.renewal_configs_dir, "the-lineage.com.conf"))) self.assertTrue( os.path.exists(os.path.join(self.config.live_dir, "README"))) self.assertTrue( os.path.exists( os.path.join(self.config.live_dir, "the-lineage.com", "README"))) self.assertTrue( misc.compare_file_modes(os.stat(result.key_path).st_mode, 0o600)) with open(result.fullchain, "rb") as f: self.assertEqual(f.read(), b"cert" + b"chain") # Let's do it again and make sure it makes a different lineage result = storage.RenewableCert.new_lineage("the-lineage.com", b"cert2", b"privkey2", b"chain2", self.config) self.assertTrue( os.path.exists( os.path.join(self.config.renewal_configs_dir, "the-lineage.com-0001.conf"))) self.assertTrue( os.path.exists( os.path.join(self.config.live_dir, "the-lineage.com-0001", "README"))) # Now trigger the detection of already existing files os.mkdir(os.path.join(self.config.live_dir, "the-lineage.com-0002")) self.assertRaises(errors.CertStorageError, storage.RenewableCert.new_lineage, "the-lineage.com", b"cert3", b"privkey3", b"chain3", self.config) os.mkdir( os.path.join(self.config.default_archive_dir, "other-example.com")) self.assertRaises(errors.CertStorageError, storage.RenewableCert.new_lineage, "other-example.com", b"cert4", b"privkey4", b"chain4", self.config) # Make sure it can accept renewal parameters result = storage.RenewableCert.new_lineage("the-lineage.com", b"cert2", b"privkey2", b"chain2", self.config)
def test_cleanup_leftovers(self): self.auth.prepare() self.auth.perform([self.achall]) leftover_path = os.path.join(self.root_challenge_path, 'leftover') os.mkdir(leftover_path) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) os.rmdir(leftover_path)
def _create_challenge_dirs(self): path_map = self.conf("map") if not path_map: raise errors.PluginError( "Missing parts of webroot configuration; please set either " "--webroot-path and --domains, or --webroot-map. Run with " " --help webroot for examples.") for name, path in path_map.items(): self.full_roots[name] = os.path.join( path, challenges.HTTP01.URI_ROOT_PATH) logger.debug("Creating root challenges validation dir at %s", self.full_roots[name]) # Change the permissions to be writable (GH #1389) # Umask is used instead of chmod to ensure the client can also # run as non-root (GH #1795) old_umask = os.umask(0o022) try: stat_path = os.stat(path) # We ignore the last prefix in the next iteration, # as it does not correspond to a folder path ('/' or 'C:') for prefix in sorted(util.get_prefixes( self.full_roots[name])[:-1], key=len): try: # This is coupled with the "umask" call above because # os.mkdir's "mode" parameter may not always work: # https://docs.python.org/3/library/os.html#os.mkdir os.mkdir(prefix, 0o0755) self._created_dirs.append(prefix) # Set owner as parent directory if possible try: os.chown(prefix, stat_path.st_uid, stat_path.st_gid) except (OSError, AttributeError) as exception: logger.info( "Unable to change owner and uid of webroot directory" ) logger.debug("Error was: %s", exception) except OSError as exception: if exception.errno not in (errno.EEXIST, errno.EISDIR): raise errors.PluginError( "Couldn't create root for {0} http-01 " "challenge responses: {1}".format( name, exception)) finally: os.umask(old_umask)
def test_perform_cleanup_multiple_challenges(self): bingo_achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=b"bingo"), "pending"), domain="thing.com", account_key=KEY) bingo_validation_path = "YmluZ28" os.mkdir(self.partial_root_challenge_path) self.auth.prepare() self.auth.perform([bingo_achall, self.achall]) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(bingo_validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) self.auth.cleanup([bingo_achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertFalse(os.path.exists(self.root_challenge_path))
def test_it(self, mock_register, mock_logger): subdir = os.path.join(self.tempdir, 'subdir') os.mkdir(subdir) self._call(self.tempdir) self._call(subdir) self._call(subdir) self.assertEqual(mock_register.call_count, 1) registered_func = mock_register.call_args[0][0] from certbot import util # Despite lock_dir_until_exit has been called twice to subdir, its lock should have been # added only once. So we expect to have two lock references: for self.tempdir and subdir self.assertTrue(len(util._LOCKS) == 2) # pylint: disable=protected-access registered_func() # Exception should not be raised # Logically, logger.debug, that would be invoked in case of unlock failure, # should never been called. self.assertEqual(mock_logger.debug.call_count, 0)
def test_perform_cleanup_multiple_challenges(self): bingo_achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb(challenges.HTTP01(token=b"bingo"), "pending"), domain="thing.com", account_key=KEY) bingo_validation_path = "YmluZ28" os.mkdir(self.partial_root_challenge_path) self.auth.prepare() self.auth.perform([bingo_achall, self.achall]) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(bingo_validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) self.auth.cleanup([bingo_achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertFalse(os.path.exists(self.root_challenge_path))
def setUp(self): super(CompleterTest, self).setUp() # directories must end with os.sep for completer to # search inside the directory for possible completions if self.tempdir[-1] != os.sep: self.tempdir += os.sep self.paths = [] # type: List[str] # create some files and directories in temp_dir for c in string.ascii_lowercase: path = os.path.join(self.tempdir, c) self.paths.append(path) if ord(c) % 2: os.mkdir(path) else: with open(path, 'w'): pass
def test_new_lineage(self, mock_rv): """Test for new_lineage() class method.""" # Mock relevant_values to say everything is relevant here (so we # don't have to mock the parser to help it decide!) mock_rv.side_effect = lambda x: x from certbot import storage result = storage.RenewableCert.new_lineage( "the-lineage.com", b"cert", b"privkey", b"chain", self.config) # This consistency check tests most relevant properties about the # newly created cert lineage. # pylint: disable=protected-access self.assertTrue(result._consistent()) self.assertTrue(os.path.exists(os.path.join( self.config.renewal_configs_dir, "the-lineage.com.conf"))) self.assertTrue(os.path.exists(os.path.join( self.config.live_dir, "README"))) self.assertTrue(os.path.exists(os.path.join( self.config.live_dir, "the-lineage.com", "README"))) self.assertTrue(misc.compare_file_modes(os.stat(result.key_path).st_mode, 0o600)) with open(result.fullchain, "rb") as f: self.assertEqual(f.read(), b"cert" + b"chain") # Let's do it again and make sure it makes a different lineage result = storage.RenewableCert.new_lineage( "the-lineage.com", b"cert2", b"privkey2", b"chain2", self.config) self.assertTrue(os.path.exists(os.path.join( self.config.renewal_configs_dir, "the-lineage.com-0001.conf"))) self.assertTrue(os.path.exists(os.path.join( self.config.live_dir, "the-lineage.com-0001", "README"))) # Now trigger the detection of already existing files os.mkdir(os.path.join( self.config.live_dir, "the-lineage.com-0002")) self.assertRaises(errors.CertStorageError, storage.RenewableCert.new_lineage, "the-lineage.com", b"cert3", b"privkey3", b"chain3", self.config) os.mkdir(os.path.join(self.config.default_archive_dir, "other-example.com")) self.assertRaises(errors.CertStorageError, storage.RenewableCert.new_lineage, "other-example.com", b"cert4", b"privkey4", b"chain4", self.config) # Make sure it can accept renewal parameters result = storage.RenewableCert.new_lineage( "the-lineage.com", b"cert2", b"privkey2", b"chain2", self.config)
def _create_challenge_dirs(self): path_map = self.conf("map") if not path_map: raise errors.PluginError( "Missing parts of webroot configuration; please set either " "--webroot-path and --domains, or --webroot-map. Run with " " --help webroot for examples.") for name, path in path_map.items(): self.full_roots[name] = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH) logger.debug("Creating root challenges validation dir at %s", self.full_roots[name]) # Change the permissions to be writable (GH #1389) # Umask is used instead of chmod to ensure the client can also # run as non-root (GH #1795) old_umask = os.umask(0o022) try: stat_path = os.stat(path) # We ignore the last prefix in the next iteration, # as it does not correspond to a folder path ('/' or 'C:') for prefix in sorted(util.get_prefixes(self.full_roots[name])[:-1], key=len): try: # This is coupled with the "umask" call above because # os.mkdir's "mode" parameter may not always work: # https://docs.python.org/3/library/os.html#os.mkdir os.mkdir(prefix, 0o0755) self._created_dirs.append(prefix) # Set owner as parent directory if possible try: os.chown(prefix, stat_path.st_uid, stat_path.st_gid) except (OSError, AttributeError) as exception: logger.info("Unable to change owner and uid of webroot directory") logger.debug("Error was: %s", exception) except OSError as exception: if exception.errno not in (errno.EEXIST, errno.EISDIR): raise errors.PluginError( "Couldn't create root for {0} http-01 " "challenge responses: {1}".format(name, exception)) finally: os.umask(old_umask)
def setUp(self): super(AuthenticatorTest, self).setUp() self.http_achall = acme_util.HTTP01_A self.dns_achall = acme_util.DNS01_A self.dns_achall_2 = acme_util.DNS01_A_2 self.achalls = [self.http_achall, self.dns_achall, self.dns_achall_2] for d in ["config_dir", "work_dir", "in_progress"]: os.mkdir(os.path.join(self.tempdir, d)) # "backup_dir" and "temp_checkpoint_dir" get created in # certbot.util.make_or_verify_dir() during the Reverter # initialization. self.config = mock.MagicMock( http01_port=0, manual_auth_hook=None, manual_cleanup_hook=None, manual_public_ip_logging_ok=False, noninteractive_mode=False, validate_hooks=False, config_dir=os.path.join(self.tempdir, "config_dir"), work_dir=os.path.join(self.tempdir, "work_dir"), backup_dir=os.path.join(self.tempdir, "backup_dir"), temp_checkpoint_dir=os.path.join( self.tempdir, "temp_checkpoint_dir"), in_progress_dir=os.path.join(self.tempdir, "in_progess")) from certbot.plugins.manual import Authenticator self.auth = Authenticator(self.config, name='manual')
def new_lineage(cls, lineagename, cert, privkey, chain, cli_config): # pylint: disable=too-many-locals """Create a new certificate lineage. Attempts to create a certificate lineage -- enrolled for potential future renewal -- with the (suggested) lineage name lineagename, and the associated cert, privkey, and chain (the associated fullchain will be created automatically). Optional configurator and renewalparams record the configuration that was originally used to obtain this cert, so that it can be reused later during automated renewal. Returns a new RenewableCert object referring to the created lineage. (The actual lineage name, as well as all the relevant file paths, will be available within this object.) :param str lineagename: the suggested name for this lineage (normally the current cert's first subject DNS name) :param str cert: the initial certificate version in PEM format :param str privkey: the private key in PEM format :param str chain: the certificate chain in PEM format :param .NamespaceConfig cli_config: parsed command line arguments :returns: the newly-created RenewalCert object :rtype: :class:`storage.renewableCert` """ # Examine the configuration and find the new lineage's name for i in (cli_config.renewal_configs_dir, cli_config.default_archive_dir, cli_config.live_dir): if not os.path.exists(i): os.makedirs(i, 0o700) logger.debug("Creating directory %s.", i) config_file, config_filename = util.unique_lineage_name( cli_config.renewal_configs_dir, lineagename) base_readme_path = os.path.join(cli_config.live_dir, README) if not os.path.exists(base_readme_path): _write_live_readme_to(base_readme_path, is_base_dir=True) # Determine where on disk everything will go # lineagename will now potentially be modified based on which # renewal configuration file could actually be created lineagename = lineagename_for_filename(config_filename) archive = full_archive_path(None, cli_config, lineagename) live_dir = _full_live_path(cli_config, lineagename) if os.path.exists(archive): config_file.close() raise errors.CertStorageError("archive directory exists for " + lineagename) if os.path.exists(live_dir): config_file.close() raise errors.CertStorageError("live directory exists for " + lineagename) os.mkdir(archive) os.mkdir(live_dir) logger.debug("Archive directory %s and live " "directory %s created.", archive, live_dir) # Put the data into the appropriate files on disk target = dict([(kind, os.path.join(live_dir, kind + ".pem")) for kind in ALL_FOUR]) archive_target = dict([(kind, os.path.join(archive, kind + "1.pem")) for kind in ALL_FOUR]) for kind in ALL_FOUR: os.symlink(_relpath_from_file(archive_target[kind], target[kind]), target[kind]) with open(target["cert"], "wb") as f: logger.debug("Writing certificate to %s.", target["cert"]) f.write(cert) with util.safe_open(archive_target["privkey"], "wb", chmod=BASE_PRIVKEY_MODE) as f: logger.debug("Writing private key to %s.", target["privkey"]) f.write(privkey) # XXX: Let's make sure to get the file permissions right here with open(target["chain"], "wb") as f: logger.debug("Writing chain to %s.", target["chain"]) f.write(chain) with open(target["fullchain"], "wb") as f: # assumes that OpenSSL.crypto.dump_certificate includes # ending newline character logger.debug("Writing full chain to %s.", target["fullchain"]) f.write(cert + chain) # Write a README file to the live directory readme_path = os.path.join(live_dir, README) _write_live_readme_to(readme_path) # Document what we've done in a new renewal config file config_file.close() # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) new_config = write_renewal_config(config_filename, config_filename, archive, target, values) return cls(new_config.filename, cli_config)
def new_lineage(cls, lineagename, cert, privkey, chain, cli_config): # pylint: disable=too-many-locals """Create a new certificate lineage. Attempts to create a certificate lineage -- enrolled for potential future renewal -- with the (suggested) lineage name lineagename, and the associated cert, privkey, and chain (the associated fullchain will be created automatically). Optional configurator and renewalparams record the configuration that was originally used to obtain this cert, so that it can be reused later during automated renewal. Returns a new RenewableCert object referring to the created lineage. (The actual lineage name, as well as all the relevant file paths, will be available within this object.) :param str lineagename: the suggested name for this lineage (normally the current cert's first subject DNS name) :param str cert: the initial certificate version in PEM format :param str privkey: the private key in PEM format :param str chain: the certificate chain in PEM format :param .NamespaceConfig cli_config: parsed command line arguments :returns: the newly-created RenewalCert object :rtype: :class:`storage.renewableCert` """ # Examine the configuration and find the new lineage's name for i in (cli_config.renewal_configs_dir, cli_config.default_archive_dir, cli_config.live_dir): if not os.path.exists(i): os.makedirs(i, 0o700) logger.debug("Creating directory %s.", i) config_file, config_filename = util.unique_lineage_name( cli_config.renewal_configs_dir, lineagename) base_readme_path = os.path.join(cli_config.live_dir, README) if not os.path.exists(base_readme_path): _write_live_readme_to(base_readme_path, is_base_dir=True) # Determine where on disk everything will go # lineagename will now potentially be modified based on which # renewal configuration file could actually be created lineagename = lineagename_for_filename(config_filename) archive = full_archive_path(None, cli_config, lineagename) live_dir = _full_live_path(cli_config, lineagename) if os.path.exists(archive): config_file.close() raise errors.CertStorageError( "archive directory exists for " + lineagename) if os.path.exists(live_dir): config_file.close() raise errors.CertStorageError( "live directory exists for " + lineagename) os.mkdir(archive) os.mkdir(live_dir) logger.debug("Archive directory %s and live " "directory %s created.", archive, live_dir) # Put the data into the appropriate files on disk target = dict([(kind, os.path.join(live_dir, kind + ".pem")) for kind in ALL_FOUR]) archive_target = dict([(kind, os.path.join(archive, kind + "1.pem")) for kind in ALL_FOUR]) for kind in ALL_FOUR: os.symlink(_relpath_from_file(archive_target[kind], target[kind]), target[kind]) with open(target["cert"], "wb") as f: logger.debug("Writing certificate to %s.", target["cert"]) f.write(cert) with util.safe_open(archive_target["privkey"], "wb", chmod=BASE_PRIVKEY_MODE) as f: logger.debug("Writing private key to %s.", target["privkey"]) f.write(privkey) # XXX: Let's make sure to get the file permissions right here with open(target["chain"], "wb") as f: logger.debug("Writing chain to %s.", target["chain"]) f.write(chain) with open(target["fullchain"], "wb") as f: # assumes that OpenSSL.crypto.dump_certificate includes # ending newline character logger.debug("Writing full chain to %s.", target["fullchain"]) f.write(cert + chain) # Write a README file to the live directory readme_path = os.path.join(live_dir, README) _write_live_readme_to(readme_path) # Document what we've done in a new renewal config file config_file.close() # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) new_config = write_renewal_config(config_filename, config_filename, archive, target, values) return cls(new_config.filename, cli_config)