Esempio n. 1
0
    def test_negative_fields(self):
        """
        Test negative fields
        """
        lines = [ "-N felix-from-blah",
                  "-A felix-from-blah ! -d 1.2.3.4/32 -j DROP",
                  "-A felix-from-blah ! -d 1.2.3.4/32 -p tcp -j DROP",
                  "-A felix-from-blah ! -d 1.2.3.4/32 ! -p udp -j DROP",
                  "-A felix-from-blah -m mark ! --mark 0x1 -j DROP"]

        data = "\n".join(lines)

        state = fiptables.TableState()
        state.read_table = mock.Mock(return_value = data)
        table = state.load_table(IPV4, "filter")
        chain = table.chains["felix-from-blah"]

        # Check that lot got parsed as expected.
        self.assertTrue(len(chain.rules), 4)

        rule = fiptables.Rule(IPV4)
        rule.dst = "!1.2.3.4/32"
        rule.target = "DROP"
        self.assertEqual(chain.rules[0], rule)

        rule.protocol = "tcp"
        self.assertEqual(chain.rules[1], rule)

        rule.protocol = "!udp"
        self.assertEqual(chain.rules[2], rule)

        rule = fiptables.Rule(IPV4)
        rule.match = "mark"
        rule.parameters["mark"] = "!0x1"
        rule.target = "DROP"
        self.assertEqual(chain.rules[3], rule)

        # Now convert back again, and compare. Lines are different here in that
        # we have (a) removed the chain specification; and (b) reordered
        # fields.
        lines = [ "! -d 1.2.3.4/32 -j DROP",
                  "! -d 1.2.3.4/32 -p tcp -j DROP",
                  "! -d 1.2.3.4/32 ! -p udp -j DROP",
                  "-m mark -j DROP ! --mark 0x1"]

        for loop in range(0,4):
            actual = " ".join(chain.rules[loop].generate_fields())
            self.assertEqual(actual, lines[loop])
Esempio n. 2
0
    def test_load_table(self):
        state = fiptables.TableState()

        data = "\n".join(["-P INPUT ACCEPT\n",
                          "-P FORWARD ACCEPT\n",
                          "-P OUTPUT ACCEPT\n",
                          "-N felix-FORWARD\n",
                          "-N felix-FROM-ENDPOINT\n",
                          "-N felix-INPUT\n",
                          "-N felix-TO-ENDPOINT\n",
                          "-N felix-from-19f8308f-81\n",
                          "-N felix-from-e6d6a9a9-37\n",
                          "-N felix-to-19f8308f-81\n",
                          "-N felix-to-e6d6a9a9-37\n",
                          "-A INPUT -j felix-INPUT\n",
                          "-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT\n",
                          "-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT\n",
                          "-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT\n",
                          "-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT\n",
                          "-A INPUT -j nova-api-INPUT\n",
                          "-A FORWARD -j felix-FORWARD\n",
                          "-A FORWARD -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT\n",
                          "-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT\n",
                          "-A FORWARD -i virbr0 -o virbr0 -j ACCEPT\n",
                          "-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable\n",
                          "-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable\n",
                          "-A FORWARD -j nova-filter-top\n",
                          "-A FORWARD -j nova-api-FORWARD\n"])
        state.read_table = mock.Mock(return_value = data)
        table = state.load_table(IPV4, "filter")

        # Check that the state contains what we expect.
        self.assertEqual(len(table.chains), 11)

        # Check a rules is present as expected.
        rule = fiptables.Rule(IPV4, "ACCEPT")
        rule.protocol = "udp"
        rule.in_interface = "virbr0"
        rule.match = "udp"
        rule.parameters["dport"] = "53"

        self.assertEqual(str(table.chains["INPUT"].rules[1]),
                         "-p udp -i virbr0 -m udp -j ACCEPT --dport 53")
        self.assertEqual(table.chains["INPUT"].rules[1], rule)
Esempio n. 3
0
    def test_load_table_rule_not_known(self):
        """
        Test that loading a rule which we cannot understand is still valid.
        """
        state = fiptables.TableState()

        data = "\n".join(["-P INPUT ACCEPT\n",
                          "-A INPUT -i eth0 -p tcp --some-thing x y -j DROP"])

        state.read_table = mock.Mock(return_value = data)
        table = state.load_table(IPV4, "filter")

        self.assertEqual(str(table.chains["INPUT"].rules[0]),
                         "-p tcp -i eth0 -j DROP --some-thing x y")

        rule = fiptables.Rule(IPV4, "DROP")
        rule.protocol = "tcp"
        rule.in_interface = "eth0"
        rule.parameters["some-thing"] = "x y"

        self.assertEqual(rule, table.chains["INPUT"].rules[0])
