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"])
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)
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)