示例#1
0
    def test_create(self):
        retcode = futils.CommandOutput("", "")

        with mock.patch('calico.felix.futils.call_silent', return_value=1):
            with mock.patch('calico.felix.futils.check_call', return_value=retcode):
                ipsets.create("a", "b", "c")
                futils.call_silent.assert_called_with(["ipset", "list", "a"])
                futils.check_call.assert_called_with(["ipset", "create", "a", "b", "family", "c"])

        with mock.patch('calico.felix.futils.call_silent', return_value=0):
            with mock.patch('calico.felix.futils.check_call', return_value=retcode):
                ipsets.create("a", "b", "c")
                futils.call_silent.assert_called_with(["ipset", "list", "a"])
                self.assertFalse(futils.check_call.called)
示例#2
0
def set_acls(suffix, type, inbound, in_default, outbound, out_default):
    """
    Set up the ACLs, making sure that they match.
    """
    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
        tmp_ipset_port  = IPSET_TMP_PORT
        tmp_ipset_addr  = IPSET_TMP_ADDR
        tmp_ipset_icmp  = IPSET_TMP_ICMP
        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
        tmp_ipset_port  = IPSET6_TMP_PORT
        tmp_ipset_addr  = IPSET6_TMP_ADDR
        tmp_ipset_icmp  = IPSET6_TMP_ICMP
        family          = "inet6"

    if in_default != "deny" or out_default != "deny":
        #*********************************************************************#
        #* Only default deny rules are implemented. When we implement        *#
        #* default accept rules, it will be necessary for                    *#
        #* set_ep_specific_rules to at least know what the default policy    *#
        #* is. That implies that set_ep_specific_rules probably ought to be  *#
        #* moved to be called here rather than where it is now. This issue   *#
        #* is covered by https://github.com/Metaswitch/calico/issues/39      *#
        #*********************************************************************#
        log.critical("Only default deny rules are implemented")

    # Verify that the tmp ipsets exist and are empty.
    ipsets.create(tmp_ipset_port, "hash:net,port", family)
    ipsets.create(tmp_ipset_addr, "hash:net", family)
    ipsets.create(tmp_ipset_icmp, "hash:net", family)

    ipsets.flush(tmp_ipset_port)
    ipsets.flush(tmp_ipset_addr)
    ipsets.flush(tmp_ipset_icmp)

    update_ipsets(type, type + " inbound", suffix,
                  inbound,
                  to_ipset_addr, to_ipset_port, to_ipset_icmp,
                  tmp_ipset_addr, tmp_ipset_port, tmp_ipset_icmp)
    update_ipsets(type, type + " outbound", suffix,
                  outbound,
                  from_ipset_addr, from_ipset_port, from_ipset_icmp,
                  tmp_ipset_addr, tmp_ipset_port, tmp_ipset_icmp)
示例#3
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)