def test_sg_rule_icmp(self): # ICMP has stateless and stateful types sg = self.create_security_group() for ip_version in self.ip_versions: if Topology.up_to_nuage('5.4') and ip_version == 6: # do not test ipv6 icmp on 5.4 and below continue ethertype = 'IPv' + str(ip_version) stateful_types = (n_constants.STATEFUL_ICMP_V4_TYPES if ip_version == 4 else n_constants.STATEFUL_ICMP_V6_TYPES) icmp_protocol = 'icmp' if ip_version == 4 else 'ipv6-icmp' # Create stateful rules for stateful_type in stateful_types: self.create_security_group_rule_with_manager( security_group=sg, direction='ingress', ethertype=ethertype, protocol=icmp_protocol, port_range_min=stateful_type, port_range_max=0) # Create stateless rule: icmp_type: 69 self.create_security_group_rule_with_manager( security_group=sg, direction='egress', ethertype=ethertype, protocol=icmp_protocol, port_range_min=69, port_range_max=0) # Check for cross-contamination between IPV4 and IPV6 stateful # types by creating with icmp_code that is stateful in the # other ethertype all_stateful_types = (n_constants.STATEFUL_ICMP_V4_TYPES + n_constants.STATEFUL_ICMP_V6_TYPES) for stateful_type in all_stateful_types: if stateful_type not in stateful_types: self.create_security_group_rule_with_manager( security_group=sg, direction='ingress', ethertype=ethertype, protocol=icmp_protocol, port_range_min=stateful_type, port_range_max=0) # Check legacy icmpv6 usage if ip_version == 6: self.create_security_group_rule_with_manager( security_group=sg, direction='egress', ethertype=ethertype, protocol='icmpv6', port_range_min=68, port_range_max=0) sg = self.get_security_group(sg['id']) port = self.create_port(self.network, security_groups=[sg['id']]) # Verify VSD # Get updated SG with SGRules sg = self.get_security_group(sg['id']) self._verify_sg(sg, ports=[port])
# Copyright 2020 NOKIA # All Rights Reserved. import getpass import inspect from nuage_tempest_plugin.lib.topology import Topology if Topology.up_to_nuage('20.10'): from vspk import v5_0 as vspk5 else: vspk5 = None if Topology.from_nuage('6.0'): from vspk import v6 as vspk6 else: vspk6 = None CONF = Topology.get_conf() LOG = Topology.get_logger(__name__) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # CAUTION : THIS SUITE IS HIGHLY INTRUSIVE # - it relies heavily on devstack env # - it installs new packages in the tox env (like neutron) # - it changes the neutron branch out of which neutron runs # - it restarts neutron # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def _verify_sg_rule(self, pg, sg, sg_rule, is_reverse_rule=False, domain=None): """_verify_sg_rule :param pg: Policygroup :param sg: Securitygroup :param sg_rule: security group rule to check :param is_reverse_rule: True if the rule is a reverse rule of an actual sg_rule. Used for not checking the reverse of a reverse rule. :param domain: VSD domain where ACL is located """ domain = domain or self.domain is_ipv4 = sg_rule['ethertype'] == 'IPv4' stateful_icmp_types = (n_constants.STATEFUL_ICMP_V4_TYPES if is_ipv4 else n_constants.STATEFUL_ICMP_V6_TYPES) ext_id_filter = self.vsd.get_external_id_filter(sg_rule['id']) if sg_rule['direction'] == 'ingress': acl_entry = domain.egress_acl_entry_templates.get_first( filter=ext_id_filter) else: acl_entry = domain.ingress_acl_entry_templates.get_first( filter=ext_id_filter) self.assertIsNotNone( acl_entry, "aclEntryTemplate not found for " "SG Rule: {}".format(sg_rule)) # Remote group id refers to another Policy Group remote_pg = None if sg_rule.get('remote_group_id'): ext_id_filter = self.vsd.get_external_id_filter( sg_rule['remote_group_id']) remote_pgs = domain.policy_groups.get(ext_id_filter) self.assertNotEmpty(remote_pgs, "Remote PG not found") remote_pg = remote_pgs[0] # Remote ip prefix refers to enterprise network / network macro enterprise_network = None if sg_rule.get('remote_ip_prefix'): ip_network = netaddr.IPNetwork(sg_rule['remote_ip_prefix']) enterprise_network = self._get_enterprise_network(ip_network) # Ethertype self.assertEqual(n_constants.PROTO_NAME_TO_NUM[sg_rule['ethertype']], acl_entry.ether_type) # Protocol os_protocol = sg_rule['protocol'] try: expected_protocol = int(os_protocol) except (ValueError, TypeError): if not os_protocol: expected_protocol = 'ANY' elif os_protocol == 'icmp' and sg_rule['ethertype'] == 'IPv6': expected_protocol = n_constants.PROTO_NAME_TO_NUM['icmpv6'] else: expected_protocol = n_constants.PROTO_NAME_TO_NUM[os_protocol] self.assertEqual(expected_protocol, acl_entry.protocol) # Stateful if not sg['stateful']: expected_stateful = False elif str(os_protocol) in ['icmp', 'icmpv6', 'ipv6-icmp', 1, 58]: if Topology.up_to_nuage('5.4') and not is_ipv4: # no support for icmp v6 in 5.4 expected_stateful = True else: # ICMP rules are not stateful unless special cases if (not sg_rule['port_range_min'] and not sg_rule['port_range_max']): expected_stateful = False elif (sg_rule['port_range_min'] not in stateful_icmp_types): expected_stateful = False else: expected_stateful = True else: expected_stateful = True self.assertEqual(expected_stateful, acl_entry.stateful) # Network Type if (sg_rule.get('remote_group_id') or sg_rule.get('remote_external_group_id')): expected_network_type = 'POLICYGROUP' elif sg_rule.get('remote_ip_prefix'): expected_network_type = 'ENTERPRISE_NETWORK' else: if Topology.from_nuage('20.10'): expected_network_type = 'ANY' else: # Legacy usage of ANY network macro / enterprise network expected_network_type = 'ENTERPRISE_NETWORK' self.assertEqual(expected_network_type, acl_entry.network_type) # Network ID if sg_rule.get('remote_external_group_id'): expected_network_id = sg_rule['remote_external_group_id'] elif sg_rule.get('remote_ip_prefix'): expected_network_id = enterprise_network.id elif sg_rule.get('remote_group_id'): expected_network_id = remote_pg.id else: if Topology.from_nuage('20.10'): # No network id, as ANY type is used expected_network_id = None else: # Legacy usage of ANY network macro / enterprise network address = '0.0.0.0/0' if is_ipv4 else '::/0' ip_network = netaddr.IPNetwork(address) enterprise_network = self._get_enterprise_network(ip_network) expected_network_id = enterprise_network.id self.assertEqual(expected_network_id, acl_entry.network_id) # Location type self.assertEqual('POLICYGROUP', acl_entry.location_type) # Location ID self.assertEqual(pg.id, acl_entry.location_id) # Action self.assertEqual('FORWARD', acl_entry.action) # DSCP self.assertEqual('*', acl_entry.dscp) # TCP/UDP specific attributes if sg_rule['protocol'] in ['tcp', 'udp']: # Source port self.assertEqual('*', acl_entry.source_port) # Destination port if (not sg_rule['port_range_min'] and not sg_rule['port_range_max']): expected_dest_port = '*' elif sg_rule['port_range_min'] == sg_rule['port_range_max']: expected_dest_port = str(sg_rule['port_range_min']) else: expected_dest_port = '{}-{}'.format(sg_rule['port_range_min'], sg_rule['port_range_max']) self.assertEqual(expected_dest_port, acl_entry.destination_port) # ICMP specific attributes elif sg_rule['protocol'] in ['icmp', 'icmpv6', 'ipv6-icmp', 1, 58]: if sg_rule['port_range_min']: self.assertEqual(str(sg_rule['port_range_min']), acl_entry.icmp_type) if sg_rule['port_range_max']: self.assertEqual(str(sg_rule['port_range_max']), acl_entry.icmp_code) # check reverse ICMP rule if needed if Topology.up_to_nuage('5.4') and not is_ipv4: # no support for icmpv6 reverse rules return else: acl_icmp_type = (int(acl_entry.icmp_type) if acl_entry.icmp_type != '*' else '*') if (acl_icmp_type not in stateful_icmp_types and not is_reverse_rule): sg_rule['direction'] = ('ingress' if sg_rule['direction'] == 'egress' else 'egress') self._verify_sg_rule(pg, sg, sg_rule, is_reverse_rule=True, domain=domain)
class TestFWaaS(fwaas_mixins.FWaaSClientMixin, NuageBaseTest): default_prepare_for_connectivity = True @classmethod def skip_checks(cls): super(TestFWaaS, cls).skip_checks() if not Topology.has_fwaas_v6_support(): msg = 'No fwaas v6 support.' raise cls.skipException(msg) def setUp(self): super(TestFWaaS, self).setUp() required_exts = ['fwaas', 'security-group', 'router'] for ext in required_exts: if not test.is_extension_enabled(ext, 'network'): msg = "%s Extension not enabled." % ext raise self.skipException(msg) def assert_no_icmp_connectivity(self, **kwargs): self.assert_icmp_connectivity(is_connectivity_expected=False, **kwargs) def assert_no_tcp_connectivity(self, **kwargs): self.assert_tcp_connectivity(is_connectivity_expected=False, **kwargs) def assert_connectivity(self, **kwargs): self.assert_icmp_connectivity(**kwargs) self.assert_tcp_connectivity(**kwargs) def assert_no_connectivity(self, **kwargs): self.assert_no_icmp_connectivity(**kwargs) self.assert_no_tcp_connectivity(**kwargs) def _empty_policy(self, router_id=None, **_kwargs): # NOTE(yamamoto): an empty policy would deny all # We allow ipv4 traffic here fw_rule_ipv4 = self.create_firewall_rule(action='allow', ip_version=4) fw_policy = self.create_firewall_policy( firewall_rules=[fw_rule_ipv4['id']]) fw = self.create_firewall(firewall_policy_id=fw_policy['id'], router_ids=[router_id]) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, } def _all_disabled_rules(self, **_kwargs): # NOTE(yamamoto): a policy whose rules are all disabled would deny all fw_rule = self.create_firewall_rule(action="allow", enabled=False, ip_version=6) fw_rule_ipv4 = self.create_firewall_rule(action='allow', ip_version=4) fw_policy = self.create_firewall_policy( firewall_rules=[fw_rule['id'], fw_rule_ipv4['id']]) fw = self.create_firewall(firewall_policy_id=fw_policy['id']) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'fw_rule': fw_rule, } def _block_destination_ip(self, server1_fixed_ip, server2_fixed_ip, router_id, **_kwargs): rules = [ # NOTE(yamamoto): The filtering is taken place after # destination ip is rewritten to fixed-ip. self.create_firewall_rule(destination_ip_address=server2_fixed_ip, action="deny", ip_version=6), self.create_firewall_rule(action='allow', ip_version=6), self.create_firewall_rule(action="allow", ip_version=4) ] rule_ids = [r['id'] for r in rules] fw_policy = self.create_firewall_policy(firewall_rules=rule_ids) fw = self.create_firewall(firewall_policy_id=fw_policy['id'], router_ids=[router_id]) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'server1_fixed_ip': server1_fixed_ip, 'server2_fixed_ip': server2_fixed_ip, } def _block_source_ip(self, server1_fixed_ip, server2_fixed_ip, router_id, **_kwargs): rules = [ # NOTE(yamamoto): The filtering is taken place after # destination ip is rewritten to fixed-ip. self.create_firewall_rule(source_ip_address=server1_fixed_ip, action="deny", ip_version=6), self.create_firewall_rule(action='allow', ip_version=6), self.create_firewall_rule(action="allow", ip_version=4) ] fw_policy = self.create_firewall_policy( firewall_rules=[r['id'] for r in rules]) fw = self.create_firewall(firewall_policy_id=fw_policy['id'], router_ids=[router_id]) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'server1_fixed_ip': server1_fixed_ip, 'server2_fixed_ip': server2_fixed_ip, } def _block_icmp(self, router_id=None, **_kwargs): deny_icmp = self.create_firewall_rule(protocol="ipv6-icmp", action="deny", ip_version=6) allow_ipv6 = self.create_firewall_rule(action="allow", ip_version=6) allow_ipv4 = self.create_firewall_rule(action='allow', ip_version=4) fw_policy = self.create_firewall_policy(firewall_rules=[ deny_icmp['id'], allow_ipv6['id'], allow_ipv4['id'] ]) fw = self.create_firewall(firewall_policy_id=fw_policy['id'], router_ids=[router_id]) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'fw_rule': deny_icmp, 'router_id': router_id } def _block_all_with_default_allow(self, router_id, **_kwargs): fw_rule = self.create_firewall_rule(action="deny", ip_version=6) fw_rule_allow = self.create_firewall_rule(action="allow", ip_version=6) fw_rule_ipv4 = self.create_firewall_rule(action='allow', ip_version=4) fw_policy = self.create_firewall_policy(firewall_rules=[ fw_rule['id'], fw_rule_allow['id'], fw_rule_ipv4['id'] ]) fw = self.create_firewall(firewall_policy_id=fw_policy['id'], router_ids=[router_id]) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'fw_rules': [fw_rule], } def _block_certain_ports(self, router_id, **_kwargs): deny_source_9090 = self.create_firewall_rule(action="deny", ip_version=6, source_port=9090, protocol='tcp') deny_destination_80 = self.create_firewall_rule(action="deny", ip_version=6, destination_port=80, protocol='tcp') allow_v6 = self.create_firewall_rule(action="allow", ip_version=6) allow_v4 = self.create_firewall_rule(action='allow', ip_version=4) fw_policy = self.create_firewall_policy(firewall_rules=[ deny_source_9090['id'], deny_destination_80['id'], allow_v6['id'], allow_v4['id'] ]) fw = self.create_firewall(firewall_policy_id=fw_policy['id'], router_ids=[router_id]) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'fw_rules': (deny_source_9090, deny_destination_80), } def _confirm_certain_ports_blocked(self, from_server, to_server): servers = {'from_server': from_server, 'to_server': to_server} # icmp not blocked self.assert_icmp_connectivity(**servers) # ports that are not blocked self.assert_tcp_connectivity(destination_port=81, source_port=9091, **servers) # blocked destination port self.assert_no_tcp_connectivity(destination_port=80, source_port=9092, **servers) # blocked source port self.assert_no_tcp_connectivity(destination_port=81, source_port=9090, **servers) def _confirm_certain_ports_allowed(self, from_server, to_server): servers = {'from_server': from_server, 'to_server': to_server} # icmp not blocked self.assert_icmp_connectivity(**servers) # ports that are not blocked self.assert_tcp_connectivity(destination_port=81, source_port=9091, **servers) # blocked destination port self.assert_tcp_connectivity(destination_port=80, source_port=9092, **servers) # blocked source port self.assert_tcp_connectivity(destination_port=81, source_port=9090, **servers) def _remove_rule_and_wait(self, firewall_id, firewall_policy_id, firewall_rule_id): self.firewall_policies_client.remove_firewall_rule_from_policy( firewall_policy_id=firewall_policy_id, firewall_rule_id=firewall_rule_id) self._wait_firewall_ready(firewall_id) def _delete_firewall(self, ctx): self.delete_firewall_and_wait(ctx['fw']['id']) def _remove_rule(self, ctx): for rule in ctx['fw_rules']: self._remove_rule_and_wait( firewall_id=ctx['fw']['id'], firewall_policy_id=ctx['fw_policy']['id'], firewall_rule_id=rule['id']) def _disable_rules(self, ctx): for rule in ctx['fw_rules']: self.firewall_rules_client.update_firewall_rule( firewall_rule_id=rule['id'], enabled=False) self._wait_firewall_ready(ctx['fw']['id']) def _reverse_rules_order(self, ctx): self.firewall_policies_client.update_firewall_policy( ctx['fw_policy']['id'], firewall_rules=list(reversed(ctx['fw_policy']['firewall_rules']))) self._wait_firewall_ready(ctx['fw']['id']) def _confirm_blocked_one_way(self, from_server, to_server, **_kwargs): # one way self.assert_no_icmp_connectivity(from_server=from_server, to_server=to_server) self.assert_no_tcp_connectivity(from_server=from_server, to_server=to_server) # other way self.assert_icmp_connectivity(from_server=to_server, to_server=from_server) self.assert_tcp_connectivity(from_server=to_server, to_server=from_server) def _confirm_icmp_blocked_but_tcp_allowed(self, from_server, to_server): self.assert_no_icmp_connectivity(from_server=from_server, to_server=to_server) self.assert_tcp_connectivity(from_server=from_server, to_server=to_server) def _create_topology(self, router, cidrv4=None, cidrv6=None): """Create a topology for testing +--------+ +-----------+ |"server"| | "subnet" | | VM +-------------+ "network" | +--------+ +----+------+ | | router interface port +----+-----+ | "router" | +----+-----+ | router gateway port | | +----+------------------+ | existing network | | ("public_network_id") | +-----------------------+ """ network = self.create_network() subnet = self.create_subnet(network, cidr=cidrv4) subnet6 = self.create_subnet(network, ip_version=6, cidr=cidrv6) self.router_attach(router, subnet) self.router_attach(router, subnet6) security_group = self.create_open_ssh_security_group() self.create_security_group_rule_with_manager(security_group, direction='ingress', protocol='tcp', ethertype='IPv6', port_range_min=80, port_range_max=9999) server = self.create_tenant_server([network], security_groups=[security_group], prepare_for_connectivity=True) fixed_ip4 = IPAddress( server.get_server_ip_in_network(network['name'], 4)) fixed_ip6 = IPAddress( server.get_server_ip_in_network(network['name'], 6)) return server, fixed_ip4, fixed_ip6 def _test_firewall_basic(self, block, allow=None, confirm_allowed=None, confirm_blocked=None, ports_for_webserver=(80, )): LOG.info('[{}] Begin _test_firewall_basic'.format(self.test_tag)) if allow is None: allow = self._delete_firewall if confirm_allowed is None: confirm_allowed = self.assert_connectivity if confirm_blocked is None: confirm_blocked = self.assert_no_connectivity LOG.info('[{}] 1. Creating topology'.format(self.test_tag)) try: router = self.get_router() except AttributeError: # In queens/rocky-em this function is defined as # _get_router router = self._get_router() (server2, server2_fixed_ip4, server2_fixed_ip6) = self._create_topology( router, cidrv4=IPNetwork('20.0.0.0/24'), cidrv6=IPNetwork('cafe:bace::/64')) (server1, server1_fixed_ip4, server1_fixed_ip6) = self._create_topology( router, cidrv4=IPNetwork('10.0.0.0/24'), cidrv6=IPNetwork('cafe:babe::/64')) for port in ports_for_webserver: self.start_web_server(server1, port=port) self.start_web_server(server2, port=port) self.sleep(10, 'Naively mitigating slow CI') server1.echo_debug_info() server2.echo_debug_info() LOG.info('[{}] 2. Verify connectivity'.format(self.test_tag)) self.assert_connectivity(from_server=server1, to_server=server2) self.sleep(10, 'Naively mitigating slow CI') LOG.info('[{}] 3. Create firewall'.format(self.test_tag)) ctx = block(server1_fixed_ip=server1_fixed_ip6, server2_fixed_ip=server2_fixed_ip6, router_id=router['id']) self.sleep(10, 'Naively mitigating slow CI') LOG.info('[{}] 4. Verify no connectivity'.format(self.test_tag)) confirm_blocked(from_server=server1, to_server=server2) LOG.info('[{}] 5. Allow traffic'.format(self.test_tag)) allow(ctx) self.sleep(10, 'Naively mitigating slow CI') LOG.info('[{}] 6. Verify connectivity'.format(self.test_tag)) confirm_allowed(from_server=server1, to_server=server2) def test_block_port(self): self._test_firewall_basic( block=self._block_certain_ports, confirm_blocked=self._confirm_certain_ports_blocked, allow=self._disable_rules, confirm_allowed=self._confirm_certain_ports_allowed, ports_for_webserver=(80, 81)) def test_firewall_block_source_ip(self): self._test_firewall_basic( block=self._block_source_ip, confirm_blocked=self._confirm_blocked_one_way) def test_firewall_destination_ip(self): self._test_firewall_basic( block=self._block_destination_ip, confirm_blocked=self._confirm_blocked_one_way) def test_firewall_block_icmp(self): self._test_firewall_basic( block=self._block_icmp, confirm_blocked=self._confirm_icmp_blocked_but_tcp_allowed) def test_firewall_remove_rule(self): self._test_firewall_basic(block=self._block_all_with_default_allow, allow=self._remove_rule) def test_firewall_disable_rule(self): self._test_firewall_basic(block=self._block_all_with_default_allow, allow=self._disable_rules) def test_firewall_empty_policy(self): self._test_firewall_basic(block=self._empty_policy) def test_firewall_all_disabled_rules(self): self._test_firewall_basic(block=self._all_disabled_rules) @testtools.skipIf(Topology.at_nuage('6.0') or Topology.up_to_nuage('5.4'), reason='VSD-42518') def test_firewall_order_rules(self): self._test_firewall_basic(block=self._block_all_with_default_allow, allow=self._reverse_rules_order)