def _from_xml(cls, address_book_elt):
        addrbook = cls()
        abe = address_book_elt
        addrbook.name = abe.find('name').text

        # address book
        for elt in abe:
            if elt.tag == 'name':
                addrbook.name = elt.text
            elif elt.tag == 'address':
                ip = IPSet([IP(elt.findtext('ip-prefix'))])
                name = elt.findtext('name')
                addrbook.addresses[name] = ip
            elif elt.tag == 'address-set':
                # note: assumes address-sets follow addresses
                ip = IPSet()
                for setaddr in elt.findall('address'):
                    setname = setaddr.findtext('name')
                    ip += addrbook.addresses[setname]
                name = elt.findtext('name')
                addrbook.addresses[name] = ip
            elif elt.tag == 'attach':
                attaches = []
                for z in elt:
                    attaches.append(z.findtext('name'))
                addrbook.attaches = attaches

        return addrbook
Example #2
0
def test_ippairs_sub_empty():
    ten = IPSet([IP('10.0.0.0/8')])
    twenty = IPSet([IP('20.0.0.0/8')])
    for pairs in [
            IPPairs(),
            IPPairs((ten, twenty)),
            IPPairs((ten, twenty), (twenty, ten)),
    ]:
        eq_(pairs - IPPairs(), pairs)
Example #3
0
def test_ipset_and_ip_list():
    eq_(
        ten24s & IPSet([
            IP('10.0.0.99'),
            IP('10.0.1.10'),
            IP('10.0.3.40'),
            IP('11.1.1.99'),
        ]), IPSet([
            IP('10.0.1.10'),
            IP('10.0.3.40'),
        ]))
def _parse_addrbook(addrbook):
    addresses = {}
    for addr in addrbook:
        name = addr.findtext('name')
        if addr.tag == 'address':
            ip = IPSet([IP(addr.findtext('ip-prefix'))])
        else:  # note: assumes address-sets follow addresses
            ip = IPSet()
            for setaddr in addr.findall('address'):
                setname = setaddr.findtext('name')
                ip += addresses[setname]
        addresses[name] = ip
    return addresses
def run(cfg, fwunit_cfg):
    address_spaces = {}
    for name, ip_space in cfg['address_spaces'].iteritems():
        if not isinstance(ip_space, list):
            ip_space = [ip_space]
        address_spaces[name] = IPSet([IP(s) for s in ip_space])

    # Add an "unmanaged" address space for any IP space not mentioned.
    managed_space = IPSet([])
    for sp in address_spaces.itervalues():
        managed_space += sp
    unmanaged_space = IPSet([IP('0.0.0.0/0')]) - managed_space
    if unmanaged_space:
        address_spaces['unmanaged'] = unmanaged_space

    # parse the routes configuration, expanding wildcards
    sources = dict()
    routes = {}
    for src in address_spaces:
        for dst in address_spaces:
            routes[src, dst] = set()
    for srcdest, rulesources in cfg['routes'].iteritems(): 
        if not isinstance(rulesources, list):
            rulesources = [rulesources]
        mo = re.match(r'(.*?) ?(<?)-> ?(.*)', srcdest)
        if not mo:
            raise RuntimeError("invalid route name {:r}".format(srcdest))
        srcs, bidir, dsts = mo.groups()
        # expand wildcards
        srcs = address_spaces.keys() if srcs == '*' else [srcs]
        dsts = address_spaces.keys() if dsts == '*' else [dsts]
        for src in srcs:
            if src not in address_spaces:
                raise RuntimeError('Unknown address space %s' % src)
            for dst in dsts:
                if dst not in address_spaces:
                    raise RuntimeError('Unknown address space %s' % dst)
                rt = routes[src, dst]
                for rs in rulesources:
                    rt.add(rs)
                    sources[rs] = None
                if bidir:
                    rt = routes[dst, src]
                    for rs in rulesources:
                        rt.add(rs)

    # load the rules for all of the rule sources referenced
    for rs in sources:
        sources[rs] = get_rules(fwunit_cfg, rs)

    return process.combine(address_spaces, routes, sources)
def test_simplify_combine_iterations():
    """A set of rules that requires a few passes to simplify is fully simplified"""
    rules = {
        'testapp': [
            r('10.0.0.0', IPSet([IP('20.0.0.0')])),
            r('10.0.0.0', IPSet([IP('30.0.0.0')])),
            r('11.0.0.0', IPSet([IP('20.0.0.0'),
                                 IP('30.0.0.0')])),
            r('12.0.0.0', IPSet([IP('20.0.0.0'),
                                 IP('30.0.0.0')])),
            r(IPSet([IP('10.0.0.0'),
                     IP('11.0.0.0'),
                     IP('12.0.0.0')]), IPSet([IP('30.0.0.0'),
                                              IP('40.0.0.0')])),
        ]
    }
    exp = {
        'testapp': [
            r(IPSet([IP('10.0.0.0'),
                     IP('11.0.0.0'),
                     IP('12.0.0.0')]),
              IPSet([IP('20.0.0.0'),
                     IP('30.0.0.0'),
                     IP('40.0.0.0')])),
        ]
    }
    eq_(simplify_rules(rules), exp)
