예제 #1
0
    def test_flush(self):
        retcode = futils.CommandOutput("", "")

        with mock.patch('calico.felix.futils.check_call',
                        return_value=retcode):
            ipsets.flush("a")
            futils.check_call.assert_called_with(["ipset", "flush", "a"])
예제 #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 update_ipsets(type,
                  descr,
                  suffix,
                  rule_list,
                  ipset_addr,
                  ipset_port,
                  ipset_icmp,
                  tmp_ipset_addr,
                  tmp_ipset_port,
                  tmp_ipset_icmp):
    """
    Update the ipsets with a given set of rules. If a rule is invalid we do
    not throw an exception or give up, but just log an error and continue.
    """
    for rule in rule_list:
        if rule.get('cidr') is None:
            log.error("Invalid %s rule without cidr for %s : %s" %
                      (descr, suffix, rule))
            continue

        #*********************************************************************#
        #* The ipset format is something like "10.11.1.3,udp:0"              *#
        #* Further valid examples include                                    *#
        #*   10.11.1.0/24                                                    *#
        #*   10.11.1.0/24,tcp                                                *#
        #*   10.11.1.0/24,80                                                 *#
        #*                                                                   *#
        #*********************************************************************#
        if rule['cidr'].endswith("/0"):
            #*****************************************************************#
            #* We have to handle any CIDR with a "/0" specially, since we    *#
            #* split it into two ipsets entries; ipsets cannot have zero     *#
            #* CIDR length in bits.                                          *#
            #*****************************************************************#
            if type == IPV4:
                cidrs = ["0.0.0.0/1", "128.0.0.0/1"]
            else:
                cidrs = ["::/1", "8000::/1"]
        else:
            cidrs = [rule['cidr']]

        #*********************************************************************#
        #* Now handle the protocol. There are three types of protocol. tcp / *#
        #* sctp /udp / udplite have an optional port. icmp / icmpv6 have an  *#
        #* optional type and code. Anything else doesn't have ports.         *#
        #*                                                                   *#
        #* We build the value to insert without the CIDR, then prepend the   *#
        #* CIDR later (since we may need to use two CIDRs).                  *#
        #*********************************************************************#
        protocol  = rule.get('protocol')
        port      = rule.get('port')
        icmp_type = rule.get('icmp_type')
        icmp_code = rule.get('icmp_code')

        if protocol is None:
            if rule.get('port') is not None:
                # No protocol, so port is not allowed.
                log.error(
                    "Invalid %s rule with port but no protocol for %s : %s",
                    descr, suffix, rule)
                continue
            ipset_value = ""
            ipset  = tmp_ipset_addr
        elif protocol in ("tcp", "sctp", "udp", "udplite"):
            if port is None:
                # ipsets use port 0 to mean "any port"
                ipset_value = ",%s:0" % (protocol)
                ipset = tmp_ipset_port
            elif isinstance(port, list) and len(port) == 2:
                # List of two ports - port range
                if (not common.validate_port(str(port[0])) or
                    not common.validate_port(str(port[1]))    ):
                    # Port range was not two valid ports.
                    log.error(
                        "Invalid port range in %s rule for %s : %s",
                        descr, suffix, rule)
                    continue
                ipset_value = ",%s:%s-%s" % (protocol, port[0], port[1])
                ipset = tmp_ipset_port
            else:
                if not common.validate_port(str(port)):
                    # Port was supplied but was not an integer or range.
                    log.error(
                        "Invalid port in %s rule for %s : %s",
                        descr, suffix, rule)
                    continue

                # An integer port was specified.
                ipset_value = ",%s:%s" % (protocol, port)
                ipset = tmp_ipset_port
        elif protocol in ("icmp", "icmpv6"):
            if rule.get('port') is not None:
                # No protocol, so port is not allowed.
                log.error(
                    "Invalid %s rule for %s with port for protocol %s : %s",
                    descr, suffix, protocol, rule)
                continue

            if (icmp_type is None and icmp_code is not None):
                # A code but no type - not allowed.
                log.error(
                    "Invalid %s rule with ICMP code but no type for %s : %s",
                    descr, suffix, rule)
                continue
            if icmp_type is None:
                # No type - all ICMP to / from the cidr, so use the ICMP ipset.
                ipset_value = ""
                ipset  = tmp_ipset_icmp
            else:
                try:
                    # Assume integer ICMP type first.
                    int(icmp_type)
                    if icmp_code is None:
                        # Code defaults to 0 if not supplied.
                        icmp_code = 0
                    ipset_value = ",%s:%s/%s" % (protocol, icmp_type, icmp_code)
                    ipset  = tmp_ipset_port
                except ValueError:
                    # Not an integer ICMP type - must be a string code name.
                    ipset_value = ",%s:%s" % (protocol, icmp_type)
                    ipset  = tmp_ipset_port
        else:
            if port is not None:
                # The supplied protocol does not allow ports.
                log.error(
                    "Invalid %s rule with port but no protocol for %s : %s",
                    descr, suffix, rule)
                continue
            # ipsets use port 0 to mean "any port"
            ipset_value = ",%s:0" % (protocol)
            ipset = tmp_ipset_port

        # Now add those values to the ipsets.
        for cidr in cidrs:
            try:
                ipsets.add(ipset, cidr + ipset_value)
            except FailedSystemCall:
                log.exception("Failed to add %s rule (%s) for %s",
                              descr,
                              cidr + ipset_value,
                              suffix)

    # Now that we have filled the tmp ipset, swap it with the real one.
    ipsets.swap(tmp_ipset_addr, ipset_addr)
    ipsets.swap(tmp_ipset_port, ipset_port)
    ipsets.swap(tmp_ipset_icmp, ipset_icmp)

    # Get the temporary ipsets clean again - we leave them existing but empty.
    ipsets.flush(tmp_ipset_port)
    ipsets.flush(tmp_ipset_addr)
    ipsets.flush(tmp_ipset_icmp)