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. raises: InvalidRulesSchemaError: if invalid rules definition. """ mode = rule_def.get('mode', '') if mode not in _SUPPORTED_MODES: raise errors.InvalidRulesSchemaError( 'Unexpected "mode" in rule {}: got {}, want one of {}'.format( rule_index, mode, _SUPPORTED_MODES)) if not rule_def.get('resource_types'): raise errors.InvalidRulesSchemaError( 'Missing non empty "resource_types" in rule {}'.format( rule_index)) resource_tree = ResourceTree.from_json( rule_def.get('resource_trees', [])) self.rules.append( Rule(name=rule_def['name'], index=rule_index, resource_types=set(rule_def['resource_types']), resource_tree=resource_tree))
def __init__(self, rule_index, rule_name, permissions, res): """Initialize. Args: rule_index (int): The index of the rule. rule_name (str): Name of the rule. permissions (int): Expected permissions of the role. res (dict): Parent resource of the role that should obey the rule. """ self.rule_name = rule_name self.rule_index = rule_index self.permissions = permissions[:] self.res_types = res[:] for index, res_item in enumerate(self.res_types): if 'type' not in res_item: raise audit_errors.InvalidRulesSchemaError( 'Lack of resource:type in rule {}'.format(rule_index)) if res_item['type'] not in [ 'organization', 'folder', 'project', 'role' ]: raise audit_errors.InvalidRulesSchemaError( 'Wrong resource:type {} in rule {}'.format( res_item['type'], rule_index)) if 'resource_ids' not in res_item: raise audit_errors.InvalidRulesSchemaError( 'Lack of resource:resource_ids in rule {}'.format( rule_index)) if '*' in res_item['resource_ids']: self.res_types[index]['resource_ids'] = ['*']
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. """ if 'name' not in rule_def: raise audit_errors.InvalidRulesSchemaError( 'Lack of role_name in rule {}'.format(rule_index)) if 'role_name' not in rule_def: raise audit_errors.InvalidRulesSchemaError( 'Lack of role_name in rule {}'.format(rule_index)) role_name = rule_def['role_name'] if 'permissions' not in rule_def: raise audit_errors.InvalidRulesSchemaError( 'Lack of permissions in rule {}'.format(rule_index)) if 'resource' not in rule_def: raise audit_errors.InvalidRulesSchemaError( 'Lack of resource in rule {}'.format(rule_index)) res = rule_def['resource'] rule = Rule(rule_index=rule_index, rule_name=rule_def['name'], permissions=rule_def['permissions'], res=res) if role_name not in self.rules_map: self.rules_map[role_name] = [rule] else: self.rules_map[role_name].append(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)) 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_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 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 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. """ resources = rule_def.get('resource') for resource in resources: resource_ids = 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)) bucket = rule_def.get('bucket') entity = rule_def.get('entity') email = rule_def.get('email') domain = rule_def.get('domain') role = rule_def.get('role') if ((bucket is None) or (entity is None) or (email is None) or (domain is None) or (role is None)): raise audit_errors.InvalidRulesSchemaError( 'Faulty rule {}'.format(rule_def.get('name'))) rule_def_resource = BucketAccessControls( project_id='', bucket=escape_and_globify(bucket), full_name='', entity=escape_and_globify(entity), email=escape_and_globify(email), domain=escape_and_globify(domain), role=escape_and_globify(role.upper()), raw_json=json.dumps(rule_def, sort_keys=True) ) rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rules=rule_def_resource) resource_rules = self.resource_rules_map.get(rule_index) if not resource_rules: self.resource_rules_map[rule_index] = 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. """ 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 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 resource in resources: resource_ids = 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)) instance_name = rule_def.get('instance_name') authorized_networks = rule_def.get('authorized_networks') require_ssl = rule_def.get('ssl_enabled', '').lower() == 'true' if instance_name is None or authorized_networks is None: raise audit_errors.InvalidRulesSchemaError( 'Faulty rule {}'.format(rule_def.get('name'))) rule_def_resource = CloudSqlAccessControl( project_id='', instance_name=escape_and_globify(instance_name), full_name='', ipv4_enabled=True, authorized_networks=escape_and_globify(authorized_networks), require_ssl=require_ssl, raw_json=json.dumps(rule_def, sort_keys=True) ) rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rules=rule_def_resource) resource_rules = self.resource_rules_map.get(rule_index) if not resource_rules: self.resource_rules_map[rule_index] = 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 validate_ancestor(self, ancestor, rule_index): """Validate the ancestor in a rule. Args: ancestor (str): The ancestor defined by the rule. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ if not ancestor: raise audit_errors.InvalidRulesSchemaError( 'Missing ancestor in rule {}'.format(rule_index)) ancestor_result = self.ancestor_pattern.match(ancestor) if not ancestor_result: message = ('Ancestor in rule {} must start with ' '\"organizations/\" or \"folders/\"').format(rule_index) raise audit_errors.InvalidRulesSchemaError(message)
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 _build_rule(cls, rule_def, rule_index): """Build 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: Rule: rule for the given definition. """ for field in ['name', 'mode', 'applies_to', 'locations']: if field not in rule_def: raise errors.InvalidRulesSchemaError( 'Missing field "{}" in rule {}'.format(field, rule_index)) applies_to = {} for applies_dict in rule_def.get('applies_to'): # For backwards compatibility for when applies_to was a string. if isinstance(applies_dict, str): applies_dict = {'type': applies_dict, 'resource_ids': ['*']} resource_type = applies_dict['type'] if resource_type != '*' and ( resource_type not in SUPPORTED_LOCATION_RESOURCE_TYPES): raise errors.InvalidRulesSchemaError( 'Unsupported applies to type "{}" in rule {}'.format( resource_type, rule_index)) applies_to[resource_type] = applies_dict['resource_ids'] return Rule(name=rule_def.get('name'), index=rule_index, mode=Mode(rule_def.get('mode')), applies_to=applies_to, location_patterns=rule_def.get('locations'))
def get_retention_range(rule_def, rule_index): """Get the min and max value of the retention. 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: pair: the minimum and maximum value of the Age. """ minimum_retention = rule_def.get('minimum_retention') maximum_retention = rule_def.get('maximum_retention') if minimum_retention is None and maximum_retention is None: raise audit_errors.InvalidRulesSchemaError( 'Lack of minimum_retention and ' 'maximum_retention in rule {}'.format(rule_index)) elif minimum_retention != None and maximum_retention != None: if minimum_retention > maximum_retention: raise audit_errors.InvalidRulesSchemaError( 'minimum_retention larger than ' 'maximum_retention in rule {}'.format(rule_index)) return minimum_retention, maximum_retention
def validate_user(self, user, rule_index): """Validate a user in a rule. Must be an e-mail address Args: user (str): A user defined by the rule rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ email_result = self.email_pattern.match(user) if not email_result: message = ('User {} in rule {} must be a properly' 'formatted e-mail address').format(user, rule_index) raise audit_errors.InvalidRulesSchemaError(message)
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. """ self._rules_sema.acquire() try: applies_to = rule_def.get('applies_to') if applies_to is None: raise audit_errors.InvalidRulesSchemaError( 'Lack of applies_to in rule {}'.format(rule_index)) minimum_retention, maximum_retention = get_retention_range( rule_def, rule_index) if any(support_type not in SUPPORTED_RETENTION_RES_TYPES for support_type in applies_to): raise audit_errors.InvalidRulesSchemaError( 'Invalid applies_to resource in rule {}'.format( rule_index)) added_applies_to = set() for appto in applies_to: if appto in added_applies_to: raise audit_errors.InvalidRulesSchemaError( 'redundant applies_to in rule {}'.format(rule_index)) added_applies_to.add(appto) self.create_and_add_rule(rule_def, rule_index, appto, minimum_retention, maximum_retention) finally: self._rules_sema.release()
def verify(cls, applies_to): """Verify whether the applies_to is valid. Args: applies_to (str): What the rule applies to. Returns: str: The applies_to property. Raises: InvalidRulesSchemaError if applies_to is not valid. """ if applies_to not in cls.apply_types: raise audit_errors.InvalidRulesSchemaError( 'Invalid applies_to: {}'.format(applies_to)) return applies_to
def verify(cls, mode): """Verify whether the mode is valid. Args: mode (str): The rules mode. Returns: str: The rules mode property. Raises: InvalidRulesSchemaError if mode is not valid. """ if mode not in cls.modes: raise audit_errors.InvalidRulesSchemaError( 'Invalid rule mode: {}'.format(mode)) return mode
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. Raises: InvalidRulesSchemaError: if rule has format error """ target = rule_def.get('target') mode = rule_def.get('mode') load_balancing_scheme = rule_def.get('load_balancing_scheme') port_range = rule_def.get('port_range') port = rule_def.get('port') ip_address = rule_def.get('ip_address') ip_protocol = rule_def.get('ip_protocol') if ((target is None) or (mode is None) or (load_balancing_scheme is None) or (ip_address is None) or (ip_protocol is None)): raise audit_errors.InvalidRulesSchemaError('Faulty rule {}'.format( rule_def.get('name'))) rule_def_resource = { 'target': target, 'mode': mode, 'load_balancing_scheme': load_balancing_scheme, 'port_range': port_range, 'ip_address': ip_address, 'ip_protocol': ip_protocol, 'port': port, 'full_name': '' } rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rules=rule_def_resource) resource_rules = self.resource_rules_map.get(rule_index) if not resource_rules: self.resource_rules_map[rule_index] = rule
def _build_rule(cls, rule_def, rule_index): """Build 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: Rule: rule for the given definition. """ for field in ['name', 'restrictions']: if field not in rule_def: raise errors.InvalidRulesSchemaError( 'Missing field "{}" in rule {}'.format(field, rule_index)) return Rule(name=rule_def.get('name'), index=rule_index, restrictions=rule_def.get('restrictions'))
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): # pylint: disable=too-many-locals """Add a rule to the rule book. Args: rule_def (dict): rule definition properties rule_index (int): index of the rule from the rule definitions, assigned automatically when the rule book is built """ self._rules_sema.acquire() try: 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)) allowed_alternate_services = [ regular_exp.escape_and_globify(glob) for glob in rule_def.get('allowed_alternate_services', '').split(',') if glob ] allowed_direct_access_sources = [ regular_exp.escape_and_globify(glob) for glob in rule_def.get('allowed_direct_access_sources', '').split(',') if glob ] allowed_iap_enabled = regular_exp.escape_and_globify( rule_def.get('allowed_iap_enabled', '*')) # 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_name=rule_def.get('name'), rule_index=rule_index, allowed_alternate_services=allowed_alternate_services, allowed_direct_access_sources=( allowed_direct_access_sources), allowed_iap_enabled=allowed_iap_enabled) 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 _build_rule(cls, rule_def, rule_index): """Build 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: Rule: rule for the given definition. """ dataset_ids = [] for dataset_id in rule_def.get('dataset_ids', []): dataset_ids.append(regular_exp.escape_and_globify(dataset_id)) # Check `dataset_id` for backwards compatibility. # TODO: stop supporting this. if 'dataset_id' in rule_def: dataset_ids.append( regular_exp.escape_and_globify(rule_def['dataset_id'])) if not dataset_ids: raise audit_errors.InvalidRulesSchemaError( 'Missing dataset_ids in rule {}'.format(rule_index)) bindings = [] # TODO: stop supporting this. binding = cls._get_binding_from_old_syntax(rule_def) if binding: bindings.append(binding) # Default mode to blacklist for backwards compatibility as that was # the behaviour before mode was configurable. # TODO: make mode required? mode = Mode(rule_def.get('mode', 'blacklist')) for raw_binding in rule_def.get('bindings', []): if 'role' not in raw_binding: raise audit_errors.InvalidRulesSchemaError( 'Missing role in binding in rule {}'.format(rule_index)) role = regular_exp.escape_and_globify(raw_binding['role']) if 'members' not in raw_binding: raise audit_errors.InvalidRulesSchemaError( 'Missing members in binding in rule {}'.format(rule_index)) members = [] for raw_member in raw_binding['members']: fields = { field: regular_exp.escape_and_globify(raw_member.get(field)) for field in ['domain', 'group_email', 'user_email', 'special_group'] } # only one key should be set per member num_fields_set = sum( [val is not None for val in list(fields.values())]) if num_fields_set != 1: raise audit_errors.InvalidRulesSchemaError( 'At most one member field may be set in rule {}'. format(rule_index)) members.append(Member(**fields)) bindings.append(Binding(role, members)) if not bindings: raise audit_errors.InvalidRulesSchemaError( 'Missing bindings in rule {}'.format(rule_index)) return Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rule_reference=RuleReference(dataset_ids=dataset_ids, bindings=bindings, mode=mode))
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: required resource: - type: organization applies_to: children resource_ids: - 11223344 sink: - destination: 'bigquery.googleapis.com/projects/my-proj/*' filter: 'logName:"logs/cloudaudit.googleapis.com"' include_children: '*' ... gets parsed into: { 'name': 'a rule', 'mode': 'required', 'resource': [{ 'type': 'organization', 'applies_to': 'children', 'resource_ids': ['11223344'] }], 'sink': { 'destination': 'bigquery.googleapis.com/projects/my-proj/*', 'filter': logName:"logs/cloudaudit.googleapis.com"', 'include_children': '*' } } 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') sink = _parse_sink_rule_spec(rule_def.get('sink')) if not resources or sink 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') applies_to = resource.get('applies_to') 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 applies_to not in self.supported_rule_applies_to: raise audit_errors.InvalidRulesSchemaError( 'Invalid applies_to type in rule {}'.format( rule_index)) if applies_to == 'children' and resource_type in [ 'project', 'billing_account' ]: raise audit_errors.InvalidRulesSchemaError( 'Rule {} cannot apply to children of a {}'.format( rule_index, resource_type)) 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 applies_to => 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 = { 'sink': sink, '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[applies_to][gcp_resource].add(rule) finally: self._rules_sema.release()
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. 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: all networks covered in whitelist project: '*' network: '*' is_external_network: True whitelist: master: - master-1 network: - network-1 - network-2 default: - default-1 ... gets parsed into: { "rules": [ { "name": "all networks covered in whitelist", "project": "*", "network": "*", "is_external_network": true, "whitelist": { "master": [ "master-1" ], "network": [ "network-1", "network-2" ], "default": [ "default-1" ] } } ] } 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. """ project = rule_def.get('project') network = rule_def.get('network') whitelist = rule_def.get('whitelist') is_external_network = rule_def.get('is_external_network') if ((whitelist is None) or (project is None) or (network is None) or (is_external_network is None)): raise audit_errors.InvalidRulesSchemaError( 'Faulty rule {}'.format(rule_def.get('name'))) rule_def_resource = {'whitelist': whitelist, 'project': escape_and_globify(project), 'network': escape_and_globify(network), 'is_external_network': is_external_network} rule = Rule(rule_name=rule_def.get('name'), rule_index=rule_index, rules=rule_def_resource) resource_rules = self.resource_rules_map.get(rule_index) if not resource_rules: self.resource_rules_map[rule_index] = rule
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()