Пример #1
0
def _get_repository_protection_utils():
    """
    Instantiate our repository protection utilities with the config file.

    :return: A 2-tuple of repo_cert_utils and protected_repo_utils
    :rtype:  tuple
    """
    repo_auth_config = SafeConfigParser()
    repo_auth_config.read(constants.REPO_AUTH_CONFIG_FILE)
    repo_cert_utils = RepoCertUtils(repo_auth_config)
    protected_repo_utils = ProtectedRepoUtils(repo_auth_config)

    return repo_cert_utils, protected_repo_utils
Пример #2
0
class TestProtectedRepoUtils(unittest.TestCase):
    def setUp(self):
        if os.path.exists(TEST_FILE):
            os.remove(TEST_FILE)
        self.utils = ProtectedRepoUtils(CONFIG)

    def tearDown(self):
        if os.path.exists(TEST_FILE):
            os.remove(TEST_FILE)
        global_cert_location = CONFIG.get('repos', 'global_cert_location')
        if os.path.exists(global_cert_location):
            shutil.rmtree(global_cert_location)

    def test_add_protected_repo(self):
        """
        Tests adding a protected repo.
        """

        # Setup
        repo_id = 'prot-repo-1'
        relative_path = 'path-1'

        # Test
        self.utils.add_protected_repo(relative_path, repo_id)

        # Verify
        listings = self.utils.read_protected_repo_listings()

        self.assertEqual(1, len(listings))
        self.assertTrue(relative_path in listings)
        self.assertEqual(listings[relative_path], repo_id)

    def test_delete_protected_repo(self):
        """
        Tests deleting an existing protected repo.
        """

        # Setup
        repo_id = 'prot-repo-1'
        relative_path = 'path-1'
        self.utils.add_protected_repo(relative_path, repo_id)

        # Test
        self.utils.delete_protected_repo(relative_path)

        # Verify
        listings = self.utils.read_protected_repo_listings()

        self.assertEqual(0, len(listings))
class TestProtectedRepoUtils(unittest.TestCase):
    def setUp(self):
        if os.path.exists(TEST_FILE):
            os.remove(TEST_FILE)
        self.utils = ProtectedRepoUtils(CONFIG)

    def tearDown(self):
        if os.path.exists(TEST_FILE):
            os.remove(TEST_FILE)
        global_cert_location = CONFIG.get('repos', 'global_cert_location')
        if os.path.exists(global_cert_location):
            shutil.rmtree(global_cert_location)

    def test_add_protected_repo(self):
        """
        Tests adding a protected repo.
        """

        # Setup
        repo_id = 'prot-repo-1'
        relative_path = 'path-1'

        # Test
        self.utils.add_protected_repo(relative_path, repo_id)

        # Verify
        listings = self.utils.read_protected_repo_listings()

        self.assertEqual(1, len(listings))
        self.assertTrue(relative_path in listings)
        self.assertEqual(listings[relative_path], repo_id)

    def test_delete_protected_repo(self):
        """
        Tests deleting an existing protected repo.
        """

        # Setup
        repo_id = 'prot-repo-1'
        relative_path = 'path-1'
        self.utils.add_protected_repo(relative_path, repo_id)

        # Test
        self.utils.delete_protected_repo(relative_path)

        # Verify
        listings = self.utils.read_protected_repo_listings()

        self.assertEqual(0, len(listings))
Пример #4
0
 def __init__(self, config):
     self.config = config
     self.repo_cert_utils = RepoCertUtils(config)
     self.protected_repo_utils = ProtectedRepoUtils(config)
     self.repo_url_prefixes = self._get_repo_url_prefixes_from_config(
         config)
