def test_missing_required_key(self, test_name, key): """A rule missing a required key returns error.""" test_rule = generate_ingress_rule() test_rule.pop(key) err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_allowed_missing_ip_protocol(self): """Rule missing IPProtocol in an allow predicate returns error.""" test_rule = generate_ingress_rule() test_rule['allowed'][0].pop('IPProtocol') err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_direction_egress_source_ranges(self): """Rule with direction set to EGRESS + sourceRanges returns error.""" test_rule = generate_egress_rule() test_rule['sourceRanges'] = ['10.8.0.0/24'] err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_invalid_direction(self): """Rule with direction set to invalid returns error.""" test_rule = generate_ingress_rule() test_rule['direction'] = 'INVALID' err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_no_allowed_or_denied(self): """A rule with no allow or deny ports returns error.""" test_rule = generate_ingress_rule() test_rule.pop('allowed') err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_denied_rule(self): """A rule with denied ports returns None.""" test_rule = generate_ingress_rule() allowed = test_rule.pop('allowed') test_rule['denied'] = allowed err = rule_validator.validate_gcp_rule(test_rule) self.assertIsNone(err, 'expected no error message, got err: %s' % err)
def test_allowed_and_denied(self): """A rule with allow and deny ports returns error.""" test_rule = generate_ingress_rule() test_rule['denied'] = [{'IPProtocol': u'udp'}] err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_unknown_key(self): """A rule with an unknown key returns error.""" test_rule = generate_ingress_rule() test_rule['someUnknownKey'] = True err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_missing_allowed_or_deny(self): """A rule missing either allowed returns error.""" test_rule = generate_ingress_rule() test_rule.pop('allowed') err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_ingress_keys_with_more_than_256_values(self, test_name, key): """Ingress rule entries with more than 256 values return error.""" test_rule = generate_ingress_rule() test_rule['direction'] = 'INGRESS' test_rule[key] = range(257) err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_source_and_destination_ranges(self): """Rule with sourceRanges and destinationRanges returns error.""" test_rule = generate_ingress_rule() test_rule['sourceRanges'] = [u'10.8.0.0/24'] test_rule['destinationRanges'] = [u'10.8.0.0/24'] err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_tag_name_validation(self, test_name, tag, expected): """A rule with invalid tag raises InvalidFirewallRuleError.""" test_rule = generate_ingress_rule() test_rule['sourceTags'] = [tag] err = rule_validator.validate_gcp_rule(test_rule) res = err is None self.assertEqual( expected, res, 'expected %s got %s with err: %s' % (expected, res, err))
def test_name_validation(self, test_name, name, expected): """Test different name validations.""" test_rule = generate_ingress_rule() test_rule['name'] = name err = rule_validator.validate_gcp_rule(test_rule) res = err is None self.assertEqual( expected, res, 'expected %s got %s with err: %s' % (expected, res, err))
def test_invalid_priority_out_of_range(self, test_name, priority, expected): """Rule with priority set to various values.""" test_rule = generate_ingress_rule() test_rule['priority'] = priority err = rule_validator.validate_gcp_rule(test_rule) res = err is None self.assertEqual( expected, res, 'expected %s got %s with err: %s' % (expected, res, err))
def test_direction_ingress_destination_ranges(self, test_name, explicit): """Rule with INGRESS direction + destinationRanges returns error.""" test_rule = generate_ingress_rule() if explicit: test_rule['direction'] = 'INGRESS' test_rule.pop('sourceRanges') test_rule['destinationRanges'] = [u'10.8.0.0/24'] err = rule_validator.validate_gcp_rule(test_rule) self.assertIsInstance(err, text, 'expected error message, got "%s"' % err)
def test_valid_explicit_ingress(self): """Verify valid ingress rule with explicit direction returns None.""" test_rule = generate_ingress_rule() test_rule['direction'] = 'INGRESS' err = rule_validator.validate_gcp_rule(test_rule) self.assertIsNone(err, 'expected no error message, got err: %s' % err)
def add_rule(self, rule, network_name=None): """Adds rule to the self.rules dictionary. Args: rule (dict): A valid dict representing a GCE firewall rule network_name (str): If set, rules which have no network currently defined will have their network set to network_name, and network_name will be prepended to the rule name. Rules that do have a network defined have their network matched against network_name, and if they differ the rule is not added. Raises: DuplicateFirewallRuleNameError: Two or more rules have the same name. InvalidFirewallRuleError: One or more rules failed validation. """ # pylint: disable=too-many-branches if not isinstance(rule, dict): raise InvalidFirewallRuleError( 'Invalid rule type. Found %s expected dict' % type(rule)) new_rule = self._order_lists_in_rule(rule) if network_name: if 'network' in new_rule: rule_network = get_network_name_from_url(new_rule['network']) if rule_network != network_name: # Don't add the rule if it's network does not match # network_name LOGGER.info( 'Firewall rule does not apply to network %s, ' 'skipping: %s', network_name, json.dumps(new_rule, sort_keys=True)) return # Normalize network value to full network URL for rules that # already include a network name, so comparison with a rule # from the API will return True new_rule['network'] = build_network_url( self._project, network_name) else: new_rule['network'] = build_network_url( self._project, network_name) # Update the rule name by prepending the network, so it is # unique. If the new rule does not have a name defined it will # fail the _check_rule_before_adding validation and an # InvalidFirewallRuleError exception will be raised. if 'name' in new_rule: # Truncate network name if too long. This may result in # duplicate rule names, which will cause the network name # to be changed to a md5 hash representation. new_name = '%s-%s' % (network_name[:( 62 - len(new_rule['name']))], new_rule['name']) while new_name in self.rules: # Firewall rule names must start with [a-z], hashes # could start with a number, so we prepend hn- # (hashed network) to the name. network_name = 'hn-' + hashlib.md5( network_name.encode()).hexdigest() new_name = '%s-%s' % (network_name[:( 62 - len(new_rule['name']))], new_rule['name']) new_rule['name'] = new_name if 'priority' not in new_rule: new_rule['priority'] = self.DEFAULT_PRIORITY if 'direction' not in new_rule: new_rule['direction'] = self.DEFAULT_DIRECTION if 'logConfig' not in new_rule: new_rule['logConfig'] = self.DEFAULT_LOGCONFIG if 'disabled' not in new_rule: new_rule['disabled'] = self.DEFAULT_DISABLED error = rv.validate_gcp_rule(new_rule) if error: raise InvalidFirewallRuleError(error) if rule['name'] in self.rules: raise DuplicateFirewallRuleNameError( 'Rule %s already defined in rules: %s' % (rule['name'], ', '.join(sorted(self.rules.keys())))) callback_ok = (self._add_rule_callback(new_rule) if self._add_rule_callback else True) if callback_ok: self.rules[new_rule['name']] = new_rule
def test_valid_egress(self): """Verify valid egress rules returns None.""" test_rule = generate_egress_rule() err = rule_validator.validate_gcp_rule(test_rule) self.assertIsNone(err, 'expected no error message, got err: %s' % err)