Source code for

# Copyright 2017 The Forseti Security Authors. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.

"""Rules engine for NetworkInterface."""
from builtins import object
from collections import namedtuple
import itertools
import re

from import resource as resource_mod
from import escape_and_globify
from import logger
from import base_rules_engine as bre
from import errors as audit_errors

LOGGER = logger.get_logger(__name__)

[docs]class InstanceNetworkInterfaceRulesEngine(bre.BaseRulesEngine): """Rules engine for InstanceNetworkInterfaceRules.""" def __init__(self, rules_file_path, snapshot_timestamp=None): """Initialize. Args: rules_file_path (str): file location of rules snapshot_timestamp (str): timestamp for database. """ super(InstanceNetworkInterfaceRulesEngine, self).__init__(rules_file_path=rules_file_path) self.rule_book = None
[docs] def build_rule_book(self, global_configs=None): """Build InstanceNetworkInterfaceRuleBook from rules definition file. Args: global_configs (dict): Global Configs """ self.rule_book = InstanceNetworkInterfaceRuleBook( self._load_rule_definitions())
[docs] def find_violations(self, instance_network_interface, force_rebuild=False): """Determine whether the networks violates rules. Args: instance_network_interface (list): list of instance_network_interface force_rebuild (bool): set to false to not force a rebuiid Return: list: iterator of all violations """ violations = itertools.chain() if self.rule_book is None or force_rebuild: self.build_rule_book() resource_rules = self.rule_book.get_resource_rules() for rule in resource_rules: violations = itertools.chain(violations, rule.find_violations( instance_network_interface)) return violations
[docs] def add_rules(self, rules): """Add rules to the rule book. Args: rules (dicts): rule definitions """ if self.rule_book is not None: self.rule_book.add_rules(rules)
[docs]class InstanceNetworkInterfaceRuleBook(bre.BaseRuleBook): """The RuleBook for enforced networks resources.""" def __init__(self, rule_defs=None): """Initialize. Args: rule_defs (dict): The parsed dictionary of rules from the YAML definition file. """ super(InstanceNetworkInterfaceRuleBook, self).__init__() self.resource_rules_map = {} if not rule_defs: self.rule_defs = {} else: self.rule_defs = rule_defs self.add_rules(rule_defs)
[docs] def add_rules(self, rule_defs): """Add rules to the rule book. Args: rule_defs (dict): rules definitions """ for (i, rule) in enumerate(rule_defs.get('rules', [])): self.add_rule(rule, i)
[docs] 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
[docs] def get_resource_rules(self): """Get all the resource rules. Return: list: resource_rules_map values """ return list(self.resource_rules_map.values())
[docs]class Rule(object): """The rules class for instance_network_interface.""" def __init__(self, rule_name, rule_index, rules): """Initialize. Args: rule_name (str): Name of the loaded rule rule_index (int): The index of the rule from the definitions rules (dict): The resources associated with the rules like the whitelist """ self.rule_name = rule_name self.rule_index = rule_index self.rules = rules
[docs] def find_violations(self, instance_network_interface_list): """Raise violation is the ip is not in the whitelist. Args: instance_network_interface_list (list): list of InstanceNetworkInterface objects Yields: namedtuple: Returns RuleViolation named tuple """ for instance_network_interface in instance_network_interface_list: network_and_project = r'compute/.*/projects/([^/]*).*networks/([^/]*)', project = network = is_external_network = (instance_network_interface.access_configs is not None) ips = None if (network not in self.rules['whitelist'].get(project, []) and is_external_network): ips = [config['natIP'] for config in instance_network_interface.access_configs if 'natIP' in config] yield self.RuleViolation(, resource_type=resource_mod.ResourceType.INSTANCE,, full_name=instance_network_interface.full_name, rule_name=self.rule_name, rule_index=self.rule_index, violation_type='INSTANCE_NETWORK_INTERFACE_VIOLATION', project=project, network=network, ip=ips, resource_data=instance_network_interface.as_json())
# Rule violation. # resource_type: string # rule_name: string # rule_index: int # violation_type: UNENFORCED_NETWORK_VIOLATION # project: string # network: string # ip: string RuleViolation = namedtuple('RuleViolation', ['resource_type', 'resource_id', 'full_name', 'rule_name', 'rule_index', 'violation_type', 'project', 'network', 'ip', 'resource_data', 'resource_name'])