Esempio n. 4
0
def set_global_rules(config):
    """
    Set up global iptables rules. These are rules that do not change with
    endpoint, and are expected never to change - but they must be present.
    """
    # The IPV4 nat table first. This must have a felix-PREROUTING chain.
    table = fiptables.get_table(futils.IPV4, "nat")
    chain = fiptables.get_chain(table, CHAIN_PREROUTING)

    if config.METADATA_IP is None:
        # No metadata IP. The chain should be empty - if not, clean it out.
        chain.flush()
    else:
        # Now set the chain to have a single rule by adding it at the start,
        # then truncating. The rule looks like this.
        #  DNAT tcp -- any any anywhere 169.254.169.254 tcp dpt:http to:127.0.0.1:9697
        rule          = fiptables.Rule(futils.IPV4)
        rule.dst      = "169.254.169.254"
        rule.protocol = "tcp"
        rule.create_target("DNAT", {"to_destination": 
                                    "%s:%s" % (config.METADATA_IP,
                                               config.METADATA_PORT)})

        rule.create_tcp_match("80")
        fiptables.insert_rule(rule, chain, 0)
        truncate_rules(chain, 1)

    # Add a rule that forces us through the chain we just created.
    chain = fiptables.get_chain(table, "PREROUTING")
    rule = fiptables.Rule(futils.IPV4, CHAIN_PREROUTING)
    fiptables.insert_rule(rule, chain, force_position=False)

    #*************************************************************************#
    #* Now the filter table. This needs to have calico-filter-FORWARD and    *#
    #* calico-filter-INPUT chains, which we must create before adding any    *#
    #* rules that send to them.                                              *#
    #*************************************************************************#
    for type in (IPV4, IPV6):
        table = fiptables.get_table(type, "filter")
        fiptables.get_chain(table, CHAIN_FROM_ENDPOINT)
        fiptables.get_chain(table, CHAIN_TO_ENDPOINT)
        fiptables.get_chain(table, CHAIN_INPUT)
        fiptables.get_chain(table, CHAIN_FORWARD)

        # Add rules that force us through the main Felix chain.
        chain = fiptables.get_chain(table, "FORWARD")
        rule  = fiptables.Rule(type, CHAIN_FORWARD)
        fiptables.insert_rule(rule, chain, force_position=False)

        chain = fiptables.get_chain(table, "INPUT")
        rule  = fiptables.Rule(type, CHAIN_INPUT)
        fiptables.insert_rule(rule, chain, force_position=False)

        # The felix forward chain tests traffic to and from endpoints
        chain = fiptables.get_chain(table, CHAIN_FORWARD)
        rule  = fiptables.Rule(type, CHAIN_FROM_ENDPOINT)
        rule.in_interface = "tap+"
        fiptables.insert_rule(rule, chain, 0)

        rule  = fiptables.Rule(type, CHAIN_TO_ENDPOINT)
        rule.out_interface = "tap+"
        fiptables.insert_rule(rule, chain, 1)

        rule  = fiptables.Rule(type, "ACCEPT")
        rule.in_interface = "tap+"
        fiptables.insert_rule(rule, chain, 2)

        rule  = fiptables.Rule(type, "ACCEPT")
        rule.out_interface = "tap+"
        fiptables.insert_rule(rule, chain, 3)

        truncate_rules(chain, 4)

        # The felix INPUT chain tests traffic from endpoints
        chain = fiptables.get_chain(table, CHAIN_INPUT)
        rule  = fiptables.Rule(type, CHAIN_FROM_ENDPOINT)
        rule.in_interface = "tap+"
        fiptables.insert_rule(rule, chain, 0)

        rule  = fiptables.Rule(type, "ACCEPT")
        rule.in_interface = "tap+"
        fiptables.insert_rule(rule, chain, 1)

        truncate_rules(chain, 2)