Example #7
0
def process_address_sets_per_policy(zones, policies_by_zone_pair,
                                    addrbooks_per_zone, global_addrbook):
    logger.info("computing address sets per policy")
    src_per_policy = {}
    dst_per_policy = {}
    for (from_zone, to_zone), zpolicies in policies_by_zone_pair.iteritems():
        from_addrbook = addrbooks_per_zone.get(from_zone, global_addrbook)
        get_from = lambda a: a if isinstance(a, IPSet) else from_addrbook[a]
        to_addrbook = addrbooks_per_zone.get(to_zone, global_addrbook)
        get_to = lambda a: a if isinstance(a, IPSet) else to_addrbook[a]
        for pol in zpolicies:
            src_per_policy[pol] = sum(
                (get_from(a) for a in pol.source_addresses), IPSet())
            dst_per_policy[pol] = sum(
                (get_to(a) for a in pol.destination_addresses), IPSet())
    return src_per_policy, dst_per_policy
Example #8
0
def test_parse_addrbook_attached():
    f = FakeSRX()
    ab = f.add_addrbook('general-stuff')
    f.add_address(ab, 'trustedhost', '10.0.9.2/32')
    f.add_attach(ab, 'untrust')
    f.add_attach(ab, 'trust')

    elt = parse_xml(f.fake_show('configuration security address-book'),
                    './/address-book')
    z = parse.AddressBook._from_xml(elt)
    eq_(z.attaches, ['untrust', 'trust'])
    eq_(sorted(z.addresses.keys()),
        sorted(['any', 'any-ipv4', 'any-ipv6', 'trustedhost']))
    eq_(z.addresses['any'], IPSet([IP('0.0.0.0/0')]))
    eq_(z.addresses['any-ipv4'], IPSet([IP('0.0.0.0/0')]))
    eq_(z.addresses['any-ipv6'], IPSet([]))
    eq_(z.addresses['trustedhost'], IPSet([IP('10.0.9.2')]))
Example #9
0
def test_parse_zone():
    f = FakeSRX()
    z = f.add_zone('untrust')
    f.add_address(z, 'host1', '9.0.9.1/32')
    f.add_address(z, 'host2', '9.0.9.2/32')
    f.add_address(z, 'puppet', '9.0.9.3/32')
    f.add_address_set(z, 'hosts', 'host1', 'host2')
    f.add_interface(z, 'reth0')

    elt = parse_xml(f.fake_show('configuration security zones'),
                    './/security-zone')
    z = parse.Zone._from_xml(elt)
    eq_(z.interfaces, ['reth0'])
    eq_(
        sorted(z.addresses.keys()),
        sorted([
            'any', 'any-ipv4', 'any-ipv6', 'host1', 'host2', 'hosts', 'puppet'
        ]))
    eq_(z.addresses['any'], IPSet([IP('0.0.0.0/0')]))
    eq_(z.addresses['any-ipv4'], IPSet([IP('0.0.0.0/0')]))
    eq_(z.addresses['any-ipv6'], IPSet([]))
    eq_(z.addresses['host1'], IPSet([IP('9.0.9.1')]))
    eq_(z.addresses['host2'], IPSet([IP('9.0.9.2')]))
    eq_(z.addresses['puppet'], IPSet([IP('9.0.9.3')]))
    eq_(z.addresses['hosts'], IPSet([IP('9.0.9.1'), IP('9.0.9.2')]))
Example #10
0
def test_ippairs():
    any = IPSet([IP('0.0.0.0/0')])
    ten = IPSet([IP('10.0.0.0/8')])
    not_ten = any - ten
    ten26 = IPSet([IP('10.26.0.0/16')])
    not_ten26 = any - ten26
    ten33 = IPSet([IP('10.33.0.0/16')])
    eq_(IPPairs((any, any)) - IPPairs((any, ten)), IPPairs((any, not_ten)))
    eq_(
        IPPairs((any, any)) - IPPairs((any, ten)) - IPPairs((any, ten26)),
        IPPairs((any, not_ten)))
    eq_(
        IPPairs((any, any)) - IPPairs((any, ten)) - IPPairs((ten26, any)),
        IPPairs((not_ten26, not_ten)))
    eq_(IPPairs((any, ten26 + ten33)) - IPPairs((any, ten)), IPPairs())
    eq_(
        IPPairs((ten, ten)) - IPPairs((ten26, ten26)),
        IPPairs((ten, ten - ten26), (ten - ten26, ten26)))
