def test_service_account_has_role_in_service_policy(cloud_manager): """ Test service account has roles in its policy """ faked_json = { "bindings": [ { "role": "roles/owner", "members": [ "user:[email protected]", "group:[email protected]", "domain:google.com", "serviceAccount:[email protected]", ], }, { "role": "roles/viewer", "members": ["user:[email protected]"] }, ] } (cloud_manager.get_service_account_policy.return_value) = MockResponse( faked_json, 200) assert service_account_has_external_access("test_service_account", cloud_manager)
def check_validity(self, early_return=True, check_type_and_access=True, config=None): google_managed_sa_domains = ( config["GOOGLE_MANAGED_SERVICE_ACCOUNT_DOMAINS"] if config else None) is_owned_by_google_project = is_service_account_from_google_project( self.account_id, self.google_project_id, self.google_project_number, google_managed_sa_domains=google_managed_sa_domains, ) self.set("owned_by_project", is_owned_by_google_project) if not is_owned_by_google_project: # we cannot determine further information if the account isn't # owned by the project return if check_type_and_access: valid_type = is_valid_service_account_type(self.google_project_id, self.account_id) self.set("valid_type", valid_type) if not valid_type and early_return: return no_external_access = not (service_account_has_external_access( self.account_id, self.google_project_id)) self.set("no_external_access", no_external_access) if not no_external_access and early_return: return
def test_service_account_has_external_access_no_authorization(cloud_manager): """ In the case that a exception is raised when there is no access to the service account policy """ (cloud_manager.get_service_account_policy.return_value) = MockResponse({}, 403) assert service_account_has_external_access("test_service_account", cloud_manager)
def test_service_account_has_external_access_raise_exception(cloud_manager): """ In the case that a exception is raised when there is no access to the service account policy """ (cloud_manager.get_service_account_policy.return_value ) = Exception("exception") with pytest.raises(Exception): assert service_account_has_external_access("test_service_account", cloud_manager)
def test_service_account_does_not_have_external_access(cloud_manager): """ Test that service account does not have any role or user managed key in its policy """ faked_json = {"etag": "ACAB"} (cloud_manager.get_service_account_policy.return_value) = MockResponse( faked_json, 200) (cloud_manager.get_service_account_keys_info.return_value) = [] assert not service_account_has_external_access("test_service_account", cloud_manager)
def test_service_account_has_user_managed_key_in_service_policy(cloud_manager): """ Test that service account has user managed keys in its policy """ faked_json = {"etag": "ACAB"} (cloud_manager.get_service_account_policy.return_value) = MockResponse( faked_json, 200 ) (cloud_manager.get_service_account_keys_info.return_value) = ["key1", "key2"] assert service_account_has_external_access("test_service_account", cloud_manager)
def check_validity( self, early_return=True, check_type=True, check_external_access=True, check_policy_accessible=True, ): logger.debug( "Validating Google Service Account {} for Google Project {}.".format( self.account_id, self.google_project_id ) ) self.google_cloud_manager.open() # check ownership logger.debug( "Determining if {} is owned by the Google Project.".format(self.account_id) ) is_owned_by_google_project = is_service_account_from_google_project( self.account_id, self.google_project_id, self.google_project_number ) self.set("owned_by_project", is_owned_by_google_project) if not is_owned_by_google_project: logger.warning( "INVALID SA {}, it is NOT owned by the Google Project {}.".format( self.account_id, self.google_project_id ) ) if early_return: self.google_cloud_manager.close() return # select the GCM to use for the remainder of the checks # if the account is not owned by the google project then # it is invalid, however, if Fence has access to the SA's # project, we can still check the other conditions if is_owned_by_google_project: gcm = self.google_cloud_manager else: self.google_cloud_manager.close() try: # check to see if we can access the project the SA belongs to project_id = self.account_id.split("@")[-1].split(".")[0] gcm = GoogleCloudManager(project_id) gcm.open() except Exception: logger.debug( "Could not access the Google Project for Service " "Account {}. Unable to continue validity " "checkingwithout access to the project, " "early exit.".format(self.account_id) ) return # check if the SA's policy is accessible policy_accessible = None sa_policy = None if check_policy_accessible: try: policy_accessible = True sa_policy = get_service_account_policy(self.account_id, gcm) except Exception: policy_accessible = False gcm.close() return finally: self.set("policy_accessible", policy_accessible) if check_external_access: if not policy_accessible: logger.warning( "Invalid function use. External Access check requires " "Service Account Policy & may fail if policy is not " "accessible. If you want to check external access, make " "sure you are also checking policy_accessible. " ) gcm.close() return no_external_access = not ( service_account_has_external_access(self.account_id, gcm, sa_policy) ) self.set("no_external_access", no_external_access) if not no_external_access: logger.warning( "INVALID SA {}, it has external access " "(keys generated or roles on it).".format(self.account_id) ) if early_return: gcm.close() return # check if the SA is an allowed type if check_type: if not policy_accessible: logger.warning( "Policy access was not checked. If the service account's " "policy is not accessible or the service account does not " "exist, this check may fail." ) # don't return early, we can still check type without checking # policy, however, if the SA doesn't exist, this will fail valid_type = is_valid_service_account_type(self.account_id, gcm) self.set("valid_type", valid_type) if not valid_type: logger.warning( "INVALID SA {}, it is not a valid SA type.".format(self.account_id) ) if early_return: gcm.close() return gcm.close()