Exemple #1
0
 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)
Exemple #2
0
 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)
Exemple #3
0
  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)
Exemple #4
0
 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())
Exemple #5
0
    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]))
Exemple #6
0
    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')
Exemple #7
0
  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)
Exemple #8
0
    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)
Exemple #9
0
 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')])
Exemple #10
0
  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')
Exemple #11
0
  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')
Exemple #12
0
 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]))
Exemple #13
0
  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')
Exemple #14
0
 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)
Exemple #15
0
    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])
Exemple #16
0
    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)
Exemple #17
0
  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')
Exemple #18
0
    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")
Exemple #19
0
    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')
Exemple #20
0
    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)
Exemple #21
0
 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)
Exemple #22
0
 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')
Exemple #23
0
  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)
Exemple #24
0
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)
Exemple #25
0
 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)
Exemple #26
0
 def testInet6Address(self):
     self.assertListEqual(
         self.defs.GetNetAddr('BAZ'),
         [nacaddr.IPv6('::FFFF:FFFF:FFFF:FFFF'),
          nacaddr.IPv6('::1/128')])
Exemple #27
0
    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
Exemple #28
0
    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