Esempio n. 5
0
def set_ep_specific_rules(suffix, iface, type, localips, mac):
    """
    Add (or modify) the rules for a particular endpoint, whose suffix is
    supplied. This routine :
    - ensures that the chains specific to this endpoint exist, where there is
      a chain for packets leaving and a chain for packets arriving at the
      endpoint;
    - routes packets to / from the tap interface to the chains created above;
    - fills out the endpoint specific chains with the correct rules;
    - verifies that the ipsets exist.

    The net of all this is that every bit of iptables configuration that is
    specific to this particular endpoint is created (or verified), with the
    exception of ACLs (i.e. the configuration of the list of other addresses
    for which routing is permitted) - this is done in set_acls.
    Note however that this routine handles IPv4 or IPv6 not both; it is
    normally called twice in succession (once for each).
    """
    to_chain_name   = CHAIN_TO_PREFIX + suffix
    from_chain_name = CHAIN_FROM_PREFIX + suffix

    # Set up all the ipsets.
    if type == IPV4:
        to_ipset_port   = IPSET_TO_PORT_PREFIX + suffix
        to_ipset_addr   = IPSET_TO_ADDR_PREFIX + suffix
        to_ipset_icmp   = IPSET_TO_ICMP_PREFIX + suffix
        from_ipset_port = IPSET_FROM_PORT_PREFIX + suffix
        from_ipset_addr = IPSET_FROM_ADDR_PREFIX + suffix
        from_ipset_icmp = IPSET_FROM_ICMP_PREFIX + suffix
        family          = "inet"
    else:
        to_ipset_port   = IPSET6_TO_PORT_PREFIX + suffix
        to_ipset_addr   = IPSET6_TO_ADDR_PREFIX + suffix
        to_ipset_icmp   = IPSET6_TO_ICMP_PREFIX + suffix
        from_ipset_port = IPSET6_FROM_PORT_PREFIX + suffix
        from_ipset_addr = IPSET6_FROM_ADDR_PREFIX + suffix
        from_ipset_icmp = IPSET6_FROM_ICMP_PREFIX + suffix
        family            = "inet6"

    # Create ipsets if they do not already exist.
    ipsets.create(to_ipset_port, "hash:net,port", family)
    ipsets.create(to_ipset_addr, "hash:net", family)
    ipsets.create(to_ipset_icmp, "hash:net", family)
    ipsets.create(from_ipset_port, "hash:net,port", family)
    ipsets.create(from_ipset_addr, "hash:net", family)
    ipsets.create(from_ipset_icmp, "hash:net", family)

    # Get the table.
    table = fiptables.get_table(type, "filter")

    # Create the chains for packets to the interface
    to_chain = fiptables.get_chain(table, to_chain_name)

    #*************************************************************************#
    #* Put rules into that "from" chain, i.e. the chain traversed by         *#
    #* outbound packets. Note that we never ACCEPT, but always RETURN if we  *#
    #* want to accept this packet. This is because the rules here are for    *#
    #* this endpoint only - we cannot (for example) ACCEPT a packet which    *#
    #* would be rejected by the "to" rules for another endpoint to which it  *#
    #* is addressed which happens to exist on the same host.                 *#
    #*************************************************************************#
    index = 0

    if type == IPV6:
        #************************************************************************#
        #* In ipv6 only, there are 6 rules that need to be created first.       *#
        #* RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 130                 *#
        #* RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 131                 *#
        #* RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 132                 *#
        #* RETURN ipv6-icmp anywhere anywhere ipv6-icmp router-advertisement    *#
        #* RETURN ipv6-icmp anywhere anywhere ipv6-icmp neighbour-solicitation  *#
        #* RETURN ipv6-icmp anywhere anywhere ipv6-icmp neighbour-advertisement *#
        #*                                                                      *#
        #* These rules are ICMP types 130, 131, 132, 134, 135 and 136, and can  *#
        #* be created on the command line with something like :                 *#
        #*    ip6tables -A plw -j RETURN --protocol icmpv6 --icmpv6-type 130    *#
        #************************************************************************#
        for icmp in ["130", "131", "132", "134", "135", "136"]:
            rule = fiptables.Rule(futils.IPV6, "RETURN")
            rule.protocol = "icmpv6"
            rule.create_icmp6_match([icmp])
            fiptables.insert_rule(rule, to_chain, index)
            index += 1

    rule = fiptables.Rule(type, "DROP")
    rule.create_conntrack_match(["INVALID"])
    fiptables.insert_rule(rule, to_chain, index)
    index += 1

    # "Return if state RELATED or ESTABLISHED".
    rule = fiptables.Rule(type, "RETURN")
    rule.create_conntrack_match(["RELATED,ESTABLISHED"])
    fiptables.insert_rule(rule, to_chain, index)
    index += 1

    # "Return anything whose source matches this ipset" (for three ipsets)
    rule = fiptables.Rule(type, "RETURN")
    rule.create_set_match([to_ipset_port, "src,dst"])
    fiptables.insert_rule(rule, to_chain, index)
    index += 1

    rule = fiptables.Rule(type, "RETURN")
    rule.create_set_match([to_ipset_addr, "src"])
    fiptables.insert_rule(rule, to_chain, index)
    index += 1

    rule = fiptables.Rule(type, "RETURN")
    if type is IPV4:
        rule.protocol = "icmp"
    else:
        rule.protocol = "icmpv6"
    rule.create_set_match([to_ipset_icmp, "src"])
    fiptables.insert_rule(rule, to_chain, index)
    index += 1

    # If we get here, drop the packet.
    rule = fiptables.Rule(type, "DROP")
    fiptables.insert_rule(rule, to_chain, index)
    index += 1

    #*************************************************************************#
    #* Delete all rules from here to the end of the chain, in case there     *#
    #* were rules present which should not have been.                        *#
    #*************************************************************************#
    truncate_rules(to_chain, index)

    #*************************************************************************#
    #* Now the chain that manages packets from the interface, and the rules  *#
    #* in that chain.                                                        *#
    #*************************************************************************#
    from_chain = fiptables.get_chain(table, from_chain_name)

    index = 0
    if type == IPV6:
        # In ipv6 only, allows all ICMP traffic from this endpoint to anywhere.
        rule = fiptables.Rule(type, "RETURN")
        rule.protocol = "icmpv6"
        fiptables.insert_rule(rule, from_chain, index)
        index += 1

    # "Drop if state INVALID".
    rule = fiptables.Rule(type, "DROP")
    rule.create_conntrack_match(["INVALID"])
    fiptables.insert_rule(rule, from_chain, index)
    index += 1

    # "Return if state RELATED or ESTABLISHED".
    rule = fiptables.Rule(type, "RETURN")
    rule.create_conntrack_match(["RELATED,ESTABLISHED"])
    fiptables.insert_rule(rule, from_chain, index)
    index += 1

    if type == IPV4:
        # Allow outgoing v4 DHCP packets.
        rule = fiptables.Rule(type, "RETURN")
        rule.protocol = "udp"
        rule.create_udp_match("68", "67")
        fiptables.insert_rule(rule, from_chain, index)
        index += 1
    else:
        # Allow outgoing v6 DHCP packets.
        rule = fiptables.Rule(type, "RETURN")
        rule.protocol = "udp"
        rule.create_udp_match("546", "547")
        fiptables.insert_rule(rule, from_chain, index)
        index += 1

    #*************************************************************************#
    #* Now only allow through packets from the correct MAC and IP address.   *#
    #* We do this by first setting a mark if it matches any of the IPs, then *#
    #* dropping the packets if that mark is not set.  There may be rules     *#
    #* here from addresses that this endpoint no longer has - in which case  *#
    #* we insert before them and they get tidied up when we truncate the     *#
    #* chain.                                                                *#
    #*************************************************************************#
    for ip in localips:
        rule = fiptables.Rule(type)
        rule.create_target("MARK", {"set_mark": "1"})
        rule.src = ip
        rule.create_mac_match(mac)
        fiptables.insert_rule(rule, from_chain, index)
        index += 1

    rule = fiptables.Rule(type, "DROP")
    rule.create_mark_match("!1")
    fiptables.insert_rule(rule, from_chain, index)
    index += 1
  
    # "Permit packets whose destination matches the supplied ipsets."
    rule = fiptables.Rule(type, "RETURN")
    rule.create_set_match([from_ipset_port, "dst,dst"])
    fiptables.insert_rule(rule, from_chain, index)
    index += 1

    rule = fiptables.Rule(type, "RETURN")
    rule.create_set_match([from_ipset_addr, "dst"])
    fiptables.insert_rule(rule, from_chain, index)
    index += 1

    rule = fiptables.Rule(type, "RETURN")
    if type is IPV4:
        rule.protocol = "icmp"
    else:
        rule.protocol = "icmpv6"
    rule.create_set_match([from_ipset_icmp, "dst"])
    fiptables.insert_rule(rule, from_chain, index)
    index += 1

    # If we get here, drop the packet.
    rule = fiptables.Rule(type, "DROP")
    fiptables.insert_rule(rule, from_chain, index)
    index += 1

    #*************************************************************************#
    #* Delete all rules from here to the end of the chain, in case there     *#
    #* were rules present which should not have been.                        *#
    #*************************************************************************#
    truncate_rules(from_chain, index)

    #*************************************************************************#
    #* We have created the chains and rules that control input and output    *#
    #* for the interface but not routed traffic through them. First a rule   *#
    #* for traffic arriving to the endpoint.                                 *#
    #*************************************************************************#
    chain = fiptables.get_chain(table, CHAIN_FROM_ENDPOINT)

    rule = fiptables.Rule(type, from_chain_name)
    rule.in_interface = iface
    fiptables.insert_rule(rule,
                          chain,
                          fiptables.RULE_POSN_LAST,
                          force_position=False)

    #*************************************************************************#
    #* Similarly, create the rules that direct packets that are forwarded    *#
    #* either to or from the endpoint, sending them to the "to" or "from"    *#
    #* chains as appropriate.                                                *#
    #*************************************************************************#
    chain = fiptables.get_chain(table, CHAIN_TO_ENDPOINT)

    rule = fiptables.Rule(type, to_chain_name)
    rule.out_interface = iface
    fiptables.insert_rule(rule,
                          chain,
                          fiptables.RULE_POSN_LAST,
                          force_position=False)
