def testAddressListExcludeCaseSix(self): # IPv6 does not affect IPv4 superset = [nacaddr.IPv6('0::ffff:0.0.0.0/96')] excludes = [nacaddr.IPv4('0.0.0.0/0')] expected = [nacaddr.IPv6('0::ffff:0.0.0.0/96')] self.assertListEqual(nacaddr.AddressListExclude(superset, excludes), expected)
def testAddressListExcludeCaseOne(self): # Small block eliminated by large block, and an extra block that stays. # For both IP versions. superset = [nacaddr.IPv4('200.0.0.0/24'), nacaddr.IPv4('10.1.0.0/24'), nacaddr.IPv6('200::/56'), nacaddr.IPv6('10:1::/56')] excludes = [nacaddr.IPv6('10::/16'), nacaddr.IPv4('10.0.0.0/8')] expected = [nacaddr.IPv4('200.0.0.0/24'), nacaddr.IPv6('200::/56')] self.assertListEqual(nacaddr.AddressListExclude(superset, excludes), expected)
def testSafeCollapsing(self): test_data = [([nacaddr.IPv4('10.0.0.0/8'), nacaddr.IPv4('10.0.0.0/10')], [nacaddr.IPv4('10.0.0.0/9')], [nacaddr.IPv4('10.0.0.0/8'), nacaddr.IPv4('10.0.0.0/10')]), ([nacaddr.IPv4('192.168.0.0/27'), nacaddr.IPv4('192.168.0.0/24')], [nacaddr.IPv4('192.168.1.0/24')], [nacaddr.IPv4('192.168.0.0/24')]), ([nacaddr.IPv6('10::/56'), nacaddr.IPv6('10::/128')], [nacaddr.IPv6('10::/64')], [nacaddr.IPv6('10::/56'), nacaddr.IPv6('10::/128')]), ([nacaddr.IPv6('10::/128'), nacaddr.IPv6('10::/56')], [nacaddr.IPv6('8::/64')], [nacaddr.IPv6('10::/56')]) ] for addresses, complement_addresses, result in test_data: self.assertEqual(nacaddr.CollapseAddrList(addresses, complement_addresses), result)
def testTermAddressByteLength(self): """Tests the AddressByteLength function.""" pol = HEADER + GOOD_TERM_2 self.naming.GetNetAddr.return_value = [ nacaddr.IPv4('10.0.0.1/32'), nacaddr.IPv4('10.0.0.2/32'), nacaddr.IPv6('2001:4860:4860::8844/128'), nacaddr.IPv6('2001:4860:4860::8888/128')] ret = policy.ParsePolicy(pol, self.naming) term = ret.filters[0][1][0] self.assertEqual(2, term.AddressesByteLength([4])) self.assertEqual(8, term.AddressesByteLength([6])) self.assertEqual(10, term.AddressesByteLength())
def test_token_to_ips(self): expected_results = [(r'GOOGLE_DNS', [ nacaddr.IPv4('8.8.4.4/32'), nacaddr.IPv4('8.8.8.8/32'), nacaddr.IPv6('2001:4860:4860::8844/128'), nacaddr.IPv6('2001:4860:4860::8888/128') ])] options = Namespace() options.obj = ('GOOGLE_DNS', ) results = cgrep.get_nets(options.obj, self.db) self.assertEquals(results[0][0], expected_results[0][0]) self.assertEquals(set(results[0][1]), set(expected_results[0][1]))
def __init__(self, term, af): """Setup a new nftables term. Args: term: A policy.Term object af: The capirca address family for the term, "inet", "inet6", or "mixed" Raises: InvalidAddressFamily: if supplied target options are invalid. Note: AF of mixed requires kernel 3.15 or higher """ super(Term, self).__init__(term) self.term = term self.af = af if af == 'inet6': self.all_ips = nacaddr.IPv6('::/0') elif af == 'inet': self.all_ips = nacaddr.IPv4('0.0.0.0/0') elif af == 'mixed': # TODO(castagno): Need to add support for a mixed address family raise InvalidAddressFamily( 'Address family mixed is not supported yet') else: raise InvalidAddressFamily('Not a valid address family')
def testV6SlashFourteenReplacement(self): self.naming.GetNetAddr.return_value = ([ nacaddr.IPv4('0.0.0.0/1'), nacaddr.IPv6('::/14') ]) self.naming.GetServiceByProto.return_value = ['25'] expectedv4 = (' term good-term-2-inet {\n' + ' from {\n' + ' destination-address {\n' + ' 0.0.0.0/1;\n' + ' }') expectedv6 = (' term good-term-2-inet6 {\n' + ' from {\n' + ' destination-address {\n' + ' ::/16;\n' + ' 1::/16;\n' + ' 2::/16;\n' + ' 3::/16;\n' + ' }') msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER_MIXED + GOOD_TERM_1, self.naming), EXP_INFO) output = str(msmpc) self.assertIn(expectedv4, output, output) self.assertIn(expectedv6, output, output)
def __init__(self, term, filter_name, filter_action, af='inet'): """Setup a new term. Args: term: A policy.Term object to represent in windows_ipsec. filter_name: The name of the filter chan to attach the term to. filter_action: The default action of the filter. af: Which address family ('inet' or 'inet6') to apply the term to. Raises: UnsupportedFilterError: Filter is not supported. """ super().__init__(term) self.term = term # term object self.filter = filter_name # actual name of filter self.default_action = filter_action self.options = [] self.af = af if af == 'inet6': self._all_ips = nacaddr.IPv6('::/0') else: self._all_ips = nacaddr.IPv4('0.0.0.0/0') self.term_name = '%s_%s' % (self.filter[:1], self.term.name)
def testMixedAddresses(self): self.assertListEqual(self.defs.GetNetAddr('BING'), [nacaddr.IPv4('10.0.0.0/8'), nacaddr.IPv6('::FFFF:FFFF:FFFF:FFFF')]) # same thing but letting nacaddr decide which v4 or v6. self.assertListEqual(self.defs.GetNetAddr('BING'), [nacaddr.IP('10.0.0.0/8'), nacaddr.IP('::FFFF:FFFF:FFFF:FFFF')])
def testRestrictAddressFamilyType(self): self.naming.GetNetAddr.return_value = [ nacaddr.IPv4('127.0.0.1'), nacaddr.IPv6('::1/128')] acl = cisco.Cisco(policy.ParsePolicy(GOOD_MIXED_HEADER + GOOD_TERM_23, self.naming), EXP_INFO) output = str(acl) self.assertIn('127.0.0.1', output, output) self.assertNotIn('::1/128', output, output) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST')
def testBridgeFilterInetType(self): self.naming.GetNetAddr.return_value = [ nacaddr.IPv4('127.0.0.1'), nacaddr.IPv6('::1/128')] jcl = juniper.Juniper(policy.ParsePolicy( GOOD_HEADER_BRIDGE + GOOD_TERM_12, self.naming), EXP_INFO) output = str(jcl) self.assertNotIn('::1/128', output, output) self.naming.GetNetAddr.assert_called_once_with('LOCALHOST')
def test_token_to_ip_fail(self): expected_results = [(r'GOOGLE_DNS', [ nacaddr.IPv4('69.171.239.12/32'), nacaddr.IPv6('2a03:2880:fffe:c:face:b00c:0:35/128') ])] options = Namespace() options.obj = ('GOOGLE_DNS', ) results = cgrep.get_nets(options.obj, self.db) # the network object name should match, but not the IPs contained within self.assertEquals(results[0][0], expected_results[0][0]) self.assertNotEquals(set(results[0][1]), set(expected_results[0][1]))
def testIPv6IcmpOrder(self): self.naming.GetNetAddr.return_value = [ nacaddr.IPv6('fd87:6044:ac54:3558::/64')] pol = policy.ParsePolicy(IPV6_HEADER_1 + ICMPV6_TERM_1, self.naming) acl = iptables.Iptables(pol, EXP_INFO) result = str(acl) self.failUnless('-s fd87:6044:ac54:3558::/64 -p ipv6-icmp -m icmp6' ' --icmpv6-type 1' in result, 'incorrect order of ICMPv6 match elements') self.naming.GetNetAddr.assert_called_once_with('IPV6_INTERNAL')
def testAddressListExcludeCaseThree(self): # Two blocks off both ends of a large block. superset = [nacaddr.IPv4('200.0.0.0/24'), nacaddr.IPv4('10.0.0.0/8'), nacaddr.IPv6('200::/56'), nacaddr.IPv6('10::/16')] excludes = [nacaddr.IPv6('10::/18'), nacaddr.IPv6('10:c000::/18'), nacaddr.IPv4('10.0.0.0/10'), nacaddr.IPv4('10.192.0.0/10')] expected = [nacaddr.IPv4('10.64.0.0/10'), nacaddr.IPv4('10.128.0.0/10'), nacaddr.IPv4('200.0.0.0/24'), nacaddr.IPv6('10:4000::/18'), nacaddr.IPv6('10:8000::/18'), nacaddr.IPv6('200::/56')] self.assertListEqual(nacaddr.AddressListExclude(superset, excludes), expected)
def testCollapsing(self): ip1 = nacaddr.IPv4('1.1.0.0/24', 'foo') ip2 = nacaddr.IPv4('1.1.1.0/24', 'foo') ip3 = nacaddr.IPv4('1.1.2.0/24', 'baz') ip4 = nacaddr.IPv4('1.1.3.0/24') ip5 = nacaddr.IPv4('1.1.4.0/24') # stored in no particular order b/c we want CollapseAddr to call [].sort # and we want that sort to call nacaddr.IP.__cmp__() on our array members ip6 = nacaddr.IPv4('1.1.0.0/22') # check that addreses are subsumed properlly. collapsed = nacaddr.CollapseAddrList([ip1, ip2, ip3, ip4, ip5, ip6]) self.assertEqual(len(collapsed), 2) # test that the comments are collapsed properlly, and that comments aren't # added to addresses that have no comments. self.assertListEqual([collapsed[0].text, collapsed[1].text], ['foo, baz', '']) self.assertListEqual( collapsed, [nacaddr.IPv4('1.1.0.0/22'), nacaddr.IPv4('1.1.4.0/24')]) # test that two addresses are supernet'ed properlly collapsed = nacaddr.CollapseAddrList([ip1, ip2]) self.assertEqual(len(collapsed), 1) self.assertEqual(collapsed[0].text, 'foo') self.assertListEqual(collapsed, [nacaddr.IPv4('1.1.0.0/23')]) ip_same1 = ip_same2 = nacaddr.IPv4('1.1.1.1/32') self.assertListEqual(nacaddr.CollapseAddrList([ip_same1, ip_same2]), [ip_same1]) ip1 = nacaddr.IPv6('::2001:1/100') ip2 = nacaddr.IPv6('::2002:1/120') ip3 = nacaddr.IPv6('::2001:1/96') # test that ipv6 addresses are subsumed properlly. collapsed = nacaddr.CollapseAddrList([ip1, ip2, ip3]) self.assertListEqual(collapsed, [ip3])
def testMixedv6(self): self.naming.GetNetAddr.return_value = ([nacaddr.IPv6('2001::/33')]) self.naming.GetServiceByProto.return_value = ['25'] expected = (' term good-term-2 {\n' + ' from {\n' + ' destination-address {\n' + ' 2001::/33;\n' + ' }') msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER_MIXED + GOOD_TERM_1, self.naming), EXP_INFO) output = str(msmpc) self.assertIn(expected, output, output)
def testNoVerboseV6(self): addr_list = list() for octet in range(0, 256): net = nacaddr.IPv6('2001:db8:1010:' + str(octet) + '::64/64') addr_list.append(net) self.naming.GetNetAddr.return_value = addr_list self.naming.GetServiceByProto.return_value = ['25'] jcl = juniper.Juniper( policy.ParsePolicy( GOOD_NOVERBOSE_V6_HEADER + GOOD_TERM_1 + GOOD_TERM_COMMENT, self.naming), EXP_INFO) self.failUnless('2001:db8:1010:90::/61;' in str(jcl)) self.failUnless('COMMENT' not in str(jcl)) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST') self.naming.GetServiceByProto.assert_called_once_with('SMTP', 'tcp')
def testNoVerboseV6(self): addr_list = list() for octet in range(0, 256): net = nacaddr.IPv6("2001:db8:1010:" + str(octet) + "::64/64", strict=False) addr_list.append(net) self.naming.GetNetAddr.return_value = addr_list self.naming.GetServiceByProto.return_value = ["25"] atp = arista_tp.AristaTrafficPolicy( policy.ParsePolicy( GOOD_NOVERBOSE_V6_HEADER + GOOD_TERM_1 + GOOD_TERM_COMMENT, self.naming), EXP_INFO) self.assertIn("2001:db8:1010:90::/61", str(atp)) self.assertNotIn("COMMENT", str(atp)) self.naming.GetNetAddr.assert_called_once_with("SOME_HOST") self.naming.GetServiceByProto.assert_called_once_with("SMTP", "tcp")
def testNoVerboseV6(self): addr_list = list() for octet in range(0, 256): net = nacaddr.IPv6('2001:db8:1010:' + str(octet) + '::64/64', strict=False) addr_list.append(net) self.naming.GetNetAddr.return_value = addr_list self.naming.GetServiceByProto.return_value = ['25'] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy( GOOD_NOVERBOSE_V6_HEADER + GOOD_TERM_1 + GOOD_TERM_COMMENT, self.naming), EXP_INFO) self.assertIn('2001:db8:1010:90::/61;', str(msmpc)) self.assertNotIn('COMMENT', str(msmpc)) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST') self.naming.GetServiceByProto.assert_called_once_with('SMTP', 'tcp')
def __init__(self, term, filter_name, trackstate, filter_action, af='inet', verbose=True): """Setup a new term. Args: term: A policy.Term object to represent in iptables. filter_name: The name of the filter chan to attach the term to. trackstate: Specifies if conntrack should be used for new connections. filter_action: The default action of the filter. af: Which address family ('inet' or 'inet6') to apply the term to. verbose: boolean if comments should be printed Raises: UnsupportedFilterError: Filter is not supported. """ super(Term, self).__init__(term) self._action_table = { 'accept': '-j ACCEPT', 'deny': '-j DROP', 'reject': '-j REJECT --reject-with icmp-host-prohibited', 'reject-with-tcp-rst': '-j REJECT --reject-with tcp-reset', 'next': '-j RETURN' } self.trackstate = trackstate self.term = term # term object self.filter = filter_name # actual name of filter self.default_action = filter_action self.options = [] self.af = af self.verbose = verbose if af == 'inet6': self._all_ips = nacaddr.IPv6('::/0') self._action_table['reject'] = ('-j REJECT --reject-with ' 'icmp6-adm-prohibited') else: self._all_ips = nacaddr.IPv4('0.0.0.0/0') self._action_table['reject'] = ('-j REJECT --reject-with ' 'icmp-host-prohibited') self.term_name = '%s_%s' % (self.filter[:1], self.term.name)
def testSafeToMerge(self): test_data = [(nacaddr.IPv4('10.0.0.0/10'), nacaddr.IPv4('10.0.0.0/8'), [nacaddr.IPv4('10.0.0.0/9')], False), (nacaddr.IPv4('192.168.0.0/27'), nacaddr.IPv4('192.168.0.0/24'), [nacaddr.IPv4('192.168.1.0/24')], True), (nacaddr.IPv6('10::/128'), nacaddr.IPv6('10::/56'), [nacaddr.IPv6('10::/64')], False), (nacaddr.IPv6('10::/128'), nacaddr.IPv6('10::/56'), [nacaddr.IPv6('8::/64')], True)] for address, merge_target, check_addresses, expected in test_data: self.assertEqual( nacaddr.SafeToMerge(address, merge_target, check_addresses), expected)
def setUp(self): self.addr1 = nacaddr.IPv4('10.0.0.0/8', 'The 10 block') self.addr2 = nacaddr.IPv6('DEAD:BEEF:BABE:FACE:DEAF:FEED:C0DE:F001/64', 'An IPv6 Address')
def __str__(self): # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if self._PLATFORM not in self.term.platform: return '' if self.term.platform_exclude: if self._PLATFORM in self.term.platform_exclude: return '' if self.enable_dsmo: raise NotImplementedError('enable_dsmo not implemented for msmpc') ret_str = juniper.Config(indent=self._DEFAULT_INDENT) # COMMENTS # this deals just fine with multi line comments, but we could probably # output them a little cleaner; do things like make sure the # len(output) < 80, etc. Note, if 'noverbose' is set for the filter, skip # all comment processing. if not self.noverbose: if self.term.owner: self.term.comment.append('Owner: %s' % self.term.owner) if self.term.comment: ret_str.Append('/*') for comment in self.term.comment: for line in comment.split('\n'): ret_str.Append('** ' + line) ret_str.Append('*/') # Term verbatim output - this will skip over normal term creation # code. Warning generated from policy.py if appropriate. if self.term.verbatim: for next_term in self.term.verbatim: if next_term[0] == self._PLATFORM: ret_str.Append(str(next_term[1]), verbatim=True) return str(ret_str) # Determine whether there are any match conditions for the term. has_match_criteria = ( self.term.address or self.term.dscp_except or self.term.dscp_match or self.term.destination_address or self.term.destination_port or self.term.destination_prefix or self.term.destination_prefix_except or self.term.encapsulate or self.term.ether_type or self.term.flexible_match_range or self.term.forwarding_class or self.term.forwarding_class_except or self.term.fragment_offset or self.term.hop_limit or self.term.next_ip or self.term.port or self.term.precedence or self.term.protocol or self.term.protocol_except or self.term.source_address or self.term.source_port or self.term.source_prefix or self.term.source_prefix_except or self.term.traffic_type or self.term.ttl) suffixes = [] duplicate_term = False has_icmp = 'icmp' in self.term.protocol has_icmpv6 = 'icmpv6' in self.term.protocol has_v4_ip = self.term.GetAddressOfVersion( 'source_address', self.AF_MAP.get('inet')) or self.term.GetAddressOfVersion( 'source_address_exclude', self.AF_MAP.get('inet')) or self.term.GetAddressOfVersion( 'destination_address', self.AF_MAP.get('inet')) or self.term.GetAddressOfVersion( 'destination_address_exclude', self.AF_MAP.get('inet')) has_v6_ip = self.term.GetAddressOfVersion( 'source_address', self.AF_MAP.get('inet6')) or self.term.GetAddressOfVersion( 'source_address_exclude', self.AF_MAP.get('inet6')) or self.term.GetAddressOfVersion( 'destination_address', self.AF_MAP.get('inet6')) or self.term.GetAddressOfVersion( 'destination_address_exclude', self.AF_MAP.get('inet6')) if self.term_type == 'mixed': if not (has_v4_ip or has_v6_ip): suffixes = ['inet'] elif not has_v6_ip: suffixes = ['inet'] elif not has_v4_ip: suffixes = ['inet6'] else: suffixes = ['inet', 'inet6'] duplicate_term = True if not suffixes and self.term_type in ['inet', 'inet6']: suffixes = [self.term_type] for suffix in suffixes: if self.term_type == 'mixed' and (not (has_icmp and has_icmpv6)) and ( has_v4_ip and has_v6_ip): if (has_icmp and suffix != 'inet') or (has_icmpv6 and suffix != 'inet6'): continue source_address = self.term.GetAddressOfVersion('source_address', self.AF_MAP.get(suffix)) source_address_exclude = self.term.GetAddressOfVersion( 'source_address_exclude', self.AF_MAP.get(suffix)) source_address, source_address_exclude = self._MinimizePrefixes( source_address, source_address_exclude) destination_address = self.term.GetAddressOfVersion( 'destination_address', self.AF_MAP.get(suffix)) destination_address_exclude = self.term.GetAddressOfVersion( 'destination_address_exclude', self.AF_MAP.get(suffix)) destination_address, destination_address_exclude = self._MinimizePrefixes( destination_address, destination_address_exclude) if ((not source_address) and self.term.GetAddressOfVersion( 'source_address', self.AF_MAP.get('mixed')) and not source_address_exclude) or ( (not destination_address) and self.term.GetAddressOfVersion( 'destination_address', self.AF_MAP.get('mixed')) and not destination_address_exclude): continue if ((has_icmpv6 and not has_icmp and suffix == 'inet') or (has_icmp and not has_icmpv6 and suffix == 'inet6')) and self.term_type != 'mixed': logging.debug( self.NO_AF_LOG_PROTO.substitute( term=self.term.name, proto=', '.join(self.term.protocol), af=suffix)) return '' # NAME # if the term is inactive we have to set the prefix if self.term.inactive: term_prefix = 'inactive:' else: term_prefix = '' ret_str.Append( '%s term %s%s {' % (term_prefix, self.term.name, '-' + suffix if duplicate_term else '')) # We only need a "from {" clause if there are any conditions to match. if has_match_criteria: ret_str.Append('from {') # SOURCE ADDRESS if source_address or source_address_exclude: ret_str.Append('source-address {') if source_address: for saddr in source_address: for comment in self._Comment(saddr): ret_str.Append('%s' % comment) if saddr.version == 6 and 0 < saddr.prefixlen < 16: for saddr2 in saddr.subnets(new_prefix=16): ret_str.Append('%s;' % saddr2) else: if saddr == nacaddr.IPv6('0::0/0'): saddr = 'any-ipv6' elif saddr == nacaddr.IPv4('0.0.0.0/0'): saddr = 'any-ipv4' ret_str.Append('%s;' % saddr) # SOURCE ADDRESS EXCLUDE if source_address_exclude: for ex in source_address_exclude: for comment in self._Comment(ex): ret_str.Append('%s' % comment) if ex.version == 6 and 0 < ex.prefixlen < 16: for ex2 in ex.subnets(new_prefix=16): ret_str.Append('%s except;' % ex2) else: if ex == nacaddr.IPv6('0::0/0'): ex = 'any-ipv6' elif ex == nacaddr.IPv4('0.0.0.0/0'): ex = 'any-ipv4' ret_str.Append('%s except;' % ex) ret_str.Append('}') # source-address {...} # DESTINATION ADDRESS if destination_address or destination_address_exclude: ret_str.Append('destination-address {') if destination_address: for daddr in destination_address: for comment in self._Comment(daddr): ret_str.Append('%s' % comment) if daddr.version == 6 and 0 < daddr.prefixlen < 16: for daddr2 in daddr.subnets(new_prefix=16): ret_str.Append('%s;' % daddr2) else: if daddr == nacaddr.IPv6('0::0/0'): daddr = 'any-ipv6' elif daddr == nacaddr.IPv4('0.0.0.0/0'): daddr = 'any-ipv4' ret_str.Append('%s;' % daddr) # DESTINATION ADDRESS EXCLUDE if destination_address_exclude: for ex in destination_address_exclude: for comment in self._Comment(ex): ret_str.Append('%s' % comment) if ex.version == 6 and 0 < ex.prefixlen < 16: for ex2 in ex.subnets(new_prefix=16): ret_str.Append('%s except;' % ex2) else: if ex == nacaddr.IPv6('0::0/0'): ex = 'any-ipv6' elif ex == nacaddr.IPv4('0.0.0.0/0'): ex = 'any-ipv4' ret_str.Append('%s except;' % ex) ret_str.Append('}') # destination-address {...} # source prefix <except> list if self.term.source_prefix or self.term.source_prefix_except: for pfx in self.term.source_prefix: ret_str.Append('source-prefix-list ' + pfx + ';') for epfx in self.term.source_prefix_except: ret_str.Append('source-prefix-list ' + epfx + ' except;') # destination prefix <except> list if self.term.destination_prefix or self.term.destination_prefix_except: for pfx in self.term.destination_prefix: ret_str.Append('destination-prefix-list ' + pfx + ';') for epfx in self.term.destination_prefix_except: ret_str.Append('destination-prefix-list ' + epfx + ' except;') # APPLICATION if (self.term.source_port or self.term.destination_port or self.term.icmp_type or self.term.protocol): if hasattr(self.term, 'replacement_application_name'): ret_str.Append('application-sets ' + self.term.replacement_application_name + '-app;') else: ret_str.Append('application-sets ' + self.filter_name[:((MAX_IDENTIFIER_LEN) // 2)] + self.term.name[-((MAX_IDENTIFIER_LEN) // 2):] + '-app;') ret_str.Append('}') # from {...} ret_str.Append('then {') # ACTION for action in self.term.action: ret_str.Append(self._ACTIONS.get(str(action)) + ';') if self.term.logging and 'disable' not in [ x.value for x in self.term.logging ]: ret_str.Append('syslog;') ret_str.Append('}') # then {...} ret_str.Append('}') # term {...} return str(ret_str)
class JuniperMSMPCTest(parameterized.TestCase): def setUp(self): super(JuniperMSMPCTest, self).setUp() self.naming = mock.create_autospec(naming.Naming) def testTermAndFilterName(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('10.0.0.0/8')] self.naming.GetServiceByProto.return_value = ['25'] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_1, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('term good-term-1 {', output, output) self.assertIn('rule test-filter {', output, output) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST') self.naming.GetServiceByProto.assert_called_once_with('SMTP', 'tcp') def testBadFilterType(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('10.0.0.0/8')] self.naming.GetServiceByProto.return_value = ['25'] pol = policy.ParsePolicy(BAD_HEADER_2 + GOOD_TERM_1, self.naming) self.assertRaises(junipermsmpc.UnsupportedHeaderError, junipermsmpc.JuniperMSMPC, pol, EXP_INFO) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST') self.naming.GetServiceByProto.assert_called_once_with('SMTP', 'tcp') def testMultipleFilterType(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('10.0.0.0/8')] self.naming.GetServiceByProto.return_value = ['25'] pol = policy.ParsePolicy(BAD_HEADER_3 + GOOD_TERM_1, self.naming) self.assertRaises(junipermsmpc.ConflictingTargetOptionsError, junipermsmpc.JuniperMSMPC, pol, EXP_INFO) def testMixedv4(self): self.naming.GetNetAddr.return_value = ([ nacaddr.IPv4('192.168.0.0/24') ]) self.naming.GetServiceByProto.return_value = ['25'] expected = (' term good-term-2 {\n' + ' from {\n' + ' destination-address {\n' + ' 192.168.0.0/24;\n' + ' }') msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER_MIXED + GOOD_TERM_1, self.naming), EXP_INFO) output = str(msmpc) self.assertIn(expected, output, output) def testMixedv6(self): self.naming.GetNetAddr.return_value = ([nacaddr.IPv6('2001::/33')]) self.naming.GetServiceByProto.return_value = ['25'] expected = (' term good-term-2 {\n' + ' from {\n' + ' destination-address {\n' + ' 2001::/33;\n' + ' }') msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER_MIXED + GOOD_TERM_1, self.naming), EXP_INFO) output = str(msmpc) self.assertIn(expected, output, output) def testMixedBoth(self): self.naming.GetNetAddr.return_value = ([ nacaddr.IPv4('192.168.0.0/24'), nacaddr.IPv6('2001::/33') ]) self.naming.GetServiceByProto.return_value = ['25'] expectedv4 = (' term good-term-2-inet {\n' + ' from {\n' + ' destination-address {\n' + ' 192.168.0.0/24;\n' + ' }') expectedv6 = (' term good-term-2-inet6 {\n' + ' from {\n' + ' destination-address {\n' + ' 2001::/33;\n' + ' }') msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER_MIXED + GOOD_TERM_1, self.naming), EXP_INFO) output = str(msmpc) self.assertIn(expectedv4, output, output) self.assertIn(expectedv6, output, output) def testCommentShrinking(self): long_comment = ' this is a very descriptive comment ' * 10 expected = (' ' * 32 + '/* this is a very descriptive comment this\n' + ' ' * 33 + '** is a very descriptive comment this is a\n' + ' ' * 33 + '** very descriptive comment this is a very\n' + ' ' * 33 + '** descript */') self.naming.GetNetAddr.return_value = ([ nacaddr.IPv4('10.0.0.0/8', comment=long_comment) ]) self.naming.GetServiceByProto.return_value = ['25'] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_1, self.naming), EXP_INFO) output = str(msmpc) self.assertIn(expected, output, output) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST') self.naming.GetServiceByProto.assert_called_once_with('SMTP', 'tcp') def testDefaultDeny(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + DEFAULT_TERM_1, self.naming), EXP_INFO) output = str(msmpc) self.assertNotIn('from {', output, output) self.assertIn('discard;', output, output) def testIcmpType(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_3, self.naming), EXP_INFO) output = str(msmpc) # verify proper translation from policy icmp-type text to juniper-esque self.assertIn('icmp-type 0;', output, output) self.assertIn('icmp-type 15;', output, output) self.assertIn('icmp-type 10;', output, output) self.assertIn('icmp-type 13;', output, output) self.assertIn('icmp-type 16;', output, output) def testIcmpCode(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_35, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('icmp-code [ 3 4 ];', output, output) def testInactiveTerm(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('10.0.0.0/8')] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_36, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('inactive: term good-term-36 {', output) def testInet6(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('2001::/33')] self.naming.GetServiceByProto.return_value = ['25'] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER_V6 + GOOD_TERM_1_V6, self.naming), EXP_INFO) output = str(msmpc) self.assertTrue( 'protocol icmp6;' in output and 'protocol tcp;' in output, output) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST') self.naming.GetServiceByProto.assert_called_once_with('SMTP', 'tcp') def testProtocolCase(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_5, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('protocol icmp;', output, output) self.assertIn('protocol tcp;', output, output) def testPrefixList(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_8, self.naming), EXP_INFO) spfx_re = re.compile(r'source-prefix-list foo_prefix_list;') dpfx_re = re.compile( r'destination-prefix-list bar_prefix_list;\W+destination-prefix-list baz_prefix_list;' ) output = str(msmpc) self.assertTrue(spfx_re.search(output), output) self.assertTrue(dpfx_re.search(output), output) def testPrefixListExcept(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_30, self.naming), EXP_INFO) spfx_re = re.compile(r'source-prefix-list foo_prefix_list except;') dpfx_re = re.compile( r'destination-prefix-list bar_prefix_list except;') output = str(msmpc) self.assertTrue(spfx_re.search(output), output) self.assertTrue(dpfx_re.search(output), output) def testPrefixListMixed(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_31, self.naming), EXP_INFO) spfx_re = re.compile(r'source-prefix-list foo_prefix;\W+' r'source-prefix-list foo_except except;') dpfx_re = re.compile(r'destination-prefix-list bar_prefix;\W+' r'destination-prefix-list bar_except except;') output = str(msmpc) self.assertTrue(spfx_re.search(output), output) self.assertTrue(dpfx_re.search(output), output) def testVerbatimTerm(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_11, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('mary had a little lamb', output, output) # check if other platforms verbatim shows up in output self.assertNotIn('mary had a second lamb', output, output) self.assertNotIn('mary had a third lamb', output, output) self.assertNotIn('mary had a fourth lamb', output, output) def testAccept(self): self.naming.GetServiceByProto.return_value = ['53'] policy_text = GOOD_HEADER + GOOD_TERM_25 msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(policy_text, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('then {', output, output) self.assertIn('accept;', output, output) self.naming.GetServiceByProto.assert_called_once_with('DNS', 'tcp') def testDiscardIPv4(self): self.naming.GetServiceByProto.return_value = ['53'] policy_text = GOOD_HEADER + GOOD_TERM_26 msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(policy_text, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('then {', output, output) self.assertIn('discard;', output, output) self.naming.GetServiceByProto.assert_called_once_with('DNS', 'tcp') def testDiscardIPv6(self): self.naming.GetServiceByProto.return_value = ['53'] policy_text = GOOD_HEADER_V6 + GOOD_TERM_26_V6 msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(policy_text, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('then {', output, output) self.assertIn('discard;', output, output) self.naming.GetServiceByProto.assert_called_once_with('DNS', 'tcp') def testRejectIPv6(self): self.naming.GetServiceByProto.return_value = ['53'] policy_text = GOOD_HEADER_V6 + GOOD_TERM_26_V6_REJECT msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(policy_text, self.naming), EXP_INFO) output = str(msmpc) self.assertIn('then {', output, output) self.assertIn('reject;', output, output) self.naming.GetServiceByProto.assert_called_once_with('DNS', 'tcp') def testTcpEstablished(self): self.naming.GetServiceByProto.return_value = ['53'] policy_text = GOOD_HEADER + ESTABLISHED_TERM_1 msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(policy_text, self.naming), EXP_INFO) output = str(msmpc) self.assertNotIn('term established-term-1', output, output) self.assertNotIn('tcp-established', output, output) self.naming.GetServiceByProto.assert_called_once_with('DNS', 'tcp') def testNoVerboseV4(self): addr_list = list() for octet in range(0, 256): net = nacaddr.IP('192.168.' + str(octet) + '.64/27') addr_list.append(net) self.naming.GetNetAddr.return_value = addr_list self.naming.GetServiceByProto.return_value = ['25'] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy( GOOD_NOVERBOSE_V4_HEADER + GOOD_TERM_1 + GOOD_TERM_COMMENT, self.naming), EXP_INFO) self.assertIn('192.168.0.64/27;', str(msmpc)) self.assertNotIn('COMMENT', str(msmpc)) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST') self.naming.GetServiceByProto.assert_called_once_with('SMTP', 'tcp') def testNoVerboseV6(self): addr_list = list() for octet in range(0, 256): net = nacaddr.IPv6('2001:db8:1010:' + str(octet) + '::64/64', strict=False) addr_list.append(net) self.naming.GetNetAddr.return_value = addr_list self.naming.GetServiceByProto.return_value = ['25'] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy( GOOD_NOVERBOSE_V6_HEADER + GOOD_TERM_1 + GOOD_TERM_COMMENT, self.naming), EXP_INFO) self.assertIn('2001:db8:1010:90::/61;', str(msmpc)) self.assertNotIn('COMMENT', str(msmpc)) self.naming.GetNetAddr.assert_called_once_with('SOME_HOST') self.naming.GetServiceByProto.assert_called_once_with('SMTP', 'tcp') def testTermTypeIndexKeys(self): # ensure an _INET entry for each _TERM_TYPE entry self.assertCountEqual(junipermsmpc.Term._TERM_TYPE.keys(), junipermsmpc.Term.AF_MAP.keys()) @mock.patch.object(junipermsmpc.logging, 'debug') def testIcmpv6InetMismatch(self, mock_debug): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + BAD_ICMPTYPE_TERM_1, self.naming), EXP_INFO) # output happens in __str_ str(msmpc) mock_debug.assert_called_once_with( 'Term icmptype-mismatch will not be rendered,' ' as it has icmpv6 match specified but ' 'the ACL is of inet address family.') @mock.patch.object(junipermsmpc.logging, 'debug') def testIcmpInet6Mismatch(self, mock_debug): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER_V6 + BAD_ICMPTYPE_TERM_2, self.naming), EXP_INFO) # output happens in __str_ str(msmpc) mock_debug.assert_called_once_with( 'Term icmptype-mismatch will not be rendered,' ' as it has icmp match specified but ' 'the ACL is of inet6 address family.') @mock.patch.object(junipermsmpc.logging, 'warning') def testExpiredTerm(self, mock_warn): _ = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + EXPIRED_TERM, self.naming), EXP_INFO) mock_warn.assert_called_once_with( 'WARNING: Term %s in policy %s is expired and will ' 'not be rendered.', 'is_expired', 'test-filter') @mock.patch.object(junipermsmpc.logging, 'info') def testExpiringTerm(self, mock_info): exp_date = datetime.date.today() + datetime.timedelta(weeks=EXP_INFO) _ = junipermsmpc.JuniperMSMPC( policy.ParsePolicy( GOOD_HEADER + EXPIRING_TERM % exp_date.strftime('%Y-%m-%d'), self.naming), EXP_INFO) mock_info.assert_called_once_with( 'INFO: Term %s in policy %s expires in ' 'less than two weeks.', 'is_expiring', 'test-filter') def testOwnerTerm(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_17, self.naming), EXP_INFO) output = str(msmpc) self.assertIn( ' /*\n' ' ** Owner: [email protected]\n' ' */', output, output) def testOwnerNoVerboseTerm(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_NOVERBOSE_V4_HEADER + GOOD_TERM_17, self.naming), EXP_INFO) output = str(msmpc) self.assertNotIn('** Owner: ', output, output) def testAddressExclude(self): big = nacaddr.IPv4('0.0.0.0/1', comment='half of everything') ip1 = nacaddr.IPv4('10.0.0.0/8', comment='RFC1918 10-net') ip2 = nacaddr.IPv4('172.16.0.0/12', comment='RFC1918 172-net') terms = (GOOD_TERM_18_SRC, GOOD_TERM_18_DST) self.naming.GetNetAddr.side_effect = [[big, ip1, ip2], [ip1] ] * len(terms) mock_calls = [] for term in terms: msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + term, self.naming), EXP_INFO) output = str(msmpc) expected_output = ( ' ' + ('source' if term == GOOD_TERM_18_SRC else 'destination') + '-address {\n' + ' /* half of everything, RFC1918 ' '10-net */\n' + ' 0.0.0.0/1;\n' + ' /* RFC1918 172-net */\n' + ' 172.16.0.0/12;\n' + ' /* RFC1918 10-net */\n' + ' 10.0.0.0/8 except;\n' + ' }') self.assertIn(expected_output, output, output) self.assertNotIn('10.0.0.0/8;', output, output) self.assertNotIn('172.16.0.0/12 except;', output, output) mock_calls.append(mock.call('INTERNAL')) mock_calls.append(mock.call('SOME_HOST')) self.naming.GetNetAddr.assert_has_calls(mock_calls) def testMinimizePrefixes(self): includes = ['1.0.0.0/8', '2.0.0.0/8'] excludes = ['1.1.1.1/32', '2.0.0.0/8', '3.3.3.3/32'] expected = ['1.0.0.0/8;', '1.1.1.1/32 except;'] unexpected = ['2.0.0.0/8;', '2.0.0.0/8 except;', '3.3.3.3/32'] self.naming.GetNetAddr.side_effect = [[ nacaddr.IPv4(ip) for ip in includes ], [nacaddr.IPv4(ip) for ip in excludes]] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_19, self.naming), EXP_INFO) output = str(msmpc) for result in expected: self.assertIn(result, output, 'expected "%s" in %s' % (result, output)) for result in unexpected: self.assertNotIn(result, output, 'unexpected "%s" in %s' % (result, output)) self.naming.GetNetAddr.assert_has_calls( [mock.call('INCLUDES'), mock.call('EXCLUDES')]) def testNoMatchReversal(self): includes = ['10.0.0.0/8', '10.0.0.0/10'] excludes = ['10.0.0.0/9'] expected = ['10.0.0.0/8;', '10.0.0.0/10;', '10.0.0.0/9 except;'] self.naming.GetNetAddr.side_effect = [[ nacaddr.IPv4(ip) for ip in includes ], [nacaddr.IPv4(ip) for ip in excludes]] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_19, self.naming), EXP_INFO) output = str(msmpc) for result in expected: self.assertIn(result, output) def testBuildTokens(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_35, self.naming), EXP_INFO) st, sst = msmpc._BuildTokens() self.assertSetEqual(st, SUPPORTED_TOKENS) self.assertDictEqual(sst, SUPPORTED_SUB_TOKENS) def testRangedPorts(self): self.naming.GetServiceByProto.side_effect = [['67'], ['68']] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + RANGE_PORTS_TERM, self.naming), EXP_INFO) self.assertIn('destination-port 67-68;', str(msmpc)) def testNotRangedPorts(self): self.naming.GetServiceByProto.side_effect = [['67'], ['69']] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + RANGE_PORTS_TERM, self.naming), EXP_INFO) self.assertNotIn('destination-port 67-68;', str(msmpc)) self.assertIn('destination-port 67;', str(msmpc)) self.assertIn('destination-port 69;', str(msmpc)) def testApplicationSets(self): self.naming.GetServiceByProto.side_effect = [['67'], ['69']] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + RANGE_PORTS_TERM, self.naming), EXP_INFO) expected = ( ' applications {\n' ' application test-filterranged-ports-1-app1 {\n' ' protocol udp;\n' ' destination-port 67;\n' ' }\n' ' application test-filterranged-ports-1-app2 {\n' ' protocol udp;\n' ' destination-port 69;\n' ' }\n' ' application-set test-filterranged-ports-1-app {\n' ' application test-filterranged-ports-1-app1;\n' ' application test-filterranged-ports-1-app2;\n' ' }\n' ' }\n') self.assertIn(expected, str(msmpc)) def testGroup(self): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + DEFAULT_TERM_1, self.naming), EXP_INFO) self.assertEqual('b;', msmpc._Group(['B'])) self.assertEqual('B;', msmpc._Group(['B'], lc=False)) self.assertEqual('b;', msmpc._Group(['B'], lc=True)) self.assertEqual('100;', msmpc._Group([100])) self.assertEqual('100-200;', msmpc._Group([(100, 200)])) self.assertEqual('[ b a ];', msmpc._Group(['b', 'A'])) self.assertEqual('[ 99 101-199 ];', msmpc._Group([99, (101, 199)])) self.assertEqual('[ 99 101-199 ];', msmpc._Group([99, (101, 199)])) @parameterized.named_parameters( ('MIXED_TO_V4', [[nacaddr.IPv4('0.0.0.0/1'), nacaddr.IPv6('2001::/33')], [nacaddr.IPv4('192.168.0.0/24')]], [ ' term good-term-inet {\n' + ' from {\n' + ' source-address {\n' + ' 0.0.0.0/1;\n' + ' }\n' + ' destination-address {\n' + ' 192.168.0.0/24;\n' + ' }' ], ['2001::/33']), ('V4_TO_MIXED', [ [nacaddr.IPv4('192.168.0.0/24')], [nacaddr.IPv4('0.0.0.0/1'), nacaddr.IPv6('2001::/33')], ], [ ' term good-term-inet {\n' + ' from {\n' + ' source-address {\n' + ' 192.168.0.0/24;\n' + ' }\n' + ' destination-address {\n' + ' 0.0.0.0/1;\n' + ' }' ], ['2001::/33']), ('MIXED_TO_V6', [[nacaddr.IPv4('0.0.0.0/1'), nacaddr.IPv6('2001::/33')], [nacaddr.IPv6('2201::/48')]], [ ' term good-term-inet6 {\n' + ' from {\n' + ' source-address {\n' + ' 2001::/33;\n' + ' }\n' + ' destination-address {\n' + ' 2201::/48;\n' + ' }' ], ['0.0.0.0/1']), ('V6_TO_MIXED', [[ nacaddr.IPv6('2201::/48') ], [nacaddr.IPv4('0.0.0.0/1'), nacaddr.IPv6('2001::/33')]], [ ' term good-term-inet6 {\n' + ' from {\n' + ' source-address {\n' + ' 2201::/48;\n' + ' }\n' + ' destination-address {\n' + ' 2001::/33;\n' + ' }' ], ['0.0.0.0/1']), ('MIXED_TO_MIXED', [[ nacaddr.IPv4('0.0.0.0/1'), nacaddr.IPv6('2001::/33') ], [nacaddr.IPv4('192.168.0.0/24'), nacaddr.IPv6('2201::/48')]], [ ' term good-term-inet {\n' + ' from {\n' + ' source-address {\n' + ' 0.0.0.0/1;\n' + ' }\n' + ' destination-address {\n' + ' 192.168.0.0/24;\n' + ' }', ' term good-term-inet6 {\n' + ' from {\n' + ' source-address {\n' + ' 2001::/33;\n' + ' }\n' + ' destination-address {\n' + ' 2201::/48;\n' + ' }' ], []), ('V4_TO_V4', [[nacaddr.IPv4('0.0.0.0/1')], [nacaddr.IPv4('192.168.0.0/24')]], [ ' term good-term {\n' + ' from {\n' + ' source-address {\n' + ' 0.0.0.0/1;\n' + ' }\n' + ' destination-address {\n' + ' 192.168.0.0/24;\n' + ' }' ], []), ('V6_TO_V6', [[nacaddr.IPv6('2001::/33')], [nacaddr.IPv6('2201::/48')]], [ ' term good-term {\n' + ' from {\n' + ' source-address {\n' + ' 2001::/33;\n' + ' }\n' + ' destination-address {\n' + ' 2201::/48;\n' + ' }' ], []), ( 'V4_TO_V6', [[nacaddr.IPv4('0.0.0.0/1')], [nacaddr.IPv6('2201::/48')]], [], ['0.0.0.0/1', '192.168.0.0/24', '2001::/33', '2201::/48'], ), ( 'V6_TO_V4', [[nacaddr.IPv6('2001::/33')], [nacaddr.IPv4('192.168.0.0/24')]], [], ['0.0.0.0/1', '192.168.0.0/24', '2001::/33', '2201::/48'], ), ) def testMixed(self, addresses, expected, notexpected): self.naming.GetNetAddr.side_effect = addresses self.naming.GetServiceByProto.return_value = ['25'] msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER_MIXED + MIXED_TESTING_TERM, self.naming), EXP_INFO) output = str(msmpc) for expect in expected: self.assertIn(expect, output, output) for notexpect in notexpected: self.assertNotIn(notexpect, output, output) @parameterized.named_parameters( ('true', 'true', True), ('True', 'True', True), ('syslog', 'syslog', True), ('local', 'local', True), ('disable', 'disable', False), ('log-both', 'log-both', True), ) def testLogging(self, option, want_logging): self.naming.GetNetAddr.return_value = [nacaddr.IPv4('192.168.0.0/24')] self.naming.GetServiceByProto.return_value = ['25'] expected_output = ( ' test-filter {\n' + ' services {\n' + ' stateful-firewall {\n' + ' rule test-filter {\n' + ' match-direction input-output;\n' + ' term good-term-1 {\n' + ' from {\n' + ' application-sets ' 'test-filtergood-term-1-app;\n' + ' }\n' + ' then {\n' + ' accept;\n' + ' syslog;\n' + ' }\n' + ' }\n' + ' }\n' + ' }\n' + ' }\n' + ' applications {\n' + ' application test-filtergood-term-1-app1 {\n' + ' protocol icmp;\n' + ' }\n' + ' application-set test-filtergood-term-1-app {\n' + ' application test-filtergood-term-1-app1;\n' + ' }\n' + ' }\n' + ' }\n' + '}\n' + 'apply-groups test-filter;') msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy( GOOD_HEADER_MIXED_IMPLICIT + (LOGGING_TERM % option), self.naming), EXP_INFO) output = str(msmpc) if want_logging: self.assertIn(expected_output, output, output) else: self.assertNotIn(expected_output, output, output) @parameterized.named_parameters(('default', GOOD_HEADER, 'input-output'), ('ingress', GOOD_HEADER_INGRESS, 'input'), ('egress', GOOD_HEADER_EGRESS, 'output')) def testDirection(self, header, direction): msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(header + GOOD_TERM_3, self.naming), EXP_INFO) output = str(msmpc) expected_output = (' rule test-filter {\n' + ' match-direction %s;') self.assertIn(expected_output % direction, output, output) def testBadDirectionCombo(self): pol = policy.ParsePolicy(BAD_HEADER_DIRECTION + GOOD_TERM_3, self.naming) self.assertRaises(junipermsmpc.ConflictingTargetOptionsError, junipermsmpc.JuniperMSMPC, pol, EXP_INFO) def testTermNameCollision(self): short_append = '1' * (junipermsmpc.MAX_IDENTIFIER_LEN // 2 - len('?ood-term-1')) long_append = short_append + '1' not_too_long_name = (TERM_NAME_COLLISION % (short_append, short_append)) too_long_name = (TERM_NAME_COLLISION % (long_append, long_append)) pol = policy.ParsePolicy(GOOD_HEADER + too_long_name, self.naming) self.assertRaises(junipermsmpc.ConflictingApplicationSetsError, junipermsmpc.JuniperMSMPC, pol, EXP_INFO) msmpc = junipermsmpc.JuniperMSMPC( policy.ParsePolicy(GOOD_HEADER + not_too_long_name, self.naming), EXP_INFO)
def setUp(self): super(NacaddrUnitTest, self).setUp() self.addr1 = nacaddr.IPv4(u'10.0.0.0/8', 'The 10 block') self.addr2 = nacaddr.IPv6('DEAD:BEEF:BABE:FACE:DEAF:FEED:C0DE:F001/64', 'An IPv6 Address', strict=False)
def testInet6Address(self): self.assertListEqual( self.defs.GetNetAddr('BAZ'), [nacaddr.IPv6('::FFFF:FFFF:FFFF:FFFF'), nacaddr.IPv6('::1/128')])
def ConvertToDict(self, priority_index): """Converts term to dict representation of SecurityPolicy.Rule JSON format. Takes all of the attributes associated with a term (match, action, etc) and converts them into a dictionary which most closely represents the SecurityPolicy.Rule JSON format. Args: priority_index: An integer priority value assigned to the term. Returns: A dict term. """ if self.skip: return {} rules = [] # Identify if this is inet6 processing for a term under a mixed policy. mixed_policy_inet6_term = False if self.policy_inet_version == 'mixed' and self.address_family == 'inet6': mixed_policy_inet6_term = True term_dict = { 'action': self.ACTION_MAP.get(self.term.action[0], self.term.action[0]), 'direction': self.term.direction, 'priority': priority_index } # Get the correct syntax for API versions. src_ip_range = ApiVersionSyntaxMap.SYNTAX_MAP[ self.api_version]['src_ip_range'] dest_ip_range = ApiVersionSyntaxMap.SYNTAX_MAP[ self.api_version]['dest_ip_range'] layer_4_config = ApiVersionSyntaxMap.SYNTAX_MAP[ self.api_version]['layer_4_config'] target_resources = [] for proj, vpc in self.term.target_resources: target_resources.append( self._TARGET_RESOURCE_FORMAT.format(proj, vpc)) if target_resources: # Only set when non-empty. term_dict['targetResources'] = target_resources term_dict['enableLogging'] = self._GetLoggingSetting() # This combo provides ability to identify the rule. term_name = self.term.name if mixed_policy_inet6_term: term_name = gcp.GetIpv6TermName(term_name) raw_description = term_name + ': ' + ' '.join(self.term.comment) term_dict['description'] = gcp.TruncateString( raw_description, self._MAX_TERM_COMMENT_LENGTH) filtered_protocols = [] for proto in self.term.protocol: # ICMP filtering by inet_version # Since each term has inet_version, 'mixed' is correctly processed here. if proto == 'icmp' and self.address_family == 'inet6': logging.warning( 'WARNING: Term %s is being rendered for inet6, ICMP ' 'protocol will not be rendered.', self.term.name) continue if proto == 'icmpv6' and self.address_family == 'inet': logging.warning( 'WARNING: Term %s is being rendered for inet, ICMPv6 ' 'protocol will not be rendered.', self.term.name) continue if proto == 'igmp' and self.address_family == 'inet6': logging.warning( 'WARNING: Term %s is being rendered for inet6, IGMP ' 'protocol will not be rendered.', self.term.name) continue filtered_protocols.append(proto) # If there is no protocol left after ICMP/IGMP filtering, drop this term. # But only do this for terms that originally had protocols. # Otherwise you end up dropping the default-deny. if self.term.protocol and not filtered_protocols: return {} protocols_and_ports = [] if not self.term.protocol: # Empty protocol list means any protocol, but any protocol in HF is # represented as "all" protocols_and_ports = [{'ipProtocol': 'all'}] else: for proto in filtered_protocols: # If the protocol name is not supported, use the protocol number. if proto not in self._ALLOW_PROTO_NAME: proto = str(self.PROTO_MAP[proto]) logging.info( 'INFO: Term %s is being rendered using protocol number', self.term.name) proto_ports = {'ipProtocol': proto} if self.term.destination_port: ports = self._GetPorts() if ports: # Only set when non-empty. proto_ports['ports'] = ports protocols_and_ports.append(proto_ports) if self.api_version == 'ga': term_dict['match'] = {layer_4_config: protocols_and_ports} else: term_dict['match'] = { 'config': { layer_4_config: protocols_and_ports } } # match needs a field called versionedExpr with value FIREWALL # See documentation: # https://cloud.google.com/compute/docs/reference/rest/beta/organizationSecurityPolicies/addRule term_dict['match']['versionedExpr'] = 'FIREWALL' ip_version = self.AF_MAP[self.address_family] if ip_version == 4: any_ip = [nacaddr.IP('0.0.0.0/0')] else: any_ip = [nacaddr.IPv6('::/0')] if self.term.direction == 'EGRESS': daddrs = self.term.GetAddressOfVersion('destination_address', ip_version) # If the address got filtered out and is empty due to address family, we # don't render the term. At this point of term processing, the direction # has already been validated, so we can just log and return empty rule. if self.term.destination_address and not daddrs: logging.warning( 'WARNING: Term %s is not being rendered for %s, ' 'because there are no addresses of that family.', self.term.name, self.address_family) return [] # This should only happen if there were no addresses set originally. if not daddrs: daddrs = any_ip destination_address_chunks = [ daddrs[x:x + self._TERM_ADDRESS_LIMIT] for x in range(0, len(daddrs), self._TERM_ADDRESS_LIMIT) ] for daddr_chunk in destination_address_chunks: rule = copy.deepcopy(term_dict) if self.api_version == 'ga': rule['match'][dest_ip_range] = [ daddr.with_prefixlen for daddr in daddr_chunk ] else: rule['match']['config'][dest_ip_range] = [ daddr.with_prefixlen for daddr in daddr_chunk ] rule['priority'] = priority_index rules.append(rule) priority_index += 1 else: saddrs = self.term.GetAddressOfVersion('source_address', ip_version) # If the address got filtered out and is empty due to address family, we # don't render the term. At this point of term processing, the direction # has already been validated, so we can just log and return empty rule. if self.term.source_address and not saddrs: logging.warning( 'WARNING: Term %s is not being rendered for %s, ' 'because there are no addresses of that family.', self.term.name, self.address_family) return [] # This should only happen if there were no addresses set originally. if not saddrs: saddrs = any_ip source_address_chunks = [ saddrs[x:x + self._TERM_ADDRESS_LIMIT] for x in range(0, len(saddrs), self._TERM_ADDRESS_LIMIT) ] for saddr_chunk in source_address_chunks: rule = copy.deepcopy(term_dict) if self.api_version == 'ga': rule['match'][src_ip_range] = [ saddr.with_prefixlen for saddr in saddr_chunk ] else: rule['match']['config'][src_ip_range] = [ saddr.with_prefixlen for saddr in saddr_chunk ] rule['priority'] = priority_index rules.append(rule) priority_index += 1 return rules
def ConvertToDict(self, priority_index): """Converts term to dict representation of SecurityPolicy.Rule JSON format. Takes all of the attributes associated with a term (match, action, etc) and converts them into a dictionary which most closely represents the SecurityPolicy.Rule JSON format. Args: priority_index: An integer priority value assigned to the term. Returns: A dict term. """ if self.skip: return {} rules = [] term_dict = { 'action': self.ACTION_MAP.get(self.term.action[0], self.term.action[0]), 'direction': self.term.direction, 'priority': priority_index } target_resources = [] for proj, vpc in self.term.target_resources: target_resources.append( self._TARGET_RESOURCE_FORMAT.format(proj, vpc)) if target_resources: # Only set when non-empty. term_dict['targetResources'] = target_resources term_dict['enableLogging'] = self._GetLoggingSetting() # This combo provides ability to identify the rule. raw_descirption = self.term.name + ': ' + ' '.join(self.term.comment) term_dict['description'] = gcp.TruncateString( raw_descirption, self._MAX_TERM_COMMENT_LENGTH) protocols_and_ports = [] if not self.term.protocol: # Empty protocol list means any protocol, but any protocol in HF is # represented as "all" protocols_and_ports = [{'ipProtocol': 'all'}] else: for proto in self.term.protocol: proto_ports = {'ipProtocol': proto} if self.term.destination_port: ports = self._GetPorts() if ports: # Only set when non-empty. proto_ports['ports'] = ports protocols_and_ports.append(proto_ports) term_dict['match'] = {'config': {'layer4Configs': protocols_and_ports}} # match needs a field called versionedExpr with value FIREWALL # See documentation: # https://cloud.google.com/compute/docs/reference/rest/beta/organizationSecurityPolicies/addRule term_dict['match']['versionedExpr'] = 'FIREWALL' ip_version = self.AF_MAP[self.address_family] if ip_version == 4: any_ip = [nacaddr.IP('0.0.0.0/0')] else: any_ip = [nacaddr.IPv6('::/0')] if self.term.direction == 'EGRESS': daddrs = self.term.GetAddressOfVersion('destination_address', ip_version) if not daddrs: daddrs = any_ip destination_address_chunks = [ daddrs[x:x + self._TERM_ADDRESS_LIMIT] for x in range(0, len(daddrs), self._TERM_ADDRESS_LIMIT) ] for daddr_chunk in destination_address_chunks: rule = copy.deepcopy(term_dict) rule['match']['config']['destIpRanges'] = [ daddr.with_prefixlen for daddr in daddr_chunk ] rule['priority'] = priority_index rules.append(rule) priority_index += 1 else: saddrs = self.term.GetAddressOfVersion('source_address', ip_version) if not saddrs: saddrs = any_ip source_address_chunks = [ saddrs[x:x + self._TERM_ADDRESS_LIMIT] for x in range(0, len(saddrs), self._TERM_ADDRESS_LIMIT) ] for saddr_chunk in source_address_chunks: rule = copy.deepcopy(term_dict) rule['match']['config']['srcIpRanges'] = [ saddr.with_prefixlen for saddr in saddr_chunk ] rule['priority'] = priority_index rules.append(rule) priority_index += 1 return rules