def cleanup(self, achalls): # pylint: disable=missing-function-docstring for achall in achalls: root_path = self.full_roots.get(achall.domain, None) if root_path is not None: validation_path = self._get_validation_path(root_path, achall) logger.debug("Removing %s", validation_path) os.remove(validation_path) self.performed[root_path].remove(achall) if not filesystem.POSIX_MODE: web_config_path = os.path.join(root_path, "web.config") if os.path.exists(web_config_path): sha256sum = crypto_util.sha256sum(web_config_path) if sha256sum in _WEB_CONFIG_SHA256SUMS: logger.info("Cleaning web.config file generated by Certbot in %s.", root_path) os.remove(web_config_path) else: logger.info("Not cleaning up the web.config file in %s " "because it is not generated by Certbot.", root_path) not_removed: List[str] = [] while self._created_dirs: path = self._created_dirs.pop() try: os.rmdir(path) except OSError as exc: not_removed.insert(0, path) logger.info("Challenge directory %s was not empty, didn't remove", path) logger.debug("Error was: %s", exc) self._created_dirs = not_removed logger.debug("All challenges cleaned up")
def setUp(self, test_dir="debian_apache_2_4/multiple_vhosts", config_root="debian_apache_2_4/multiple_vhosts/apache2", vhost_root="debian_apache_2_4/multiple_vhosts/apache2/sites-available"): # pylint: disable=arguments-differ super(ApacheTest, self).setUp() self.temp_dir, self.config_dir, self.work_dir = common.dir_setup( test_dir=test_dir, pkg="certbot_apache.tests") self.config_path = os.path.join(self.temp_dir, config_root) self.vhost_path = os.path.join(self.temp_dir, vhost_root) self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector( "rsa512_key.pem")) self.config = get_apache_configurator(self.config_path, vhost_root, self.config_dir, self.work_dir) # Make sure all vhosts in sites-enabled are symlinks (Python packaging # does not preserve symlinks) sites_enabled = os.path.join(self.config_path, "sites-enabled") if not os.path.exists(sites_enabled): return for vhost_basename in os.listdir(sites_enabled): # Keep the one non-symlink test vhost in place if vhost_basename == "non-symlink.conf": continue vhost = os.path.join(sites_enabled, vhost_basename) if not os.path.islink(vhost): # pragma: no cover os.remove(vhost) target = os.path.join( os.path.pardir, "sites-available", vhost_basename) os.symlink(target, vhost)
def test_no_file(self): # prepare should have placed a file there self._assert_current_file() os.remove(self.config.mod_ssl_conf) self.assertFalse(os.path.isfile(self.config.mod_ssl_conf)) self._call() self._assert_current_file()
def safely_remove(path): """Remove a file that may not exist.""" try: os.remove(path) except OSError as err: if err.errno != errno.ENOENT: raise
def setUp( self, test_dir="debian_apache_2_4/multiple_vhosts", config_root="debian_apache_2_4/multiple_vhosts/apache2", vhost_root="debian_apache_2_4/multiple_vhosts/apache2/sites-available" ): # pylint: disable=arguments-differ self.temp_dir, self.config_dir, self.work_dir = common.dir_setup( test_dir=test_dir, pkg=__name__) self.config_path = os.path.join(self.temp_dir, config_root) self.vhost_path = os.path.join(self.temp_dir, vhost_root) self.rsa512jwk = jose.JWKRSA.load( test_util.load_vector("rsa512_key.pem")) self.config = get_apache_configurator(self.config_path, vhost_root, self.config_dir, self.work_dir) # Make sure all vhosts in sites-enabled are symlinks (Python packaging # does not preserve symlinks) sites_enabled = os.path.join(self.config_path, "sites-enabled") if not os.path.exists(sites_enabled): return for vhost_basename in os.listdir(sites_enabled): # Keep the one non-symlink test vhost in place if vhost_basename == "non-symlink.conf": continue vhost = os.path.join(sites_enabled, vhost_basename) if not os.path.islink(vhost): # pragma: no cover os.remove(vhost) target = os.path.join(os.path.pardir, "sites-available", vhost_basename) os.symlink(target, vhost)
def safely_remove(path): """Remove a file that may not exist.""" try: os.remove(path) except OSError as err: if err.errno != errno.ENOENT: raise
def test_no_file(self): # prepare should have placed a file there self._assert_current_file() os.remove(self.config.mod_ssl_conf) self.assertFalse(os.path.isfile(self.config.mod_ssl_conf)) self._call() self._assert_current_file()
def test_no_hooks(self): self.config.pre_hook = None self.config.verb = "renew" os.remove(self.dir_hook) with mock.patch("certbot.hooks.logger") as mock_logger: mock_execute = self._call_with_mock_execute(self.config) self.assertFalse(mock_execute.called) self.assertFalse(mock_logger.info.called)
def test_no_readme_file(self): os.remove(os.path.join( self.config.live_dir, "example.org", "README")) self._call() self.assertFalse(os.path.exists(self.config_file.filename)) self.assertFalse(os.path.exists(os.path.join( self.config.live_dir, "example.org"))) self.assertFalse(os.path.exists(os.path.join( self.config.config_dir, "archive", "example.org")))
def test_no_hooks(self): self.config.renew_hook = None os.remove(self.dir_hook) with mock.patch("certbot.hooks.logger") as mock_logger: mock_execute = self._call_with_mock_execute( self.config, ["example.org"], "/foo/bar") self.assertFalse(mock_execute.called) self.assertFalse(mock_logger.info.called)
def test_no_readme_file(self): os.remove(os.path.join( self.config.live_dir, "example.org", "README")) self._call() self.assertFalse(os.path.exists(self.config_file.filename)) self.assertFalse(os.path.exists(os.path.join( self.config.live_dir, "example.org"))) self.assertFalse(os.path.exists(os.path.join( self.config.config_dir, "archive", "example.org")))
def test_no_hooks(self): self.config.renew_hook = None os.remove(self.dir_hook) with mock.patch("certbot._internal.hooks.logger") as mock_logger: mock_execute = self._call_with_mock_execute( self.config, ["example.org"], "/foo/bar") self.assertFalse(mock_execute.called) self.assertFalse(mock_logger.info.called)
def test_no_hooks(self): self.config.pre_hook = None self.config.verb = "renew" os.remove(self.dir_hook) with mock.patch("certbot._internal.hooks.logger") as mock_logger: mock_execute = self._call_with_mock_execute(self.config) self.assertFalse(mock_execute.called) self.assertFalse(mock_logger.info.called)
def handle_rw_files(_, path, __): """Handle read-only files, that will fail to be removed on Windows.""" filesystem.chmod(path, stat.S_IWRITE) try: os.remove(path) except (IOError, OSError): # TODO: remote the try/except once all logic from windows file permissions is merged if os.name != 'nt': raise
def _retry_obtain_certificate( self, key: util.Key, csr: util.CSR, domains: List[str], successful_domains: List[str] ) -> Tuple[bytes, bytes, util.Key, util.CSR]: failed_domains = [d for d in domains if d not in successful_domains] domains_list = ", ".join(failed_domains) display_util.notify( "Unable to obtain a certificate with every requested " f"domain. Retrying without: {domains_list}") if not self.config.dry_run: os.remove(key.file) os.remove(csr.file) return self.obtain_certificate(successful_domains)
def release(self): """Release the lock.""" try: msvcrt.locking(self._fd, msvcrt.LK_UNLCK, 1) os.close(self._fd) try: os.remove(self._path) except OSError as e: # If the lock file cannot be removed, it is not a big deal. # Likely another instance is acquiring the lock we just released. logger.debug(str(e)) finally: self._fd = None
def release(self): """Release the lock.""" try: msvcrt.locking(self._fd, msvcrt.LK_UNLCK, 1) os.close(self._fd) try: os.remove(self._path) except OSError as e: # If the lock file cannot be removed, it is not a big deal. # Likely another instance is acquiring the lock we just released. logger.debug(str(e)) finally: self._fd = None
def release(self): """Release the lock.""" try: # This "type: ignore" is currently needed because msvcrt methods # are only defined on Windows. See # https://github.com/python/typeshed/blob/16ae4c61201cd8b96b8b22cdfb2ab9e89ba5bcf2/stdlib/msvcrt.pyi. msvcrt.locking(self._fd, msvcrt.LK_UNLCK, 1) # type: ignore os.close(self._fd) try: os.remove(self._path) except OSError as e: # If the lock file cannot be removed, it is not a big deal. # Likely another instance is acquiring the lock we just released. logger.debug(str(e)) finally: self._fd = None
def close(self): """Close the handler and the temporary log file. The temporary log file is deleted if it wasn't used. """ self.acquire() try: # StreamHandler.close() doesn't close the stream to allow a # stream like stderr to be used self.stream.close() if self._delete: os.remove(self.path) self._delete = False super(TempHandler, self).close() finally: self.release()
def release(self): """Release the lock.""" try: # The need for this "type: ignore" was fixed in # https://github.com/python/typeshed/pull/3607 and included in # newer versions of mypy so it can be removed when mypy is # upgraded. msvcrt.locking(self._fd, msvcrt.LK_UNLCK, 1) # type: ignore os.close(self._fd) try: os.remove(self._path) except OSError as e: # If the lock file cannot be removed, it is not a big deal. # Likely another instance is acquiring the lock we just released. logger.debug(str(e)) finally: self._fd = None
def cleanup(self, achalls): # pylint: disable=missing-function-docstring for achall in achalls: root_path = self.full_roots.get(achall.domain, None) if root_path is not None: validation_path = self._get_validation_path(root_path, achall) logger.debug("Removing %s", validation_path) os.remove(validation_path) self.performed[root_path].remove(achall) not_removed = [] # type: List[str] while self._created_dirs: path = self._created_dirs.pop() try: os.rmdir(path) except OSError as exc: not_removed.insert(0, path) logger.info("Challenge directory %s was not empty, didn't remove", path) logger.debug("Error was: %s", exc) self._created_dirs = not_removed logger.debug("All challenges cleaned up")
def cleanup(self, achalls): # pylint: disable=missing-docstring for achall in achalls: root_path = self.full_roots.get(achall.domain, None) if root_path is not None: validation_path = self._get_validation_path(root_path, achall) logger.debug("Removing %s", validation_path) os.remove(validation_path) self.performed[root_path].remove(achall) not_removed = [] # type: List[str] while self._created_dirs: path = self._created_dirs.pop() try: os.rmdir(path) except OSError as exc: not_removed.insert(0, path) logger.info("Challenge directory %s was not empty, didn't remove", path) logger.debug("Error was: %s", exc) self._created_dirs = not_removed logger.debug("All challenges cleaned up")
def release(self) -> None: """Remove, close, and release the lock file.""" # It is important the lock file is removed before it's released, # otherwise: # # process A: open lock file # process B: release lock file # process A: lock file # process A: check device and inode # process B: delete file # process C: open and lock a different file at the same path try: os.remove(self._path) finally: # Following check is done to make mypy happy: it ensure that self._fd, marked # as Optional[int] is effectively int to make it compatible with os.close signature. if self._fd is None: # pragma: no cover raise TypeError('Error, self._fd is None.') try: os.close(self._fd) finally: self._fd = None
def release(self): # type: () -> None """Remove, close, and release the lock file.""" # It is important the lock file is removed before it's released, # otherwise: # # process A: open lock file # process B: release lock file # process A: lock file # process A: check device and inode # process B: delete file # process C: open and lock a different file at the same path try: os.remove(self._path) finally: # Following check is done to make mypy happy: it ensure that self._fd, marked # as Optional[int] is effectively int to make it compatible with os.close signature. if self._fd is None: # pragma: no cover raise TypeError('Error, self._fd is None.') try: os.close(self._fd) finally: self._fd = None
def _remove_contained_files(self, file_list): # pylint: disable=no-self-use """Erase all files contained within file_list. :param str file_list: file containing list of file paths to be deleted :returns: Success :rtype: bool :raises certbot.errors.ReverterError: If all files within file_list cannot be removed """ # Check to see that file exists to differentiate can't find file_list # and can't remove filepaths within file_list errors. if not os.path.isfile(file_list): return False try: with open(file_list, "r") as list_fd: filepaths = list_fd.read().splitlines() for path in filepaths: # Files are registered before they are added... so # check to see if file exists first if os.path.lexists(path): os.remove(path) else: logger.warning( "File: %s - Could not be found to be deleted %s - " "Certbot probably shut down unexpectedly", os.linesep, path) except (IOError, OSError): logger.critical( "Unable to remove filepaths contained within %s", file_list) raise errors.ReverterError( "Unable to remove filepaths contained within " "{0}".format(file_list)) return True
def _remove_contained_files(self, file_list): # pylint: disable=no-self-use """Erase all files contained within file_list. :param str file_list: file containing list of file paths to be deleted :returns: Success :rtype: bool :raises certbot.errors.ReverterError: If all files within file_list cannot be removed """ # Check to see that file exists to differentiate can't find file_list # and can't remove filepaths within file_list errors. if not os.path.isfile(file_list): return False try: with open(file_list, "r") as list_fd: filepaths = list_fd.read().splitlines() for path in filepaths: # Files are registered before they are added... so # check to see if file exists first if os.path.lexists(path): os.remove(path) else: logger.warning( "File: %s - Could not be found to be deleted %s - " "Certbot probably shut down unexpectedly", os.linesep, path) except (IOError, OSError): logger.critical("Unable to remove filepaths contained within %s", file_list) raise errors.ReverterError( "Unable to remove filepaths contained within " "{0}".format(file_list)) return True
def test_renew_no_dir_hook(self): os.remove(self.dir_hook) self._test_renew_common([self.config.post_hook])
def delete_and_stat(path): """Wrap os.stat and maybe delete the file first.""" if path == self.lock_path and should_delete.pop(0): os.remove(path) return stat(path)
def delete_and_stat(path): """Wrap os.stat and maybe delete the file first.""" if path == self.lock_path and should_delete.pop(0): os.remove(path) return stat(path)
def handle_rw_files(_, path, __): """Handle read-only files, that will fail to be removed on Windows.""" os.chmod(path, stat.S_IWRITE) os.remove(path)
def delete_files(config, certname): """Delete all files related to the certificate. If some files are not found, ignore them and continue. """ renewal_filename = renewal_file_for_certname(config, certname) # file exists full_default_archive_dir = full_archive_path(None, config, certname) full_default_live_dir = _full_live_path(config, certname) try: renewal_config = configobj.ConfigObj(renewal_filename) except configobj.ConfigObjError: # config is corrupted logger.warning( "Could not parse %s. You may wish to manually " "delete the contents of %s and %s.", renewal_filename, full_default_live_dir, full_default_archive_dir) raise errors.CertStorageError( "error parsing {0}".format(renewal_filename)) finally: # we couldn't read it, but let's at least delete it # if this was going to fail, it already would have. os.remove(renewal_filename) logger.debug("Removed %s", renewal_filename) # cert files and (hopefully) live directory # it's not guaranteed that the files are in our default storage # structure. so, first delete the cert files. directory_names = set() for kind in ALL_FOUR: link = renewal_config.get(kind) try: os.remove(link) logger.debug("Removed %s", link) except OSError: logger.debug("Unable to delete %s", link) directory = os.path.dirname(link) directory_names.add(directory) # if all four were in the same directory, and the only thing left # is the README file (or nothing), delete that directory. # this will be wrong in very few but some cases. if len(directory_names) == 1: # delete the README file directory = directory_names.pop() readme_path = os.path.join(directory, README) try: os.remove(readme_path) logger.debug("Removed %s", readme_path) except OSError: logger.debug("Unable to delete %s", readme_path) # if it's now empty, delete the directory try: os.rmdir(directory) # only removes empty directories logger.debug("Removed %s", directory) except OSError: logger.debug("Unable to remove %s; may not be empty.", directory) # archive directory try: archive_path = full_archive_path(renewal_config, config, certname) shutil.rmtree(archive_path) logger.debug("Removed %s", archive_path) except OSError: logger.debug("Unable to remove %s", archive_path)
def test_find_config_root_no_root(self): # pylint: disable=protected-access os.remove(self.parser.loc["root"]) self.assertRaises( errors.NoInstallationError, self.parser._find_config_root)
def handle_rw_files(_, path, __): """Handle read-only files, that will fail to be removed on Windows.""" os.chmod(path, stat.S_IWRITE) os.remove(path)
def obtain_certificate(self, domains, old_keypath=None): """Obtains a certificate from the ACME server. `.register` must be called before `.obtain_certificate` :param list domains: domains to get a certificate :returns: certificate as PEM string, chain as PEM string, newly generated private key (`.util.Key`), and DER-encoded Certificate Signing Request (`.util.CSR`). :rtype: tuple """ # We need to determine the key path, key PEM data, CSR path, # and CSR PEM data. For a dry run, the paths are None because # they aren't permanently saved to disk. For a lineage with # --reuse-key, the key path and PEM data are derived from an # existing file. if old_keypath is not None: # We've been asked to reuse a specific existing private key. # Therefore, we'll read it now and not generate a new one in # either case below. # # We read in bytes here because the type of `key.pem` # created below is also bytes. with open(old_keypath, "rb") as f: keypath = old_keypath keypem = f.read() key = util.Key(file=keypath, pem=keypem) # type: Optional[util.Key] logger.info("Reusing existing private key from %s.", old_keypath) else: # The key is set to None here but will be created below. key = None # Create CSR from names if self.config.dry_run: key = key or util.Key(file=None, pem=crypto_util.make_key(self.config.rsa_key_size)) csr = util.CSR(file=None, form="pem", data=acme_crypto_util.make_csr( key.pem, domains, self.config.must_staple)) else: key = key or crypto_util.init_save_key(self.config.rsa_key_size, self.config.key_dir) csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir) orderr = self._get_order_and_authorizations(csr.data, self.config.allow_subset_of_names) authzr = orderr.authorizations auth_domains = set(a.body.identifier.value for a in authzr) # pylint: disable=not-an-iterable successful_domains = [d for d in domains if d in auth_domains] # allow_subset_of_names is currently disabled for wildcard # certificates. The reason for this and checking allow_subset_of_names # below is because successful_domains == domains is never true if # domains contains a wildcard because the ACME spec forbids identifiers # in authzs from containing a wildcard character. if self.config.allow_subset_of_names and successful_domains != domains: if not self.config.dry_run: os.remove(key.file) os.remove(csr.file) return self.obtain_certificate(successful_domains) else: cert, chain = self.obtain_certificate_from_csr(csr, orderr) return cert, chain, key, csr
def test_renew_no_hooks(self): self.config.post_hook = None os.remove(self.dir_hook) self._test_renew_common([])
def test_no_renewal_config(self): os.remove(self.config_file.filename) self.assertRaises(errors.CertStorageError, self._call) self.assertTrue( os.path.exists(os.path.join(self.config.live_dir, "example.org"))) self.assertFalse(os.path.exists(self.config_file.filename))
def delete_files(config, certname): """Delete all files related to the certificate. If some files are not found, ignore them and continue. """ renewal_filename = renewal_file_for_certname(config, certname) # file exists full_default_archive_dir = full_archive_path(None, config, certname) full_default_live_dir = _full_live_path(config, certname) try: renewal_config = configobj.ConfigObj(renewal_filename) except configobj.ConfigObjError: # config is corrupted logger.warning("Could not parse %s. You may wish to manually " "delete the contents of %s and %s.", renewal_filename, full_default_live_dir, full_default_archive_dir) raise errors.CertStorageError( "error parsing {0}".format(renewal_filename)) finally: # we couldn't read it, but let's at least delete it # if this was going to fail, it already would have. os.remove(renewal_filename) logger.debug("Removed %s", renewal_filename) # cert files and (hopefully) live directory # it's not guaranteed that the files are in our default storage # structure. so, first delete the cert files. directory_names = set() for kind in ALL_FOUR: link = renewal_config.get(kind) try: os.remove(link) logger.debug("Removed %s", link) except OSError: logger.debug("Unable to delete %s", link) directory = os.path.dirname(link) directory_names.add(directory) # if all four were in the same directory, and the only thing left # is the README file (or nothing), delete that directory. # this will be wrong in very few but some cases. if len(directory_names) == 1: # delete the README file directory = directory_names.pop() readme_path = os.path.join(directory, README) try: os.remove(readme_path) logger.debug("Removed %s", readme_path) except OSError: logger.debug("Unable to delete %s", readme_path) # if it's now empty, delete the directory try: os.rmdir(directory) # only removes empty directories logger.debug("Removed %s", directory) except OSError: logger.debug("Unable to remove %s; may not be empty.", directory) # archive directory try: archive_path = full_archive_path(renewal_config, config, certname) shutil.rmtree(archive_path) logger.debug("Removed %s", archive_path) except OSError: logger.debug("Unable to remove %s", archive_path)
def test_renew_no_dir_hook(self): os.remove(self.dir_hook) self._test_renew_common([self.config.post_hook])
def test_no_delete(self): self.handler.emit(mock.MagicMock()) self.handler.close() self.assertTrue(os.path.exists(self.handler.path)) os.remove(self.handler.path)
def test_renew_no_hooks(self): self.config.post_hook = None os.remove(self.dir_hook) self._test_renew_common([])
def test_find_config_root_no_root(self): # pylint: disable=protected-access os.remove(self.parser.loc["root"]) self.assertRaises(errors.NoInstallationError, self.parser._find_config_root)
def test_no_delete(self): self.handler.emit(mock.MagicMock()) self.handler.close() self.assertTrue(os.path.exists(self.handler.path)) os.remove(self.handler.path)
def obtain_certificate(self, domains, old_keypath=None): """Obtains a certificate from the ACME server. `.register` must be called before `.obtain_certificate` :param list domains: domains to get a certificate :returns: certificate as PEM string, chain as PEM string, newly generated private key (`.util.Key`), and DER-encoded Certificate Signing Request (`.util.CSR`). :rtype: tuple """ # We need to determine the key path, key PEM data, CSR path, # and CSR PEM data. For a dry run, the paths are None because # they aren't permanently saved to disk. For a lineage with # --reuse-key, the key path and PEM data are derived from an # existing file. if old_keypath is not None: # We've been asked to reuse a specific existing private key. # Therefore, we'll read it now and not generate a new one in # either case below. # # We read in bytes here because the type of `key.pem` # created below is also bytes. with open(old_keypath, "rb") as f: keypath = old_keypath keypem = f.read() key: Optional[util.Key] = util.Key(file=keypath, pem=keypem) logger.info("Reusing existing private key from %s.", old_keypath) else: # The key is set to None here but will be created below. key = None key_size = self.config.rsa_key_size elliptic_curve = "secp256r1" # key-type defaults to a list, but we are only handling 1 currently if isinstance(self.config.key_type, list): self.config.key_type = self.config.key_type[0] if self.config.elliptic_curve and self.config.key_type == 'ecdsa': elliptic_curve = self.config.elliptic_curve self.config.auth_chain_path = "./chain-ecdsa.pem" self.config.auth_cert_path = "./cert-ecdsa.pem" self.config.key_path = "./key-ecdsa.pem" elif self.config.rsa_key_size and self.config.key_type.lower( ) == 'rsa': key_size = self.config.rsa_key_size # Create CSR from names if self.config.dry_run: key = key or util.Key( file=None, pem=crypto_util.make_key( bits=key_size, elliptic_curve=elliptic_curve, key_type=self.config.key_type, ), ) csr = util.CSR(file=None, form="pem", data=acme_crypto_util.make_csr( key.pem, domains, self.config.must_staple)) else: key = key or crypto_util.generate_key( key_size=key_size, key_dir=self.config.key_dir, key_type=self.config.key_type, elliptic_curve=elliptic_curve, strict_permissions=self.config.strict_permissions, ) csr = crypto_util.generate_csr(key, domains, self.config.csr_dir, self.config.must_staple, self.config.strict_permissions) orderr = self._get_order_and_authorizations( csr.data, self.config.allow_subset_of_names) authzr = orderr.authorizations auth_domains = set(a.body.identifier.value for a in authzr) successful_domains = [d for d in domains if d in auth_domains] # allow_subset_of_names is currently disabled for wildcard # certificates. The reason for this and checking allow_subset_of_names # below is because successful_domains == domains is never true if # domains contains a wildcard because the ACME spec forbids identifiers # in authzs from containing a wildcard character. if self.config.allow_subset_of_names and successful_domains != domains: if not self.config.dry_run: os.remove(key.file) os.remove(csr.file) return self.obtain_certificate(successful_domains) else: cert, chain = self.obtain_certificate_from_csr(csr, orderr) return cert, chain, key, csr
def test_no_renewal_config(self): os.remove(self.config_file.filename) self.assertRaises(errors.CertStorageError, self._call) self.assertTrue(os.path.exists(os.path.join( self.config.live_dir, "example.org"))) self.assertFalse(os.path.exists(self.config_file.filename))