Esempio n. 6
0
def set_global_rules(config, iface_prefix, iptables_state):
    """
    Set up global iptables rules. These are rules that do not change with
    endpoint, and are expected never to change (such as the rules that send all
    traffic through the top level Felix chains).

    This method therefore :

    - resets the table state (i.e. clears the cache in case any of the tables
      that are not Felix-owned have changed since the last read);
    - ensures that all the required global tables are present;
    - applies any changes required.

    This method should be called at start of day and periodically thereafter
    (so that we can periodically discard and reload the cache).
    """
    # Reset all the tables; we are about to recheck the global rules, so this
    # is the best place to clean out and resync our state.
    iptables_state.reset()

    # The interface matching string; for example, if interfaces start "tap"
    # then this string is "tap+".
    iface_match = iface_prefix + "+"

    # The IPV4 nat table first. This must have a felix-PREROUTING chain.
    table = iptables_state.get_table(futils.IPV4, "nat")
    chain = table.get_chain(CHAIN_PREROUTING)

    if config.METADATA_IP is None:
        # No metadata IP. The chain should be empty - if not, clean it out.
        chain.flush()
    else:
        # Now set the chain to have a single rule by adding it at the start,
        # then truncating. The rule looks like this.
        #  DNAT tcp -- any any anywhere 169.254.169.254 tcp dpt:http to:127.0.0.1:8775
        rule = fiptables.Rule(futils.IPV4)
        rule.dst = "169.254.169.254/32"
        rule.protocol = "tcp"
        rule.create_target("DNAT", {
            "to-destination":
            "%s:%s" % (config.METADATA_IP, config.METADATA_PORT)
        })

        rule.create_tcp_match("80")
        chain.insert_rule(rule, 0)
        chain.truncate_rules(1)

    # Add a rule that forces us through the chain we just created.
    chain = table.get_chain("PREROUTING")
    rule = fiptables.Rule(futils.IPV4, CHAIN_PREROUTING)
    chain.insert_rule(rule, force_position=False)

    #*************************************************************************#
    #* Now the filter table. This needs to have calico-filter-FORWARD and    *#
    #* calico-filter-INPUT chains, which we must create before adding any    *#
    #* rules that send to them.                                              *#
    #*************************************************************************#
    for type in (IPV4, IPV6):
        table = iptables_state.get_table(type, "filter")
        table.get_chain(CHAIN_FROM_ENDPOINT)
        table.get_chain(CHAIN_TO_ENDPOINT)
        table.get_chain(CHAIN_INPUT)
        table.get_chain(CHAIN_FORWARD)

        # Add rules that force us through the main Felix chain.
        chain = table.get_chain("FORWARD")
        rule = fiptables.Rule(type, CHAIN_FORWARD)
        chain.insert_rule(rule, force_position=False)

        chain = table.get_chain("INPUT")
        rule = fiptables.Rule(type, CHAIN_INPUT)
        chain.insert_rule(rule, force_position=False)

        # The felix forward chain tests traffic to and from endpoints
        chain = table.get_chain(CHAIN_FORWARD)
        rule = fiptables.Rule(type, CHAIN_FROM_ENDPOINT)
        rule.in_interface = iface_match
        chain.insert_rule(rule, 0)

        rule = fiptables.Rule(type, CHAIN_TO_ENDPOINT)
        rule.out_interface = iface_match
        chain.insert_rule(rule, 1)

        rule = fiptables.Rule(type, "ACCEPT")
        rule.in_interface = iface_match
        chain.insert_rule(rule, 2)

        rule = fiptables.Rule(type, "ACCEPT")
        rule.out_interface = iface_match
        chain.insert_rule(rule, 3)

        chain.truncate_rules(4)

        # The felix INPUT chain tests traffic from endpoints
        chain = table.get_chain(CHAIN_INPUT)
        rule = fiptables.Rule(type, CHAIN_FROM_ENDPOINT)
        rule.in_interface = iface_match
        chain.insert_rule(rule, 0)

        rule = fiptables.Rule(type, "ACCEPT")
        rule.in_interface = iface_match
        chain.insert_rule(rule, 1)

        chain.truncate_rules(2)

    # Apply all those changes.
    iptables_state.apply()