Example #11
0
def process_interface_ips(routes):
    # figure out the IPSet routed via each interface, by starting with the most
    # specific and only considering IP space not already allocated to an
    # interface.  This has the effect of leaving a "swiss cheese" default route
    # containing all IPs that aren't routed by a more-specific route.
    logger.info("calculating interface IP ranges")
    routes = routes[:]
    routes.sort(key=lambda r: -r.destination.prefixlen())
    matched = IPSet()
    interface_ips = {}
    for r in routes:
        destset = IPSet([r.destination])
        if r.interface and not r.reject:
            interface_ips[r.interface] = interface_ips.get(
                r.interface, IPSet()) + (destset - matched)
        # consider the route matched even if it didn't have an
        # interface or is a blackhole
        matched = matched + destset
    return interface_ips
def test_simplify_combine():
    """Rules with the same source are combined"""
    rules = {
        'testapp': [
            r('10.0.0.0', '20.0.0.0'),
            r('20.0.0.0', '30.0.0.0'),
            r('10.0.0.0', '40.0.0.0'),
        ]
    }
    exp = {
        'testapp': [
            r('10.0.0.0', IPSet([IP('20.0.0.0'),
                                 IP('40.0.0.0')])),
            r('20.0.0.0', '30.0.0.0'),
        ]
    }
    eq_(simplify_rules(rules), exp)
Example #13
0
def process_zone_nets(zones, interface_ips):
    # figure out the IPSet of IPs for each security zone.  This makes the
    # assumption (just like RFP) that each IP will communicate on exactly one
    # firewall interface.  Each interface is in exactly one zone, so this means
    # that each IP is in exactly one zone.
    logger.info("calculating zone IP ranges")
    zone_nets = {}
    for zone in zones:
        net = IPSet()
        for itfc in zone.interfaces:
            try:
                net += interface_ips[itfc]
            except KeyError:
                # if the interface doesn't have any attached subnet, continue on
                # (this can happen for backup interfaces, for example)
                pass
        zone_nets[zone.name] = net
    return zone_nets
Example #14
0
 def make_rules(sgid, local):
     sg = security_groups[sgid]
     for dir, sgrules in [('in', sg.rules), ('out', sg.rules_egress)]:
         for sgrule in sgrules:
             if sgrule.app == '*/any':
                 apps = all_apps | set(['@@other'])
             else:
                 apps = [sgrule.app]
             for app in apps:
                 for grant in sgrule.grants:
                     if grant.cidr_ip:
                         remote = IPSet([IP(grant.cidr_ip)])
                     else:
                         remote = ips_by_sg.get(grant.group_id, None)
                         if not remote:
                             continue
                     src, dst = (remote, local) if dir == 'in' else (local,
                                                                     remote)
                     name = "{}/{}".format(sg.name, dir)
                     # first make rules involving non-managed space, leaving
                     # only managed-to-managed
                     if dir == 'in':
                         unmanaged_src = src & unmanaged_ip_space
                         if unmanaged_src:
                             rules.setdefault(app, []).append(
                                 Rule(src=unmanaged_src,
                                      dst=dst,
                                      app=app,
                                      name=name))
                         src = src & managed_ip_space
                     else:
                         unmanaged_dst = dst & unmanaged_ip_space
                         if unmanaged_dst:
                             rules.setdefault(app, []).append(
                                 Rule(src=src,
                                      dst=unmanaged_dst,
                                      app=app,
                                      name=name))
                         dst = dst & managed_ip_space
                     if src and dst:
                         to_intersect.setdefault(app, {}).setdefault(
                             dir, []).append((src, dst, name))
 def sourcesFor(self, dst, app, ignore_sources=None):
     dst = _ipset(dst)
     log.info("sourcesFor(%s, %r, ignore_sources=%s)" %
              (dst, app, ignore_sources))
     rv = IPSet()
     for rule in self.rulesForApp(app):
         if rule.dst & dst:
             src = rule.src
             if ignore_sources:
                 src = src - ignore_sources
             if src:
                 log.info(
                     "matched policy {t.cyan}{name}{t.normal}\n{t.yellow}{src}{t.normal} "
                     "-> {t.magenta}{dst}{t.normal}".format(t=terminal,
                                                            name=rule.name,
                                                            src=src,
                                                            dst=rule.dst
                                                            & dst))
                 rv = rv + src
     return rv