Пример #5
0
class OidValidator:
    def __init__(self, config):
        self.config = config
        self.repo_cert_utils = RepoCertUtils(config)
        self.protected_repo_utils = ProtectedRepoUtils(config)
        self.repo_url_prefixes = self._get_repo_url_prefixes_from_config(
            config)

    def is_valid(self, dest, cert_pem, log_func):
        '''
        Returns if the specified  certificate should be able to access a certain URL.

        @param dest: destination URL trying to be accessed
        @type  dest: string

        @param cert_pem: PEM encoded client certificate sent with the request
        @type  cert_pem: string
        '''
        # Determine whether we should check the client certificates' signatures.
        try:
            verify_ssl = self.config.getboolean('main', 'verify_ssl')
        except NoOptionError:
            verify_ssl = True

        # Load the repo credentials if they exist
        repo_bundle = self._matching_repo_bundle(dest, self.repo_url_prefixes)
        # Load the global repo auth cert bundle and check it's CA against the client cert
        # if it didn't already pass the individual auth check
        global_bundle = self.repo_cert_utils.read_global_cert_bundle(
            log_func=log_func, pieces=['ca'])
        # If there were neither global nor repo auth credentials, auth passes.
        if global_bundle is None and repo_bundle is None:
            if self.repo_cert_utils.log_failed_cert_verbose:
                log_func(
                    'No global bundle or repo bundle found. Allowing request.')
            return True

        if verify_ssl:
            passes_individual_ca = False
            if repo_bundle is not None:

                # If there is an individual bundle but no client certificate has been specified,
                # they are invalid
                if cert_pem == '':
                    return False

                # Make sure the client cert is signed by the correct CA
                is_valid = self.repo_cert_utils.validate_certificate_pem(
                    cert_pem, repo_bundle['ca'], log_func=log_func)
                if not is_valid:
                    log_func(
                        'Client certificate did not match the repo consumer CA certificate'
                    )
                    return False
                else:
                    # Indicate it passed individual check so we don't run the global too
                    passes_individual_ca = True

            if not passes_individual_ca and global_bundle is not None:

                # If there is a global repo bundle but no client certificate has been specified,
                # they are invalid
                if cert_pem == '':
                    return False

                # Make sure the client cert is signed by the correct CA
                is_valid = self.repo_cert_utils.validate_certificate_pem(
                    cert_pem, global_bundle['ca'], log_func=log_func)
                if not is_valid:
                    log_func(
                        'Client certificate did not match the global repo auth CA certificate'
                    )
                    return False

        # If the credentials were specified for either case, apply the OID checks.
        is_valid = self._check_extensions(cert_pem, dest, log_func,
                                          self.repo_url_prefixes)
        if not is_valid:
            log_func(
                "Client certificate failed extension check for destination: %s"
                % (dest))
        elif self.repo_cert_utils.log_failed_cert_verbose:
            log_func("OID validation successful for request")
        return is_valid

    def _matching_repo_bundle(self, dest, repo_url_prefixes):

        # Load the path -> repo ID mappings
        prot_repos = self.protected_repo_utils.read_protected_repo_listings()

        repo_id = None
        for prefix in repo_url_prefixes:
            # Extract the repo portion of the URL
            # Example: https://guardian/pulp/repos/my-repo/pulp/fedora-13/i386/repodata/repomd.xml
            #   Repo Portion: /my-repo/pulp/fedora-13/i386/repodata/repomd.xml
            repo_url = dest[dest.find(prefix) + len(prefix):]

            # If the repo portion of the URL starts with any of the protected relative URLs,
            # it is considered to be a request against that protected repo
            for relative_repo_url in prot_repos.keys():

                # Relative URL is inconsistent in Pulp, so a simple "startswith" tends to
                # break. Changing this to a find helps remove issues where the leading /
                # is missing, present, or duplicated.
                if repo_url.find(relative_repo_url) != -1:
                    repo_id = prot_repos[relative_repo_url]
                    break

            # break out of checking URLs once we find a matching repo id
            if repo_id:
                break

        # if we did not find a repo, return None
        if not repo_id:
            return None
        bundle = self.repo_cert_utils.read_consumer_cert_bundle(
            repo_id, ['ca'])
        return bundle

    def _check_extensions(self, cert_pem, dest, log_func, repo_url_prefixes):
        """
        Checks the requested destination path against the entitlement cert.

        :param cert_pem: certificate as PEM
        :type  cert_pem: str
        :param dest: path of desired destination
        :type  dest: str
        :param log_func: function used for logging
        :type  log_func: callable taking 1 argument of type basestring
        :param repo_url_prefixes: list of url prefixes to strip off before checking against cert
        :type  repo_url_prefixes: list of str
        :return: True iff request is authorized, else False
        :rtype:  bool
        """
        cert = certificate.create_from_pem(cert_pem)

        valid = False
        for prefix in repo_url_prefixes:
            if dest.startswith(prefix):
                # rhsm throws a ValueError if there's no leading /. Amusingly, it immediately
                # strips it off.
                repo_dest = os.path.join('/', os.path.relpath(dest, prefix))
                try:
                    valid = cert.check_path(repo_dest)
                except AttributeError:
                    # not an entitlement certificate, so no entitlements
                    log_func(
                        'The provided client certificate is not an entitlement certificate.\n'
                    )
                # if we have a valid url check, no need to continue
                if valid:
                    break

        if not valid:
            log_func('Request denied to destination [%s]' % dest)

        return valid

    def _get_repo_url_prefixes_from_config(self, config):
        """
        Obtain the list of repo URLs prefixes from the conf file. If none
        exist, just return "/pulp/repos" as the only entry.
        """
        try:
            prefixes = config.get('main', 'repo_url_prefixes').split(',')
        except NoOptionError:
            prefixes = ["/pulp/repos", "/pulp/ostree/web"]

        return prefixes