Esempio n. 7
0
    def add_endpoint_rules(self, suffix, interface, ipv4, ipv6, mac):
        """
        This adds the rules for an endpoint, appending to the end. This generates
        a clean state to allow us to test that the state is correct, even after
        it starts with extra rules etc.
        """
        table = self.tables_v4["filter"]
        chain = table.chains["felix-FROM-ENDPOINT"]
        rule = fiptables.Rule(IPV4, "felix-from-%s" % suffix)
        rule.in_interface = interface
        chain.rules.append(rule)

        chain = table.chains["felix-TO-ENDPOINT"]
        rule = fiptables.Rule(IPV4, "felix-to-%s" % suffix)
        rule.out_interface = interface
        chain.rules.append(rule)

        chain = table.get_chain("felix-from-%s" % suffix)
        rule = fiptables.Rule(IPV4, "DROP")
        rule.create_conntrack_match("INVALID")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.create_conntrack_match("RELATED,ESTABLISHED")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.protocol = "udp"
        rule.create_udp_match("68", "67")
        chain.rules.append(rule)

        if ipv4 is not None:
            rule = fiptables.Rule(IPV4)
            rule.create_target("MARK", {"set-mark": "1"})
            rule.src = ipv4 + "/32"
            rule.create_mac_match(mac)
            chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "DROP")
        rule.create_mark_match("!1")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.create_set_match("felix-from-port-%s" % suffix, "dst,dst")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.create_set_match("felix-from-addr-%s" % suffix, "dst")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.protocol = "icmp"
        rule.create_set_match("felix-from-icmp-%s" % suffix, "dst")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "DROP")
        chain.rules.append(rule)

        chain = table.get_chain("felix-to-%s" % suffix)
        rule = fiptables.Rule(IPV4, "DROP")
        rule.create_conntrack_match("INVALID")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.create_conntrack_match("RELATED,ESTABLISHED")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.create_set_match("felix-to-port-%s" % suffix, "src,dst")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.create_set_match("felix-to-addr-%s" % suffix, "src")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "RETURN")
        rule.protocol = "icmp"
        rule.create_set_match("felix-to-icmp-%s" % suffix, "src")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV4, "DROP")
        chain.rules.append(rule)

        table = self.tables_v6["filter"]
        chain = table.chains["felix-FROM-ENDPOINT"]
        rule = fiptables.Rule(IPV6, "felix-from-%s" % suffix)
        rule.in_interface = interface
        chain.rules.append(rule)

        chain = table.chains["felix-TO-ENDPOINT"]
        rule = fiptables.Rule(IPV6, "felix-to-%s" % suffix)
        rule.out_interface = interface
        chain.rules.append(rule)

        chain = table.get_chain("felix-from-%s" % suffix)
        rule = fiptables.Rule(type, "RETURN")
        rule.protocol = "ipv6-icmp"
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "DROP")
        rule.create_conntrack_match("INVALID")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.create_conntrack_match("RELATED,ESTABLISHED")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.protocol = "udp"
        rule.create_udp_match("546", "547")
        chain.rules.append(rule)

        if ipv6 is not None:
            rule = fiptables.Rule(IPV6)
            rule.create_target("MARK", {"set-mark": "1"})
            rule.src = ipv6
            rule.create_mac_match(mac)
            chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "DROP")
        rule.create_mark_match("!1")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.create_set_match("felix-6-from-port-%s" % suffix, "dst,dst")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.create_set_match("felix-6-from-addr-%s" % suffix, "dst")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.protocol = "ipv6-icmp"
        rule.create_set_match("felix-6-from-icmp-%s" % suffix, "dst")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "DROP")
        chain.rules.append(rule)

        chain = table.get_chain("felix-to-%s" % suffix)
        for icmp in ["130", "131", "132", "134", "135", "136"]:
            rule = fiptables.Rule(IPV6, "RETURN")
            rule.protocol = "ipv6-icmp"
            rule.create_icmp6_match(icmp)
            chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "DROP")
        rule.create_conntrack_match("INVALID")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.create_conntrack_match("RELATED,ESTABLISHED")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.create_set_match("felix-6-to-port-%s" % suffix, "src,dst")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.create_set_match("felix-6-to-addr-%s" % suffix, "src")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "RETURN")
        rule.protocol = "ipv6-icmp"
        rule.create_set_match("felix-6-to-icmp-%s" % suffix, "src")
        chain.rules.append(rule)

        rule = fiptables.Rule(IPV6, "DROP")
        chain.rules.append(rule)

        self.apply()