Example #16
0
def test_parse_addrbook_global():
    f = FakeSRX()
    ab = f.add_addrbook('global')
    f.add_address(ab, 'host1', '9.0.9.1/32')
    f.add_address(ab, 'host2', '9.0.9.2/32')
    f.add_address_set(ab, 'hosts', 'host1', 'host2')

    elt = parse_xml(f.fake_show('configuration security address-book'),
                    './/address-book')
    z = parse.AddressBook._from_xml(elt)
    eq_(z.attaches, [])
    eq_(sorted(z.addresses.keys()),
        sorted(['any', 'any-ipv4', 'any-ipv6', 'host1', 'host2', 'hosts']))
    eq_(z.addresses['any'], IPSet([IP('0.0.0.0/0')]))
    eq_(z.addresses['any-ipv4'], IPSet([IP('0.0.0.0/0')]))
    eq_(z.addresses['any-ipv6'], IPSet([]))
    eq_(z.addresses['host1'], IPSet([IP('9.0.9.1')]))
    eq_(z.addresses['host2'], IPSet([IP('9.0.9.2')]))
    eq_(z.addresses['hosts'], IPSet([IP('9.0.9.1'), IP('9.0.9.2')]))
Example #17
0
def test_parse_zone_no_sets():
    f = FakeSRX()
    z = f.add_zone('untrust')
    f.add_address(z, 'trustedhost', '10.0.9.2/32')
    f.add_address(z, 'dmz', '10.1.0.0/16')
    f.add_address(z, 'shadow', '10.1.99.99/32')
    f.add_interface(z, 'reth1')

    elt = parse_xml(f.fake_show('configuration security zones'),
                    './/security-zone')
    z = parse.Zone._from_xml(elt)
    eq_(z.interfaces, ['reth1'])
    eq_(
        sorted(z.addresses.keys()),
        sorted(['any', 'any-ipv4', 'any-ipv6', 'trustedhost', 'dmz',
                'shadow']))
    eq_(z.addresses['any'], IPSet([IP('0.0.0.0/0')]))
    eq_(z.addresses['any-ipv4'], IPSet([IP('0.0.0.0/0')]))
    eq_(z.addresses['any-ipv6'], IPSet([]))
    eq_(z.addresses['trustedhost'], IPSet([IP('10.0.9.2')]))
    eq_(z.addresses['dmz'], IPSet([IP('10.1.0.0/16')]))
    eq_(z.addresses['shadow'], IPSet([IP('10.1.99.99')]))
                ('web2', '172.16.1.2', ['admin_ssh', 'webapp']),
            ]),
            ('dynamic', '172.16.2.0/24', [
                ('dynhost', '172.16.2.17', ['admin_ssh']),
                ('dynhost', '172.16.2.20', ['admin_ssh']),
            ]),
            ('perhost', '172.16.3.0/24', [
                ('server4', '172.16.3.1', ['admin_ssh']),
            ]),
        ]),
    ]),
]

RULES = {
    'ssh': [
        Rule(src=IPSet([IP('172.16.1.0/24'), IP('172.16.3.0/24')]) - IPSet([IP('172.16.1.1'), IP('172.16.1.2'), IP('172.16.3.1')]),
             dst=IPSet([IP('0.0.0.0/0')]) - IPSet([IP('172.16.1.0/24'), IP('172.16.2.0/24'), IP('172.16.3.0/24')]),
             app='ssh', name='unoccupied/out'),
        Rule(src=IPSet([IP('192.168.10.0/24')]),
             dst=IPSet([IP('172.16.1.1'), IP('172.16.1.2'), IP('172.16.2.0/24'), IP('172.16.3.1')]),
             app='ssh', name='admin_ssh/in'),
    ],
    'web': [
        Rule(src=IPSet([IP('172.16.1.0/24'), IP('172.16.3.0/24')]) - IPSet([IP('172.16.1.1'), IP('172.16.1.2'), IP('172.16.3.1')]),
             dst=IPSet([IP('0.0.0.0/0')]) - IPSet([IP('172.16.1.0/24'), IP('172.16.2.0/24'), IP('172.16.3.0/24')])
                 + IPSet([IP('172.16.1.1'), IP('172.16.1.2')]),
             app='web', name='unoccupied/out+webapp/in'),
        Rule(src=IPSet([IP('0.0.0.0/0')]) - IPSet([IP('172.16.1.0/24'), IP('172.16.2.0/24'), IP('172.16.3.0/24')]),
             dst=IPSet([IP('172.16.1.1'), IP('172.16.1.2')]),
             app='web', name='webapp/in'),
    ],
Example #19
0
            IP('1.1.1.1'),
            IP('1.1.1.3'),
            IP('1.1.2.0/24'),
        ]).isdisjoint(IPSet([
            IP('1.1.2.2'),
            IP('1.1.1.4'),
        ])))


def test_ipset_and_single_ip():
    eq_(ten24s & IPSet([IP('10.0.1.10')]), IPSet([IP('10.0.1.10')]))


