def test_create_resource_is_ok(self): """Test the resource_util.create_resource() creates the types.""" expect_org = Organization(12345) actual_org = resource_util.create_resource( 12345, ResourceType.ORGANIZATION) self.assertEqual(expect_org, actual_org) expect_proj = Project('abcd', project_number=54321) actual_proj = resource_util.create_resource( 'abcd', ResourceType.PROJECT, project_number=54321) self.assertEqual(expect_proj, actual_proj) self.assertEqual(expect_proj.project_number, actual_proj.project_number)
def find_ancestors(starting_resource, full_name): """Find the ancestors for a given resource. Take advantage of the full name from the data model which has the entire hierarchy. Keeping this outside of the class, because the class is mocked out during testing. Args: starting_resource (Resource): The GCP resource associated with the policy binding. This is where we move up the resource hierarchy. full_name (str): Full name of the resource in hierarchical format. Example of a full_name: organization/88888/project/myproject/firewall/99999/ Returns: list: A list of GCP resources in ascending order in the resource hierarchy. """ ancestor_resources = [starting_resource] resources = utils.get_resources_from_full_name(full_name) for resource_type, resource_id in resources: if (resource_type == starting_resource.type and resource_id == starting_resource.id): continue new_resource = resource_util.create_resource(resource_id, resource_type) if new_resource: ancestor_resources.append(new_resource) return ancestor_resources
def find_violations(self, project, enabled_apis): """Find enabled APIs violations in the rule book. Args: project (gcp_type): The project that these APIs are enabled on. enabled_apis (list): list of enabled APIs. Returns: iterable: A generator of the rule violations. """ violations = itertools.chain() # Check for rules on all ancestors, and the wildcard rule. resource_ancestors = (relationship.find_ancestors( project, project.full_name)) resource_ancestors.append( resource_util.create_resource(resource_id='*', resource_type='project')) for curr_resource in resource_ancestors: resource_rules = self.resource_rules_map.get(curr_resource, []) for resource_rule in resource_rules: violations = itertools.chain( violations, resource_rule.find_violations(project, enabled_apis)) return violations
def find_violations(self, settings, iam_only): """Find groups settings violations in the rule book. Args: settings (GroupsSettings): The GCP resource to check for violations. iam_only (bool): IAM only. Returns: RuleViolation: resource groups settings rule violations. """ LOGGER.debug('Looking for groups settings violations: %s', settings.name) violations = [] resource_rules = self.get_resource_rules(settings) if resource_rules: violations.extend( resource_rules.find_violations(settings, iam_only)) wildcard_resource = resource_util.create_resource( resource_id='*', resource_type=settings.type) resource_rules = self.get_resource_rules(wildcard_resource) if resource_rules: violations.extend( resource_rules.find_violations(settings, iam_only)) LOGGER.debug('Returning violations: %r', violations) return violations
def test_flatten_violations(self): """Test flattening violations""" epas.get_user_emails = mock.MagicMock(return_value=TEST_EMAILS) violation1 = Rule.RuleViolation( resource_type=resource_mod.ResourceType.PROJECT, resource_id='12345', rule_name='Only my org', rule_index=0, rule_data=dict(ancestor_resources=[ resource_util.create_resource('45678', 'organization') ]), full_name='projects/12345', violation_type='EXTERNAL_PROJECT_ACCESS_VIOLATION', member='*****@*****.**', resource_data=['projects/12345', 'organizations/67890']) scanner = epas.ExternalProjectAccessScanner( self.global_configs, self.scanner_configs, self.service_config, self.model_name, self.snapshot_timestamp, self.rules) flattened_iter = scanner._flatten_violations([violation1]) flat_violation = flattened_iter.next() self.assertEqual(flat_violation['resource_id'], '12345') self.assertEqual(flat_violation['resource_type'], resource_mod.ResourceType.PROJECT) self.assertEqual(flat_violation['rule_name'], 'Only my org') self.assertEqual(flat_violation['full_name'], 'projects/12345') self.assertEqual(flat_violation['violation_data']['member'], '*****@*****.**') with self.assertRaises(StopIteration): flat_violation = flattened_iter.next()
def find_violations(self, parent_resource, liens): """Find lien violations in the rule book. Args: parent_resource (Resource): The GCP resource associated with the liens. This is where we start looking for rule violations and we move up the resource hierarchy (if permitted by the resource's "inherit_from_parents" property). liens (List[Lien]): The liens to look for violations. Yields: RuleViolation: lien rule violations. """ all_restrictions = set() for lien in liens: for restriction in lien.restrictions: all_restrictions.add(restriction) resource_ancestors = relationship.find_ancestors( parent_resource, parent_resource.full_name) applicable_rules = [] for res in resource_ancestors: applicable_rules.extend(self.resource_to_rules.get(res, [])) wildcard_res = resource_util.create_resource( resource_id='*', resource_type=res.type) applicable_rules.extend( self.resource_to_rules.get(wildcard_res, [])) for rule in applicable_rules: for violation in rule.find_violations(parent_resource, all_restrictions): yield violation
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ resources = rule_def.get('resource') for raw_resource in resources: resource_ids = raw_resource.get('resource_ids') if not resource_ids or len(resource_ids) < 1: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) rule = self._build_rule(rule_def, rule_index) resource_type = raw_resource.get('type') for resource_id in resource_ids: resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type, ) self.resource_rules_map[resource].append(rule)
def find_violations(self, res): """Find resource locations violations in the rule book. Args: res (Resource): The GCP resource to check locations for. This is where we start looking for rule violations and we move up the resource hierarchy. Yields: RuleViolation: resource locations rule violations. """ resource_ancestors = relationship.find_ancestors(res, res.full_name) rules = [] for ancestor_res in resource_ancestors: rules.extend(self.resource_to_rules.get(ancestor_res, [])) type_resource_wildcard = resource_util.create_resource( resource_id='*', resource_type=res.type) rules.extend(self.resource_to_rules.get(type_resource_wildcard, [])) for rule in rules: for violation in rule.find_violations(res): yield violation
def process_rule(self, rule_def, rule_index): """Process a rule. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. Returns: processed_rule: The dict containing the resources and users to which the rule applies """ processed_rule = {'ancestor_resources': []} allowed_ancestors = rule_def.get('allowed_ancestors', None) self.validate_ancestors(allowed_ancestors, rule_index) for allowed_ancestor in allowed_ancestors: processed_rule['ancestor_resources'].append( resource_util.create_resource( allowed_ancestor.split('/')[1], resource_util.type_from_name(allowed_ancestor))) users = rule_def.get('users', None) if users: self.validate_users(users, rule_index) processed_rule['users'] = users return processed_rule
def find_violations(self, project, audit_config): """Find Cloud Audit Logging violations in the rule book. Args: project (gcp_type): The project that has this configuation. audit_config (IamAuditConfig): The audit config for this project, merged with ancestor configs. Returns: iterable: A generator of the rule violations. """ violations = itertools.chain() # Check for rules on all ancestors, and the wildcard rule. resource_ancestors = (relationship.find_ancestors( project, project.full_name)) resource_ancestors.append( resource_util.create_resource(resource_id='*', resource_type='project')) for curr_resource in resource_ancestors: resource_rules = self.resource_rules_map.get(curr_resource, []) for resource_rule in resource_rules: violations = itertools.chain( violations, resource_rule.find_violations(project, audit_config)) return violations
def find_violations(self, resource, policy, policy_bindings): """Find policy binding violations in the rule book. Args: resource (gcp_type): The GCP resource associated with the policy binding. This is where we start looking for rule violations and we move up the resource hierarchy (if permitted by the resource's "inherit_from_parents" property). policy (forseti_data_model_resource): The policy to compare against the rules. See https://cloud.google.com/iam/reference/rest/v1/Policy. policy_bindings (list): A list of IamPolicyBindings. Returns: iterable: A generator of the rule violations. """ violations = itertools.chain() resource_ancestors = (relationship.find_ancestors( resource, policy.full_name)) for curr_resource in resource_ancestors: wildcard_resource = resource_util.create_resource( resource_id='*', resource_type=curr_resource.type) resource_rules = self._get_resource_rules(curr_resource) resource_rules.extend(self._get_resource_rules(wildcard_resource)) # Set to None, because if the direct resource (e.g. project) # doesn't have a specific rule, we still should check the # ancestry to see if the resource's parents have any rules # that apply to the children. inherit_from_parents = None for resource_rule in resource_rules: if not self._rule_applies_to_resource(resource, curr_resource, resource_rule): continue violations = itertools.chain( violations, resource_rule.find_mismatches(resource, policy_bindings)) inherit_from_parents = resource_rule.inherit_from_parents # If the rule does not inherit the parents' rules, stop. # Due to the way rules are structured, we only define the # "inherit" property once per rule. So even though a rule # may apply to multiple resources, it will only have one # value for "inherit_from_parents". if not inherit_from_parents and inherit_from_parents is not None: break return violations
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ with self._lock: for resource in rule_def.get('resource'): resource_ids = resource.get('resource_ids') resource_type = None try: resource_type = resource_mod.ResourceType.verify( resource.get('type')) except resource_errors.InvalidResourceTypeError: raise audit_errors.InvalidRulesSchemaError( 'Missing resource type in rule {}'.format(rule_index)) if not resource_ids or len(resource_ids) < 1: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) rule_mode = rule_def.get('mode') if rule_mode not in ('blacklist', 'whitelist'): raise audit_errors.InvalidRulesSchemaError( 'Unknown mode in rule {}'.format(rule_index)) rule_key = rule_def.get('key') if rule_key is None: raise audit_errors.InvalidRulesSchemaError( 'Missing key in rule {}'.format(rule_index)) rule_values = rule_def.get('values', []) # For each resource id associated with the rule, create a # mapping of resource => rules. for resource_id in resource_ids: gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) rule = Rule( rule_def.get('name'), rule_index, rule_mode, rule_key, rule_values, ) resource_rules = self.resource_rules_map.setdefault( gcp_resource, ResourceRules(resource=gcp_resource)) if rule not in resource_rules.rules: resource_rules.rules.add(rule)
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ with self._lock: for resource in rule_def.get('resource'): resource_ids = resource.get('resource_ids') resource_type = None try: resource_type = resource_mod.ResourceType.verify( resource.get('type')) except resource_errors.InvalidResourceTypeError: raise audit_errors.InvalidRulesSchemaError( 'Missing resource type in rule {}'.format(rule_index)) if not resource_ids or len(resource_ids) < 1: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) check_serverconfig_valid_node_versions = rule_def.get( 'check_serverconfig_valid_node_versions', False) check_serverconfig_valid_master_versions = rule_def.get( 'check_serverconfig_valid_master_versions', False) allowed_nodepool_versions = rule_def.get( 'allowed_nodepool_versions', []) allowed_versions = [] for allowed_version in allowed_nodepool_versions: allowed_versions.append(VersionRule(**allowed_version)) # For each resource id associated with the rule, create a # mapping of resource => rules. for resource_id in resource_ids: gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) rule = Rule( rule_def.get('name'), rule_index, check_serverconfig_valid_node_versions, check_serverconfig_valid_master_versions, allowed_versions) resource_rules = self.resource_rules_map.setdefault( gcp_resource, ResourceRules(resource=gcp_resource)) if rule not in resource_rules.rules: resource_rules.rules.add(rule)
def add_org_policy(self, org_def): """Creates org policy and rule mapping. Sample org structure: org 1234 / \ f-1 p-c / \ p-a p-b Rules can be applied at any node above. When a policy is being audited, it the rulebook will start at the lowest level (the project) and will walk up the hierarchy until it reaches the first instance with rules and these are the only rules that are checked. Args: org_def (dict): A dictionary of resource ids and enforced rules. Raises: RuleDoesntExistError: Raised if a rule included in the group does not exist. GroupDoesntExistError: Raised if a group included in an org policy does not exist. InvalidOrgDefinition: Raised if org policy doesn't have resources. """ resources = org_def.get('resources', []) if not resources: raise InvalidOrgDefinition( 'Org policy does not have any resources') for resource in resources: resource_type = resource_mod.ResourceType.verify( resource.get('type')) ids = resource.get('resource_ids', []) rules = resource.get('rules', {}) groups = rules.get('group_ids', []) expanded_rules = set() for group_id in groups: if group_id not in self.rule_groups_map: raise GroupDoesntExistError('Group "%s" does not exist' % group_id) expanded_group = self.rule_groups_map.get(group_id, []) expanded_rules.update(expanded_group) for rule_id in rules.get('rule_ids', []): if rule_id not in self.rules_map: raise RuleDoesntExistError('Rule id "%s" does not exist' % rule_id) expanded_rules.add(rule_id) for resource_id in ids: gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) self.org_policy_rules_map[gcp_resource] = sorted( expanded_rules)
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ resources = rule_def.get('resource') mode = rule_def.get('mode') key = rule_def.get('key') if not resources or key is None or mode not in RULE_MODES: raise audit_errors.InvalidRulesSchemaError( 'Faulty rule {}'.format(rule_index)) for resource in resources: resource_type = resource.get('type') resource_ids = resource.get('resource_ids') if resource_type not in self.supported_resource_types: raise audit_errors.InvalidRulesSchemaError( 'Invalid resource type in rule {}'.format(rule_index)) if not resource_ids or len(resource_ids) < 1: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) # For each resource id associated with the rule, create a # mapping of resource => rules. for resource_id in resource_ids: gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) rule_def_resource = { 'key': key, 'mode': mode } rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rule=rule_def_resource) resource_rules = self.resource_rules_map.setdefault( gcp_resource, ResourceRules(resource=gcp_resource)) if not resource_rules: self.resource_rules_map[rule_index] = rule if rule not in resource_rules.rules: resource_rules.rules.add(rule)
def test_create_from_resource_utils(self): """Tests creating a Billing Account using resource_util.""" billing_acct = resource_util.create_resource( resource_id='000000-111111-222222', resource_type='billing_account', full_name='organization/234/billing_account/000000-111111-222222/', parent=self.org_234) self.assertEqual('000000-111111-222222', billing_acct.id) self.assertEqual('billing_account', billing_acct.type) self.assertEqual('billingAccounts/000000-111111-222222', billing_acct.name) self.assertEqual( 'organization/234/billing_account/000000-111111-222222/', billing_acct.full_name)
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ mode = rule_def.get('mode') settings = rule_def.get('settings') groups_emails = rule_def.get('groups_emails') only_iam_groups = rule_def.get('only_iam_groups') if (settings is None or only_iam_groups is None or not groups_emails or mode not in RULE_MODES): raise audit_errors.InvalidRulesSchemaError( 'Faulty rule {}'.format(rule_index)) for rule_setting in settings: if rule_setting not in self.supported_settings: raise audit_errors.InvalidRulesSchemaError( 'Faulty rule {}'.format(rule_index)) for group_email in groups_emails: # For each resource id associated with the rule, create a # mapping of resource => rules. gcp_resource = resource_util.create_resource( resource_id=group_email, resource_type=resource.ResourceType.GROUPS_SETTINGS) rule_def_resource = { 'settings': settings, 'mode': mode, 'only_iam_groups': only_iam_groups } rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rule=rule_def_resource) resource_rules = self.resource_rules_map.setdefault( gcp_resource, ResourceRules(_resource=gcp_resource)) if only_iam_groups: if rule not in resource_rules.iam_only_rules: resource_rules.iam_only_rules.add(rule) elif rule not in resource_rules.not_iam_only_rules: resource_rules.not_iam_only_rules.add(rule)
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ with self._lock: for resource in rule_def.get('resource'): resource_ids = resource.get('resource_ids') try: resource_type = resource_mod.ResourceType.verify( resource.get('type')) except resource_errors.InvalidResourceTypeError: raise audit_errors.InvalidRulesSchemaError( 'Missing resource type in rule {}'.format(rule_index)) if not resource_ids or len(resource_ids) < 1: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) key_max_age_str = rule_def.get('max_age', None) try: key_max_age = int(key_max_age_str) except (ValueError, TypeError): raise audit_errors.InvalidRulesSchemaError( 'Service account key "max_age" missing or not an ' 'integer in rule {}'.format(rule_index)) # For each resource id associated with the rule, create a # mapping of resource => rules. for resource_id in resource_ids: gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) rule = Rule( rule_def.get('name'), rule_index, key_max_age) resource_rules = self.resource_rules_map.setdefault( gcp_resource, ResourceRules(resource=gcp_resource)) if rule not in resource_rules.rules: resource_rules.rules.add(rule)
def create_and_add_rule(self, rule_def, rule_index, apply_to, min_retention, max_retention): """Add a rule to the rule book. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. apply_to (str): The resource type that the rule is applied to min_retention(int): minimum value of the age in lifecycle max_retention(int): maximum value of the age in lifecycle """ if 'resource' not in rule_def: raise audit_errors.InvalidRulesSchemaError( 'Lack of resource in rule {}'.format(rule_index)) resources = rule_def['resource'] for res in resources: if 'type' not in res: raise audit_errors.InvalidRulesSchemaError( 'Lack of type in rule {}'.format(rule_index)) resource_type = res['type'] if 'resource_ids' not in res: raise audit_errors.InvalidRulesSchemaError( 'Lack of resource_ids in rule {}'.format(rule_index)) resource_ids = res['resource_ids'] rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, min_retention=min_retention, max_retention=max_retention) for rid in resource_ids: if rid == '*': raise audit_errors.InvalidRulesSchemaError( 'The symbol * is not allowed in rule {}'.format( rule_index)) gcp_resource = resource_util.create_resource( resource_id=rid, resource_type=resource_type) self.resource_rules_map[apply_to][gcp_resource].add(rule)
def _find_violations(self, policies): """Find violations in the policies. Args: policies (list): The list of policies to find violations in. Returns: list: A list of all violations """ all_violations = [] LOGGER.info('Finding firewall policy violations...') for resource_id, p_policies in policies.items(): resource = resource_util.create_resource(resource_id=resource_id, resource_type='project') LOGGER.debug('%s => %s', resource, p_policies) violations = self.rules_engine.find_violations( resource, p_policies) all_violations.extend(violations) return all_violations
def find_violations(self, ke_cluster): """Find violations in the rule book. Args: ke_cluster (KeCluster): KE Cluster and ServerConfig data. Returns: list: RuleViolation """ LOGGER.debug('Looking for KE violations: %r', ke_cluster) violations = [] resource_ancestors = resource_util.get_ancestors_from_full_name( ke_cluster.full_name) LOGGER.debug('Ancestors of resource: %r', resource_ancestors) checked_wildcards = set() for curr_resource in resource_ancestors: if not curr_resource: # resource_ancestors will contain all the resources including # the child resource, which has type kebernete cluster and # cannot be created (return None) as part of the ancestor path, # we will skip the child as it's not part of the ancestor. continue resource_rule = self.get_resource_rules(curr_resource) if resource_rule: violations.extend( resource_rule.find_violations(ke_cluster)) wildcard_resource = resource_util.create_resource( resource_id='*', resource_type=curr_resource.type) if wildcard_resource in checked_wildcards: continue checked_wildcards.add(wildcard_resource) resource_rule = self.get_resource_rules(wildcard_resource) if resource_rule: violations.extend( resource_rule.find_violations(ke_cluster)) LOGGER.debug('Returning violations: %r', violations) return violations
def _retrieve(self): """Retrieves the data for scanner. Returns: list: a list of custom Roles, no curated roles. """ model_manager = self.service_config.model_manager scoped_session, data_access = model_manager.get(self.model_name) role_res = [] with scoped_session as session: for resource in data_access.scanner_iter(session, 'role'): parent = resource_util.create_resource( resource_id=resource.parent.name, resource_type=resource.parent.type) parent.full_name = resource.parent.full_name new_res = resource_util.create_resource_from_json( 'role', parent, resource.data) role_res.append(new_res) return role_res
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. Args: rule_def (dict): A dictionary containing rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ resources = rule_def.get('resource') if not resources: raise errors.InvalidRulesSchemaError( 'Missing field "resource" in rule {}'.format(rule_index)) for raw_resource in resources: resource_ids = raw_resource.get('resource_ids') if not resource_ids: raise errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) resource_type = raw_resource.get('type') if resource_type not in ['project', 'folder', 'organization']: raise errors.InvalidRulesSchemaError( 'Invalid resource type "{}" in rule {}'.format( resource_type, rule_index)) for resource_id in resource_ids: resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type, ) if not resource: raise errors.InvalidRulesSchemaError( 'Invalid resource in rule {} (id: {}, type: {})'. format(rule_index, resource_id, resource_type)) rule = self._build_rule(rule_def, rule_index) self.resource_to_rules[resource].append(rule)
def find_violations(self, service_account): """Find violations in the rule book. Args: service_account (ServiceAccount): service account resource. Returns: list: RuleViolation """ LOGGER.debug('Looking for service account key violations: %s', service_account.full_name) violations = [] resource_ancestors = resource_util.get_ancestors_from_full_name( service_account.full_name) LOGGER.debug('Ancestors of resource: %r', resource_ancestors) checked_wildcards = set() for curr_resource in resource_ancestors: if not curr_resource: # The leaf node in the hierarchy continue resource_rule = self.get_resource_rules(curr_resource) if resource_rule: violations.extend( resource_rule.find_violations(service_account)) wildcard_resource = resource_util.create_resource( resource_id='*', resource_type=curr_resource.type) if wildcard_resource in checked_wildcards: continue checked_wildcards.add(wildcard_resource) resource_rule = self.get_resource_rules(wildcard_resource) if resource_rule: violations.extend( resource_rule.find_violations(service_account)) LOGGER.debug('Returning violations: %r', violations) return violations
def find_violations(self, key): """Find crypto key violations in the rule book. Args: key (CryptoKey): The GCP resource to check for violations. Returns: RuleViolation: resource crypto key rule violations. """ LOGGER.debug('Looking for crypto key violations: %s', key.name) violations = [] resource_ancestors = resource_util.get_ancestors_from_full_name( key.crypto_key_full_name) LOGGER.debug('Ancestors of resource: %r', resource_ancestors) checked_wildcards = set() for curr_resource in resource_ancestors: if not curr_resource: # The leaf node in the hierarchy continue resource_rule = self.get_resource_rules(curr_resource) if resource_rule: violations.extend( resource_rule.find_violations(key)) wildcard_resource = resource_util.create_resource( resource_id='*', resource_type=curr_resource.type) if wildcard_resource in checked_wildcards: continue checked_wildcards.add(wildcard_resource) resource_rule = self.get_resource_rules(wildcard_resource) if resource_rule: violations.extend( resource_rule.find_violations(key)) LOGGER.debug('Returning violations: %r', violations) return violations
def _retrieve(self): """Retrieves the data for scanner. Returns: list: a list of Resources, with a type in SUPPORTED_RETENTION_RES_TYPES """ model_manager = self.service_config.model_manager scoped_session, data_access = model_manager.get(self.model_name) retention_res = [] with scoped_session as session: for resource_type in rre.SUPPORTED_RETENTION_RES_TYPES: for resource in data_access.scanner_iter( session, resource_type): parent = resource_util.create_resource( resource_id=resource.parent.name, resource_type=resource.parent.type) parent.full_name = resource.parent.full_name new_res = resource_util.create_resource_from_json( resource_type, parent, resource.data) retention_res.append(new_res) return retention_res
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. The rule supplied to this method is the dictionary parsed from the rules definition file. For example, this rule... # rules yaml: rules: - name: a rule resource: - type: project resource_ids: - my-project-123 service: allServices log_types: - 'ADMIN_READ' - 'DATA_WRITE' allowed_exemptions: - 'user:[email protected]' - 'user:[email protected]' ... gets parsed into: { 'name': 'a rule', 'resource': { 'type': 'project', 'resource_ids': ['my-project-id'] }, 'service': 'allServices', 'log_types': [ 'ADMIN_READ', 'DATA_WRITE', ], 'allowed_exemptions': [ 'user:[email protected]', 'user:[email protected]', ] } Args: rule_def (dict): Contains rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ self._rules_sema.acquire() try: resources = rule_def.get('resource') service = rule_def.get('service') log_types = rule_def.get('log_types') # allowed_exemptions is optional. allowed_exemptions = set(rule_def.get('allowed_exemptions', [])) if not resources or not service or not log_types: raise audit_errors.InvalidRulesSchemaError( 'Faulty rule {}'.format(rule_index)) for resource in resources: resource_ids = resource.get('resource_ids') resource_type = resource.get('type') if resource_type not in self.supported_resource_types: raise audit_errors.InvalidRulesSchemaError( 'Invalid resource type in rule {}'.format(rule_index)) if not resource_ids: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) # For each resource id associated with the rule, create a # mapping of resource => rules. for resource_id in resource_ids: if resource_id == '*' and resource_type != 'project': raise audit_errors.InvalidRulesSchemaError( 'Wild-card must use project type in rule {}'. format(rule_index)) gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) rule_def_resource = { 'service': service, 'log_types': log_types, 'allowed_exemptions': allowed_exemptions, } rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rule=rule_def_resource) # If no mapping exists, create it. If the rule isn't in the # mapping, add it. self.resource_rules_map[gcp_resource].add(rule) finally: self._rules_sema.release()
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. The rule supplied to this method is the dictionary parsed from the rules definition file. For example, this rule... # rules yaml: rules: - name: a rule mode: whitelist resource: - type: project applies_to: self resource_ids: - my-project-123 inherit_from_parents: true bindings: - role: roles/editor members: - users:[email protected] ... gets parsed into: { 'name': 'a rule', 'mode': 'whitelist', 'resource': { 'type': 'project', 'applies_to': self, 'resource_ids': ['my-project-id'] }, 'inherit_from_parents': true, 'bindings': [ { 'role': 'roles/editor', 'members': ['users:[email protected]'] } ] } Args: rule_def (dict): Contains rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ self._rules_sema.acquire() try: resources = rule_def.get('resource') for resource in resources: resource_ids = resource.get('resource_ids') resource_type = None # TODO: collect these errors and output them in a log. # TODO: log the error and keep going try: resource_type = resource_mod.ResourceType.verify( resource.get('type')) except resource_errors.InvalidResourceTypeError: raise audit_errors.InvalidRulesSchemaError( 'Missing resource type in rule {}'.format(rule_index)) if not resource_ids or len(resource_ids) < 1: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) # For each resource id associated with the rule, create a # mapping of resource => rules. for resource_id in resource_ids: gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) # TODO: Rewrite this as a list comprehension. # pylint: disable=bad-builtin rule_bindings = filter(None, [ iam_policy.IamPolicyBinding.create_from(b) for b in rule_def.get('bindings') ]) rule = scanner_rules.Rule(rule_name=rule_def.get('name'), rule_index=rule_index, bindings=rule_bindings, mode=rule_def.get('mode')) rule_applies_to = resource.get('applies_to') rule_key = (gcp_resource, rule_applies_to) # See if we have a mapping of the resource and rule resource_rules = self.resource_rules_map.get(rule_key) # If no mapping exists, create it. if not resource_rules: resource_rules = ResourceRules( resource=gcp_resource, applies_to=rule_applies_to, inherit_from_parents=rule_def.get( 'inherit_from_parents', False)) self.resource_rules_map[rule_key] = resource_rules # If the rule isn't in the mapping, add it. if rule not in resource_rules.rules: resource_rules.rules.add(rule) finally: self._rules_sema.release()
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. The rule supplied to this method is the dictionary parsed from the rules definition file. For example, this rule... # rules yaml: rules: - name: a rule mode: whitelist resource: - type: project resource_ids: - my-project-123 services: - 'compute.googleapis.com' - 'storage-component.googleapis.com' - 'storage-api.googleapis.com' ... gets parsed into: { 'name': 'a rule', 'mode': 'whitelist', 'resource': { 'type': 'project', 'resource_ids': ['my-project-id'] }, 'services': [ 'compute.googleapis.com', 'storage-component.googleapis.com', 'storage-api.googleapis.com' ] } Args: rule_def (dict): Contains rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ self._rules_sema.acquire() try: resources = rule_def.get('resource') mode = rule_def.get('mode') services = rule_def.get('services') if not resources or not services or mode not in _RULE_MODES: raise audit_errors.InvalidRulesSchemaError( 'Faulty rule {}'.format(rule_index)) for resource in resources: resource_ids = resource.get('resource_ids') resource_type = resource.get('type') if resource_type not in self.supported_resource_types: raise audit_errors.InvalidRulesSchemaError( 'Invalid resource type in rule {}'.format(rule_index)) if not resource_ids or len(resource_ids) < 1: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) # For each resource id associated with the rule, create a # mapping of resource => rules. for resource_id in resource_ids: if resource_id == '*' and resource_type != 'project': raise audit_errors.InvalidRulesSchemaError( 'Wild-card must use project type in rule {}'. format(rule_index)) gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) rule_def_resource = { 'services': services, 'mode': mode, } rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rule=rule_def_resource) # If no mapping exists, create it. If the rule isn't in the # mapping, add it. self.resource_rules_map[gcp_resource].add(rule) finally: self._rules_sema.release()
def test_create_nonexist_resource_returns_None(self): """Test that nonexistent resource type creates None.""" self.assertIsNone( resource_util.create_resource('fake-id', 'nonexist'))