Esempio n. 8
0
    def set_expected_global_rules(self, prefix="tap"):
        """
        Sets up the minimal global rules we expect to have.
        """
        self.set_empty()

        match = prefix + "+"

        table = self.get_table(IPV4, "filter")
        table.get_chain("felix-TO-ENDPOINT")
        table.get_chain("felix-FROM-ENDPOINT")
        table.get_chain("felix-FORWARD")
        table.get_chain("felix-INPUT")
        chain = table.chains["FORWARD"]
        chain.rules.append(fiptables.Rule(IPV4, "felix-FORWARD"))
        chain = table.chains["INPUT"]
        chain.rules.append(fiptables.Rule(IPV4, "felix-INPUT"))

        chain = table.chains["felix-FORWARD"]
        rule = fiptables.Rule(type, "felix-FROM-ENDPOINT")
        rule.in_interface = match
        chain.rules.append(rule)
        rule = fiptables.Rule(type, "felix-TO-ENDPOINT")
        rule.out_interface = match
        chain.rules.append(rule)
        rule = fiptables.Rule(type, "ACCEPT")
        rule.in_interface = match
        chain.rules.append(rule)
        rule = fiptables.Rule(type, "ACCEPT")
        rule.out_interface = match
        chain.rules.append(rule)

        chain = table.chains["felix-INPUT"]
        rule = fiptables.Rule(type, "felix-FROM-ENDPOINT")
        rule.in_interface = match
        chain.rules.append(rule)
        rule = fiptables.Rule(type, "ACCEPT")
        rule.in_interface = match
        chain.rules.append(rule)

        table = self.get_table(IPV4, "nat")
        chain = table.chains["PREROUTING"]
        chain.rules.append(fiptables.Rule(IPV4, "felix-PREROUTING"))

        chain = table.get_chain("felix-PREROUTING")
        rule = fiptables.Rule(IPV4)
        rule.dst = "169.254.169.254/32"
        rule.protocol = "tcp"
        rule.create_tcp_match("80")
        rule.create_target("DNAT", {'to-destination': '127.0.0.1:9697'})
        chain.rules.append(rule)

        table = self.get_table(IPV6, "filter")
        table.get_chain("felix-TO-ENDPOINT")
        table.get_chain("felix-FROM-ENDPOINT")
        table.get_chain("felix-FORWARD")
        table.get_chain("felix-INPUT")
        chain = table.chains["FORWARD"]
        chain.rules.append(fiptables.Rule(IPV6, "felix-FORWARD"))
        chain = table.chains["INPUT"]
        chain.rules.append(fiptables.Rule(IPV6, "felix-INPUT"))

        chain = table.chains["felix-FORWARD"]
        rule = fiptables.Rule(type, "felix-FROM-ENDPOINT")
        rule.in_interface = match
        chain.rules.append(rule)
        rule = fiptables.Rule(type, "felix-TO-ENDPOINT")
        rule.out_interface = match
        chain.rules.append(rule)
        rule = fiptables.Rule(type, "ACCEPT")
        rule.in_interface = match
        chain.rules.append(rule)
        rule = fiptables.Rule(type, "ACCEPT")
        rule.out_interface = match
        chain.rules.append(rule)

        chain = table.chains["felix-INPUT"]
        rule = fiptables.Rule(type, "felix-FROM-ENDPOINT")
        rule.in_interface = match
        chain.rules.append(rule)
        rule = fiptables.Rule(type, "ACCEPT")
        rule.in_interface = match
        chain.rules.append(rule)

        self.apply()