ten24s = IPSet([
    IP('10.0.1.0/24'),
    IP('10.0.3.0/24'),
    IP('10.0.5.0/24'),
    IP('10.0.7.0/24'),
])


def test_ipset_and_ip_list():
    eq_(
        ten24s & IPSet([
            IP('10.0.0.99'),
            IP('10.0.1.10'),
            IP('10.0.3.40'),
            IP('11.1.1.99'),
        ]), IPSet([
            IP('10.0.1.10'),
            IP('10.0.3.40'),
        ]))
Example #20
0
def process_attached_networks(routes):
    # return a list of networks to which this firewall is directly connected,
    # so there is no "next hop".
    logger.info("calculating attached networks")
    networks = [IPSet([r.destination]) for r in routes if r.is_local]
    return networks
Example #21
0
def test_ipset_isdisjoint():
    a = IP('128.0.0.0/16')
    b = IP('129.0.0.0/16')
    c = IP('130.0.0.0/16')
    assert_true(IPSet([a, b]).isdisjoint(IPSet([c])))
    assert_false(IPSet([a, b]).isdisjoint(IPSet([b, c])))
    assert_false(IPSet([a]).isdisjoint(IPSet([a, b])))
    assert_false(IPSet([a]).isdisjoint(IPSet([a, c])))
    assert_false(IPSet([a, b]).isdisjoint(IPSet([b, c])))

    assert_true(
        IPSet([IP('0.0.0.0/1')]).isdisjoint(IPSet([IP('128.0.0.0/1')])))
    assert_false(IPSet([IP('0.0.0.0/1')]).isdisjoint(IPSet([IP('0.0.0.0/2')])))
    assert_false(IPSet([IP('0.0.0.0/2')]).isdisjoint(IPSet([IP('0.0.0.0/1')])))
    assert_false(IPSet([IP('0.0.0.0/2')]).isdisjoint(IPSet([IP('0.1.2.3')])))
    assert_false(IPSet([IP('0.1.2.3')]).isdisjoint(IPSet([IP('0.0.0.0/2')])))
    assert_true(
        IPSet([IP('1.1.1.1'),
               IP('1.1.1.3')]).isdisjoint(IPSet([IP('1.1.1.2'),
                                                 IP('1.1.1.4')])))
    assert_false(
        IPSet([
            IP('1.1.1.1'),
            IP('1.1.1.3'),
            IP('1.1.2.0/24'),
        ]).isdisjoint(IPSet([
            IP('1.1.2.2'),
            IP('1.1.1.4'),
        ])))
Example #22
0
def test_ipset_and_single_ip():
    eq_(ten24s & IPSet([IP('10.0.1.10')]), IPSet([IP('10.0.1.10')]))
Example #23
0
def test_ipset_and_larger_net():
    eq_(ten24s & IPSet([IP('10.0.0.0/22')]),
        IPSet([IP('10.0.1.0/24'), IP('10.0.3.0/24')]))
def ipset_from_jsonable(ipset, _cache={}):
    ipset = tuple(ipset)
    if ipset not in _cache:
        _cache[ipset] = rv = IPSet([IP(pfx) for pfx in ipset])
        return rv
    return _cache[ipset]
def _ipset(ip):
    if isinstance(ip, basestring):
        ip = IP(ip)
    if isinstance(ip, IP):
        ip = IPSet([ip])
    return ip