Пример #6
0
 def __init__(self, config):
     self.config = config
     self.repo_cert_utils = RepoCertUtils(config)
     self.protected_repo_utils = ProtectedRepoUtils(config)
     self.repo_url_prefixes = self._get_repo_url_prefixes_from_config(config)
Пример #7
0
class OidValidator:
    def __init__(self, config):
        self.config = config
        self.repo_cert_utils = RepoCertUtils(config)
        self.protected_repo_utils = ProtectedRepoUtils(config)
        self.repo_url_prefixes = self._get_repo_url_prefixes_from_config(config)

    def is_valid(self, dest, cert_pem, log_func):
        '''
        Returns if the specified  certificate should be able to access a certain URL.

        @param dest: destination URL trying to be accessed
        @type  dest: string

        @param cert_pem: PEM encoded client certificate sent with the request
        @type  cert_pem: string
        '''
        # Determine whether we should check the client certificates' signatures.
        try:
            verify_ssl = self.config.getboolean('main', 'verify_ssl')
        except NoOptionError:
            verify_ssl = True

        # Load the repo credentials if they exist
        repo_bundle = self._matching_repo_bundle(dest, self.repo_url_prefixes)
        # Load the global repo auth cert bundle and check it's CA against the client cert
        # if it didn't already pass the individual auth check
        global_bundle = self.repo_cert_utils.read_global_cert_bundle(log_func=log_func,
                                                                     pieces=['ca'])
        # If there were neither global nor repo auth credentials, auth passes.
        if global_bundle is None and repo_bundle is None:
            if self.repo_cert_utils.log_failed_cert_verbose:
                log_func('No global bundle or repo bundle found. Allowing request.')
            return True

        if verify_ssl:
            passes_individual_ca = False
            if repo_bundle is not None:

                # If there is an individual bundle but no client certificate has been specified,
                # they are invalid
                if cert_pem == '':
                    return False

                # Make sure the client cert is signed by the correct CA
                is_valid = self.repo_cert_utils.validate_certificate_pem(
                    cert_pem, repo_bundle['ca'], log_func=log_func)
                if not is_valid:
                    log_func('Client certificate did not match the repo consumer CA certificate')
                    return False
                else:
                    # Indicate it passed individual check so we don't run the global too
                    passes_individual_ca = True

            if not passes_individual_ca and global_bundle is not None:

                # If there is a global repo bundle but no client certificate has been specified,
                # they are invalid
                if cert_pem == '':
                    return False

                # Make sure the client cert is signed by the correct CA
                is_valid = self.repo_cert_utils.validate_certificate_pem(
                    cert_pem, global_bundle['ca'], log_func=log_func)
                if not is_valid:
                    log_func('Client certificate did not match the global repo auth CA certificate')
                    return False

        # If the credentials were specified for either case, apply the OID checks.
        is_valid = self._check_extensions(cert_pem, dest, log_func, self.repo_url_prefixes)
        if not is_valid:
            log_func("Client certificate failed extension check for destination: %s" % (dest))
        elif self.repo_cert_utils.log_failed_cert_verbose:
            log_func("OID validation successful for request")
        return is_valid

    def _matching_repo_bundle(self, dest, repo_url_prefixes):

        # Load the path -> repo ID mappings
        prot_repos = self.protected_repo_utils.read_protected_repo_listings()

        repo_id = None
        for prefix in repo_url_prefixes:
            # Extract the repo portion of the URL
            # Example: https://guardian/pulp/repos/my-repo/pulp/fedora-13/i386/repodata/repomd.xml
            #   Repo Portion: /my-repo/pulp/fedora-13/i386/repodata/repomd.xml
            repo_url = dest[dest.find(prefix) + len(prefix):]

            # If the repo portion of the URL starts with any of the protected relative URLs,
            # it is considered to be a request against that protected repo
            for relative_repo_url in prot_repos.keys():

                # Relative URL is inconsistent in Pulp, so a simple "startswith" tends to
                # break. Changing this to a find helps remove issues where the leading /
                # is missing, present, or duplicated.
                if repo_url.find(relative_repo_url) != -1:
                    repo_id = prot_repos[relative_repo_url]
                    break

            # break out of checking URLs once we find a matching repo id
            if repo_id:
                break

        # if we did not find a repo, return None
        if not repo_id:
            return None
        bundle = self.repo_cert_utils.read_consumer_cert_bundle(repo_id, ['ca'])
        return bundle

    def _check_extensions(self, cert_pem, dest, log_func, repo_url_prefixes):
        """
        Checks the requested destination path against the entitlement cert.

        :param cert_pem: certificate as PEM
        :type  cert_pem: str
        :param dest: path of desired destination
        :type  dest: str
        :param log_func: function used for logging
        :type  log_func: callable taking 1 argument of type basestring
        :param repo_url_prefixes: list of url prefixes to strip off before checking against cert
        :type  repo_url_prefixes: list of str
        :return: True iff request is authorized, else False
        :rtype:  bool
        """
        cert = certificate.create_from_pem(cert_pem)

        valid = False
        for prefix in repo_url_prefixes:
            if dest.startswith(prefix):
                # rhsm throws a ValueError if there's no leading /. Amusingly, it immediately
                # strips it off.
                repo_dest = os.path.join('/', os.path.relpath(dest, prefix))
                try:
                    valid = cert.check_path(repo_dest)
                except AttributeError:
                    # not an entitlement certificate, so no entitlements
                    log_func('The provided client certificate is not an entitlement certificate.\n')
                # if we have a valid url check, no need to continue
                if valid:
                    break

        if not valid:
            log_func('Request denied to destination [%s]' % dest)

        return valid

    def _get_repo_url_prefixes_from_config(self, config):
        """
        Obtain the list of repo URLs prefixes from the conf file. If none
        exist, just return "/pulp/repos" as the only entry.
        """
        try:
            prefixes = config.get('main', 'repo_url_prefixes').split(',')
        except NoOptionError:
            prefixes = ["/pulp/repos", "/pulp/ostree/web"]

        return prefixes
Пример #8
0
 def setUp(self):
     if os.path.exists(TEST_FILE):
         os.remove(TEST_FILE)
     self.utils = ProtectedRepoUtils(CONFIG)
 def setUp(self):
     if os.path.exists(TEST_FILE):
         os.remove(TEST_FILE)
     self.utils = ProtectedRepoUtils(CONFIG)