Esempio n. 9
0
    def test_rule_match(self):
        """
        Test every possible rule match.
        """
        rule1 = fiptables.Rule(IPV4)
        rule2 = fiptables.Rule(IPV6)
        self.assertNotEqual(rule1, rule2)

        rule1 = fiptables.Rule(IPV4)
        rule2 = fiptables.Rule(IPV4)
        self.assertEqual(rule1, rule2)

        rule1.target = "blah"
        self.assertNotEqual(rule1, rule2)
        rule2.target = "x"
        self.assertNotEqual(rule1, rule2)
        rule2.target = "blah"
        self.assertEqual(rule1, rule2)

        rule1.dst = "dst"
        self.assertNotEqual(rule1, rule2)
        rule2.dst = "x"
        self.assertNotEqual(rule1, rule2)
        rule2.dst = "dst"
        self.assertEqual(rule1, rule2)

        rule1.src = "src"
        self.assertNotEqual(rule1, rule2)
        rule2.src = "x"
        self.assertNotEqual(rule1, rule2)
        rule2.src = "src"
        self.assertEqual(rule1, rule2)

        rule1.match = "match"
        self.assertNotEqual(rule1, rule2)
        rule2.match = "x"
        self.assertNotEqual(rule1, rule2)
        rule2.match = "match"
        self.assertEqual(rule1, rule2)

        rule1.protocol = "protocol"
        self.assertNotEqual(rule1, rule2)
        rule2.protocol = "x"
        self.assertNotEqual(rule1, rule2)
        rule2.protocol = "protocol"
        self.assertEqual(rule1, rule2)

        rule1.in_interface = "in_interface"
        self.assertNotEqual(rule1, rule2)
        rule2.in_interface = "x"
        self.assertNotEqual(rule1, rule2)
        rule2.in_interface = "in_interface"
        self.assertEqual(rule1, rule2)

        rule1.out_interface = "out_interface"
        self.assertNotEqual(rule1, rule2)
        rule2.out_interface = "x"
        self.assertNotEqual(rule1, rule2)
        rule2.out_interface = "out_interface"
        self.assertEqual(rule1, rule2)

        rule1.parameters = { "x": "blah", "y": "other" }
        self.assertNotEqual(rule1, rule2)
        rule2.parameters = { "x": "blah" }
        self.assertNotEqual(rule1, rule2)
        rule2.parameters["y"] = "other"
        self.assertEqual(rule1, rule2)