Example #26
0
def test_run_no_globals():
    fake_cfg = {
        'firewall': 'fw',
        'ssh_username': '******',
        'ssh_password': '******',
        'application-map': {
            'junos-ssh': 'ssh',
            'junos-ping': 'ping'
        },
    }
    z = F.add_zone('untrust')
    F.add_address(z, 'host1', '9.0.9.1/32')
    F.add_address(z, 'host2', '9.0.9.2/32')
    F.add_address(z, 'puppet', '9.0.9.2/32')
    F.add_address_set(z, 'hosts', 'host1', 'host2')
    F.add_interface(z, 'reth0')

    z = F.add_zone('trust')
    F.add_address(z, 'trustedhost', '10.0.9.2/32')
    F.add_address(z, 'dmz', '10.1.0.0/16')
    F.add_address(z, 'shadow', '10.1.99.99/32')
    F.add_interface(z, 'reth1')

    F.add_policy(('trust', 'trust'),
                 dict(sequence=1,
                      name='ssh',
                      src='any',
                      dst='any',
                      app='junos-ssh',
                      action='permit'))
    F.add_policy(('trust', 'trust'),
                 dict(sequence=10,
                      name='deny',
                      src='any',
                      dst='any',
                      app='any',
                      action='deny'))
    F.add_policy(('trust', 'untrust'),
                 dict(sequence=1,
                      name='ssh',
                      src='any',
                      dst='any',
                      app='junos-ssh',
                      action='permit'))
    F.add_policy(('trust', 'untrust'),
                 dict(sequence=2,
                      name='puppet',
                      src='any',
                      dst='puppet',
                      app='puppet',
                      action='permit'))
    F.add_policy(('trust', 'untrust'),
                 dict(sequence=10,
                      name='deny',
                      src='any',
                      dst='any',
                      app='any',
                      action='deny'))
    F.add_policy(('untrust', 'trust'),
                 dict(sequence=1,
                      name='no-shadow-ping',
                      src='any',
                      dst='shadow',
                      app='junos-ping',
                      action='deny'))
    F.add_policy(('untrust', 'trust'),
                 dict(sequence=2,
                      name='ping',
                      src='any',
                      dst='dmz',
                      app='junos-ping',
                      action='permit'))
    F.add_policy(('untrust', 'trust'),
                 dict(sequence=3,
                      name='admin-puppet',
                      src='puppet',
                      dst='trustedhost',
                      app='junos-ssh',
                      action='permit'))
    F.add_policy(('untrust', 'trust'),
                 dict(sequence=4,
                      name='admin',
                      src='any-ipv6',
                      dst='trustedhost',
                      app='junos-ssh',
                      action='permit'))
    F.add_policy(('untrust', 'trust'),
                 dict(sequence=10,
                      name='deny',
                      src='any',
                      dst='any',
                      app='any',
                      action='deny'))
    F.add_policy(('untrust', 'untrust'),
                 dict(sequence=10,
                      name='deny',
                      src='any',
                      dst='any',
                      app='any',
                      action='deny'))

    rules = scripts.run(fake_cfg, {})
    for r in rules.itervalues():
        r.sort()
    exp = {
        'ping': [
            Rule(src=IPSet([IP('0.0.0.0/0')]) - IPSet([IP('10.0.0.0/8')]),
                 dst=IPSet([IP('10.1.0.0/16')]) - IPSet([IP('10.1.99.99')]),
                 app='ping',
                 name='ping'),
        ],
        'puppet': [
            Rule(src=IPSet([IP('10.0.0.0/8')]),
                 dst=IPSet([IP('9.0.9.2')]),
                 app='puppet',
                 name='puppet'),
        ],
        'ssh': [
            Rule(src=IPSet([IP('9.0.9.2')]),
                 dst=IPSet([IP('10.0.9.2')]),
                 app='ssh',
                 name='admin-puppet'),
            Rule(src=IPSet([IP('10.0.0.0/8')]),
                 dst=IPSet([IP('0.0.0.0/0')]),
                 app='ssh',
                 name='ssh'),
        ],
    }
    eq_(rules, exp)
Example #27
0
def test_run_global_policies_and_addrbook():
    fake_cfg = {
        'firewall': 'fw',
        'ssh_username': '******',
        'ssh_password': '******',
        'application-map': {
            'junos-ssh': 'ssh',
            'junos-ping': 'ping'
        },
    }

    z = F.add_zone('untrust')
    F.add_address(z, 'untrust-host', '9.0.9.1/32')
    F.add_interface(z, 'reth0')

    z = F.add_zone('trust')
    F.add_address(z, 'trust-host', '10.9.1.1/32')
    F.add_interface(z, 'reth1')

    ab = F.add_addrbook('global')
    F.add_address(ab, 'global-host', '10.1.1.1/32')

    ab = F.add_addrbook('public')
    F.add_address(ab, 'public-host', '2.2.1.1/32')
    F.add_attach(ab, 'trust')
    F.add_attach(ab, 'untrust')

    F.add_policy(('trust', 'untrust'),
                 dict(sequence=1,
                      name='ssh',
                      src='trust-host',
                      dst='untrust-host',
                      app='junos-ssh',
                      action='permit'))
    F.add_policy(('untrust', 'trust'),
                 dict(sequence=1,
                      name='puppet',
                      src='public-host',
                      dst='global-host',
                      app='puppet',
                      action='permit'))
    F.add_policy(
        'global',
        dict(sequence=10,
             name='ping',
             src='any',
             dst='any',
             app='junos-ping',
             action='permit'))
    F.add_policy(
        'global',
        dict(sequence=11,
             name='deny',
             src='any',
             dst='any',
             app='any',
             action='deny'))

    rules = scripts.run(fake_cfg, {})
    for r in rules.itervalues():
        r.sort()
    exp = {
        'ping': [
            Rule(src=IPSet([IP('0.0.0.0/0')]),
                 dst=IPSet([IP('0.0.0.0/0')]),
                 app='ping',
                 name='ping'),
        ],
        'puppet': [
            Rule(src=IPSet([IP('2.2.1.1')]),
                 dst=IPSet([IP('10.1.1.1')]),
                 app='puppet',
                 name='puppet'),
        ],
        'ssh': [
            Rule(src=IPSet([IP('10.9.1.1')]),
                 dst=IPSet([IP('9.0.9.1')]),
                 app='ssh',
                 name='ssh'),
        ],
    }
    eq_(rules, exp)