Esempio n. 10
0
    def test_insert_rule(self):
        """
        Test insert_rule
        """
        data = "\n".join(["-N blah",
                          "-A blah -j original"])

        state = fiptables.TableState()
        state.read_table = mock.Mock(return_value = data)
        table = state.get_table(IPV4, "filter")
        chain = table.get_chain("blah")
        self.assertTrue(len(chain.rules), 1)

        # Set up a handy list of arguments
        base_args = ["iptables", "--wait", "--table", "filter"]

        # Put a new rule at the start
        rule = fiptables.Rule(IPV4, "rule1")
        chain.insert_rule(rule)
        self.assertTrue(len(chain.rules), 2)
        self.assertTrue(chain.rules[0].target, "rule1")
        self.assertTrue(chain.rules[1].target, "original")
        args = copy(base_args)
        args.extend(["--insert", "blah", "1", "-j", "rule1"])
        self.assertEqual(len(table.ops), 1)
        self.assertEqual(table.ops[0], args)

        # Put in a copy of the original rule, forcing position.
        rule = fiptables.Rule(IPV4, "original")
        chain.insert_rule(rule)
        self.assertTrue(len(chain.rules), 3)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        args = copy(base_args)
        args.extend(["--insert", "blah", "1", "-j", "original"])
        self.assertEqual(len(table.ops), 2)
        self.assertEqual(table.ops[1], args)

        # Now add another copy. Does not get added, as already there.
        rule = fiptables.Rule(IPV4, "original")
        chain.insert_rule(rule)
        self.assertTrue(len(chain.rules), 3)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertEqual(len(table.ops), 2)

        # Add rule1 again - doesn't get added as not forcing position.
        rule = fiptables.Rule(IPV4, "rule1")
        chain.insert_rule(rule, 0, False)
        self.assertTrue(len(chain.rules), 3)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertEqual(len(table.ops), 2)

        chain.insert_rule(rule, 1, False)
        self.assertTrue(len(chain.rules), 3)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertEqual(len(table.ops), 2)

        chain.insert_rule(rule, 2, False)
        self.assertTrue(len(chain.rules), 3)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertEqual(len(table.ops), 2)

        chain.insert_rule(rule, fiptables.RULE_POSN_LAST, False)
        self.assertTrue(len(chain.rules), 3)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertEqual(len(table.ops), 2)

        # Now use POSN_LAST but forcing position
        rule = fiptables.Rule(IPV4, "original")
        chain.insert_rule(rule, fiptables.RULE_POSN_LAST)
        self.assertTrue(len(chain.rules), 3)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertEqual(len(table.ops), 2)

        rule = fiptables.Rule(IPV4, "rule1")
        chain.insert_rule(rule, fiptables.RULE_POSN_LAST)
        self.assertTrue(len(chain.rules), 4)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertTrue(chain.rules[3].target, "rule1")
        args = copy(base_args)
        args.extend(["--append", "blah", "-j", "rule1"])
        self.assertEqual(len(table.ops), 3)
        self.assertEqual(table.ops[2], args)

        rule = fiptables.Rule(IPV4, "rule1")
        chain.insert_rule(rule, fiptables.RULE_POSN_LAST, True)
        self.assertTrue(len(chain.rules), 4)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertTrue(chain.rules[3].target, "rule1")
        self.assertEqual(len(table.ops), 3)

        rule = fiptables.Rule(IPV4, "new")
        chain.insert_rule(rule, fiptables.RULE_POSN_LAST, False)
        self.assertTrue(len(chain.rules), 5)
        self.assertTrue(chain.rules[0].target, "original")
        self.assertTrue(chain.rules[1].target, "rule1")
        self.assertTrue(chain.rules[2].target, "original")
        self.assertTrue(chain.rules[3].target, "rule1")
        self.assertTrue(chain.rules[4].target, "original")
        args = copy(base_args)
        args.extend(["--append", "blah", "-j", "new"])
        self.assertEqual(len(table.ops), 4)
        self.assertEqual(table.ops[3], args)
Esempio n. 11
0
    def test_apply(self):
        """
        Test apply method.
        """
        data = "\n".join(["-P INPUT ACCEPT",
                          "-P FORWARD ACCEPT",
                          "-P OUTPUT ACCEPT",
                          "-N felix-FORWARD",
                          "-N felix-FROM-ENDPOINT",
                          "-N felix-INPUT",
                          "-N felix-TO-ENDPOINT",
                          "-N felix-from-19f8308f-81",
                          "-N felix-from-e6d6a9a9-37",
                          "-N felix-to-19f8308f-81",
                          "-N felix-to-e6d6a9a9-37",
                          "-A INPUT -j felix-INPUT",
                          "-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT",
                          "-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT",
                          "-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT",
                          "-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT",
                          "-A INPUT -j nova-api-INPUT",
                          "-A FORWARD -j felix-FORWARD",
                          "-A FORWARD -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT",
                          "-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT",
                          "-A FORWARD -i virbr0 -o virbr0 -j ACCEPT",
                          "-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable",
                          "-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable",
                          "-A FORWARD -j nova-filter-top",
                          "-A FORWARD -j nova-api-FORWARD"])

        state = fiptables.TableState()
        state.read_table = mock.Mock(return_value = data)
        table = state.get_table(IPV4, "filter")

        # Build up a list of rules as they will be after changes.
        rules = []
        rules.append(fiptables.Rule(IPV4))
        line = "-j felix-FORWARD"
        rules[0].parse_fields(line, line.split())

        # New rule to be added at location 1
        rules.append(fiptables.Rule(IPV4, "somewhere"))
        rules[1].protocol = "udp"
        rules[1].in_interface = "blah"
        rules[1].out_interface = "stuff"
        rules[1].match = "udp"
        rules[1].parameters["dport"] = "53"

        line = "-d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT"
        rules.append(fiptables.Rule(IPV4))
        rules[2].parse_fields(line, line.split())

        chain = table.get_chain("FORWARD")
        self.assertEqual(len(chain.rules), 8)

        chain.insert_rule(rules[1], 1)

        chain.truncate_rules(3)

        # Verify that the rules are as expected.
        self.assertEqual(len(chain.rules), 3)
        self.assertEqual(rules, chain.rules)

        # Now test apply.
        with mock.patch('calico.felix.futils.multi_call') as mock_call:
            state.apply()
        self.assertEqual(mock_call.call_count, 1)

        ops = []
        op = ["iptables", "--wait", "--table", "filter", "--insert", "FORWARD", "2"]
        op.extend(rules[1].generate_fields())
        ops.append(op)

        for loop in range(0,6):
            ops.append(["iptables",
                        "--wait",
                        "--table",
                        "filter",
                        "--delete",
                        "FORWARD",
                        "4"])

        self.assertEqual(mock_call.call_count, 1)
        mock_call.assert_called_with(ops)

        # Apply again - this is a noop, as states match.
        with mock.patch('calico.felix.futils.multi_call') as mock_call:
            state.apply()
        self.assertEqual(mock_call.call_count, 0)