Example #28
0
def get_rules(aws, app_map, regions, dynamic_subnets):
    if not regions:
        logger.info("Getting all regions")
        regions = aws.all_regions()

    logger.info("collecting subnets")
    subnets = []
    managed_ip_space = IPSet([])
    for id, subnet in aws.get_all_subnets(regions).iteritems():
        name = subnet.tags.get('Name', id)
        dynamic = name in dynamic_subnets or id in dynamic_subnets
        cidr_block = IP(subnet.cidr_block)
        subnet = Subnet(cidr_block=cidr_block, name=name, dynamic=dynamic)
        subnets.append(subnet)
        managed_ip_space = managed_ip_space + IPSet([cidr_block])
    unmanaged_ip_space = IPSet([IP('0.0.0.0/0')]) - managed_ip_space

    logger.info("collecting dynamic subnet IP ranges")
    dynamic_ipsets = {}
    per_host_subnet_ips = IPSet()
    for subnet in subnets:
        if subnet.dynamic:
            ipset = dynamic_ipsets.get(subnet.name, IPSet([]))
            ipset += IPSet([subnet.cidr_block])
            dynamic_ipsets[subnet.name] = ipset
        else:
            per_host_subnet_ips += IPSet([subnet.cidr_block])

    # sort by IP subnet, so we can use a binary search
    logger.info("sorting subnets by IP")
    subnets.sort(key=lambda s: s.cidr_block)
    _subnet_blocks = [s.cidr_block for s in subnets]

    def subnet_by_ip(ip):
        i = bisect.bisect_right(_subnet_blocks, ip)
        if i and ip in _subnet_blocks[i - 1]:
            return subnets[i - 1]

    logger.info("examining instances")
    sgids_by_dynamic_subnet = {}  # {subnet name: set of SecurityGroupIds}
    sgids_by_instance = {}  # {instance_name: [ip, set of SecurityGroupIds]}
    all_sgids = set()
    ips_by_sg = {}  # {group id: IPSet}
    for id, instance in aws.get_all_instances(regions).iteritems():
        if instance.state == 'terminated' or instance.state == 'shutting-down':
            continue  # meh, who cares
        if not instance.vpc_id:
            continue  # not in vpc; ignored
        if not instance.private_ip_address:
            logger.debug(
                "ignoring instance with no private_ip_address: %s, tags %r",
                instance.id, instance.tags)
            continue
        ip = IP(instance.private_ip_address)

        for g in instance.groups:
            ips_by_sg[g.id] = ips_by_sg.get(g.id, IPSet([])) + IPSet([IP(ip)])

        subnet = subnet_by_ip(ip)
        if not subnet:
            logger.debug(
                "ignoring instance with no matching subnet for %s: %s, tags %r",
                ip, instance.id, instance.tags)
            continue

        if subnet.dynamic:
            sgset = sgids_by_dynamic_subnet.setdefault(subnet.name, set())
        else:
            inst_name = instance.tags.get('Name', instance.id)
            if inst_name in sgids_by_instance:
                inst_name = inst_name + ' ({})'.format(instance.id)
            sgset = set()
            sgids_by_instance[inst_name] = [ip, sgset]
        new_sgids = set(
            SecurityGroupId(g.id, instance.region.name)
            for g in instance.groups)
        sgset.update(new_sgids)
        all_sgids.update(new_sgids)

    logger.info("accumulating security groups")
    all_apps = set(app_map.values())
    security_groups = {}
    for sgid in all_sgids:
        sg = security_groups[sgid] = aws.get_security_group(sgid)
        assert sg, "no security group with id {}".format(sgid)
        # pre-process all of the rules' apps now
        for sgrule in itertools.chain(sg.rules, sg.rules_egress):
            proto = str(sgrule.ip_protocol)
            if proto == '-1':
                proto = 'any'
            if sgrule.from_port == sgrule.to_port:
                if str(sgrule.from_port) in ("None", "-1"):
                    app = "*/{}".format(proto)
                else:
                    app = '{}/{}'.format(sgrule.from_port, proto)
            else:
                app = '{}-{}/{}'.format(sgrule.from_port, sgrule.to_port,
                                        proto)
            app = app_map[app]
            sgrule.app = app
            all_apps.add(app)

    rules = {}
    to_intersect = {}

    def make_rules(sgid, local):
        sg = security_groups[sgid]
        for dir, sgrules in [('in', sg.rules), ('out', sg.rules_egress)]:
            for sgrule in sgrules:
                if sgrule.app == '*/any':
                    apps = all_apps | set(['@@other'])
                else:
                    apps = [sgrule.app]
                for app in apps:
                    for grant in sgrule.grants:
                        if grant.cidr_ip:
                            remote = IPSet([IP(grant.cidr_ip)])
                        else:
                            remote = ips_by_sg.get(grant.group_id, None)
                            if not remote:
                                continue
                        src, dst = (remote, local) if dir == 'in' else (local,
                                                                        remote)
                        name = "{}/{}".format(sg.name, dir)
                        # first make rules involving non-managed space, leaving
                        # only managed-to-managed
                        if dir == 'in':
                            unmanaged_src = src & unmanaged_ip_space
                            if unmanaged_src:
                                rules.setdefault(app, []).append(
                                    Rule(src=unmanaged_src,
                                         dst=dst,
                                         app=app,
                                         name=name))
                            src = src & managed_ip_space
                        else:
                            unmanaged_dst = dst & unmanaged_ip_space
                            if unmanaged_dst:
                                rules.setdefault(app, []).append(
                                    Rule(src=src,
                                         dst=unmanaged_dst,
                                         app=app,
                                         name=name))
                            dst = dst & managed_ip_space
                        if src and dst:
                            to_intersect.setdefault(app, {}).setdefault(
                                dir, []).append((src, dst, name))

    logger.info("writing rules for dynamic subnets")
    for subnet_name, sgids in sgids_by_dynamic_subnet.iteritems():
        subnet = dynamic_ipsets[subnet_name]
        logger.debug(" subnet %s, %s", subnet_name, subnet)
        for sgid in sgids:
            make_rules(sgid, subnet)

    logger.info("writing rules for instances in per-host subnets")
    per_host_host_ips = IPSet()
    for inst_name, info in sgids_by_instance.iteritems():
        ip, sgids = info
        logger.debug(" instance %s at %s", inst_name, ip)
        host_ip = IPSet([ip])
        per_host_host_ips += host_ip
        for sgid in sgids:
            make_rules(sgid, host_ip)

    logger.info(
        "assuming unrestricted outbound access from unoccupied IPs in per-host subnets"
    )
    unoccupied = per_host_subnet_ips - per_host_host_ips
    for app in all_apps:
        rules.setdefault(app, []).append(
            Rule(src=unoccupied,
                 dst=unmanaged_ip_space,
                 app=app,
                 name='unoccupied/out'))
        to_intersect.setdefault(app, {}).setdefault('out', []).append(
            (unoccupied, managed_ip_space, 'unoccupied/out'))

    # traffic within the manage Ip space is governed both by outbound rules on
    # the source and inbound rules on the destination.
    logger.info("intersecting inbound and outbound rules")
    for app, dirs in to_intersect.iteritems():
        in_rules = dirs.get('in', [])
        out_rules = dirs.get('out', [])
        logger.debug("..for %s", app)
        new_rules = []
        for inr in in_rules:
            for outr in out_rules:
                src = inr[0] & outr[0]
                if not src:
                    continue
                dst = inr[1] & outr[1]
                if not dst:
                    continue
                new_rules.append(
                    Rule(src=src,
                         dst=dst,
                         app=app,
                         name=combine_names(inr[2], outr[2])))
        # simplify now, within this app, to save space and time
        new_rules = simplify_rules({app: new_rules})[app]
        rules.setdefault(app, []).extend(new_rules)

    rules = simplify_rules(rules)
    return rules
def _parse_addrbook(addrbook):
    addresses = {}
    for addr in addrbook:
        name = addr.findtext('name')
        if addr.tag == 'address':
            ip = IPSet([IP(addr.findtext('ip-prefix'))])
        else:  # note: assumes address-sets follow addresses
            ip = IPSet()
            for setaddr in addr.findall('address'):
                setname = setaddr.findtext('name')
                ip += addresses[setname]
        addresses[name] = ip
    return addresses

_default_addresses = {
    'any': IPSet([IP('0.0.0.0/0')]),
    'any-ipv4': IPSet([IP('0.0.0.0/0')]),
    # fwunit doesn't handle ipv6, so this is an empty set
    'any-ipv6': IPSet([]),
}

class Zone(object):

    """Parse out zone names and the corresponding interfaces"""

    def __init__(self):
        #: list of interface names
        self.interfaces = []

        #: name -> ipset, based on the zone's address book
        self.addresses = _default_addresses.copy()
Example #30
0
def ipset(*ips):
    """Create an IPSet out of the given strings"""
    return IPSet(map(IP, ips))