mirror of https://github.com/apache/cloudstack.git
security groups: conntrack only if needed (#10594)
The conntrack is disabled if the security group allows all traffic. Also, refactored the code a little.
This commit is contained in:
parent
28820f6e25
commit
bb5da0e49c
|
|
@ -28,6 +28,9 @@ import fcntl
|
|||
import time
|
||||
import ipaddress
|
||||
|
||||
NOTRACK_IPV4_IPSET = 'cs_notrack'
|
||||
NOTRACK_IPV6_IPSET = 'cs_notrack6'
|
||||
|
||||
logpath = "/var/run/cloud/" # FIXME: Logs should reside in /var/log/cloud
|
||||
lock_file = "/var/lock/cloudstack_security_group.lock"
|
||||
driver = "qemu:///system"
|
||||
|
|
@ -131,20 +134,37 @@ def ipv6_link_local_addr(mac=None):
|
|||
return ipaddress.ip_address('fe80::' + ':'.join(re.findall(r'.{4}', eui64)))
|
||||
|
||||
|
||||
def split_ips_by_family(ips):
|
||||
if type(ips) is str:
|
||||
ips = [ip for ip in ips.split(';') if ip != '']
|
||||
def split_ips_by_family(*addresses):
|
||||
"""
|
||||
Takes one or more IP addresses as the input, and returns two lists:
|
||||
one for ipv4 addresses, one for ipv6 addresses
|
||||
|
||||
If one of the inputs is a string, it tries to split it by semicolon,
|
||||
and ignores the empty fields, or the fields with the value '0'
|
||||
"""
|
||||
ips = []
|
||||
for i in addresses:
|
||||
if not i: # IP address can be sometimes None (e.g. when ipv6 address not present)
|
||||
continue
|
||||
if type(i) is str:
|
||||
ips += [ip for ip in i.split(';') if ip != '' and ip != '0']
|
||||
else:
|
||||
ips.append(str(i))
|
||||
|
||||
ip4s = []
|
||||
ip6s = []
|
||||
for ip in ips:
|
||||
network = ipaddress.ip_network(ip)
|
||||
if network.version == 4:
|
||||
ip4s.append(ip)
|
||||
elif network.version == 6:
|
||||
ip6s.append(ip)
|
||||
try:
|
||||
addr = ipaddress.ip_address(ip)
|
||||
if isinstance(addr, ipaddress.IPv4Address):
|
||||
ip4s.append(str(addr))
|
||||
elif isinstance(addr, ipaddress.IPv6Address):
|
||||
ip6s.append(str(addr))
|
||||
except ValueError as e:
|
||||
logging.warning(f"Could not parse one of the IP addresses in the list {ips}: {str(e)}")
|
||||
return ip4s, ip6s
|
||||
|
||||
|
||||
def destroy_network_rules_for_nic(vm_name, vm_ip, vm_mac, vif, sec_ips):
|
||||
try:
|
||||
rules = execute("""iptables-save -t filter | awk '/ %s / { sub(/-A/, "-D", $1) ; print }'""" % vif ).split("\n")
|
||||
|
|
@ -172,6 +192,13 @@ def destroy_network_rules_for_nic(vm_name, vm_ip, vm_mac, vif, sec_ips):
|
|||
add_to_ipset(vm_name, ips, "-D")
|
||||
ebtables_rules_vmip(vm_name, vm_mac, ips, "-D")
|
||||
|
||||
ip4s = sec_ips.split(';')
|
||||
ip4s.pop()
|
||||
ip4s = [ x for x in ip4s if x != '0']
|
||||
ip4s.append(vm_ip)
|
||||
|
||||
add_to_ipset(NOTRACK_IPV4_IPSET, ip4s, "del")
|
||||
|
||||
vmchain_in = vm_name + "-in"
|
||||
vmchain_out = vm_name + "-out"
|
||||
vmchain_in_src = vm_name + "-in-src"
|
||||
|
|
@ -454,16 +481,11 @@ def create_ipset_forvm(ipsetname, type='iphash', family='inet'):
|
|||
|
||||
|
||||
def add_to_ipset(ipsetname, ips, action):
|
||||
result = True
|
||||
for ip in ips:
|
||||
try:
|
||||
logging.debug("vm ip " + str(ip))
|
||||
execute("ipset " + action + " " + ipsetname + " " + str(ip))
|
||||
except:
|
||||
logging.debug("vm ip already in ip set " + str(ip))
|
||||
continue
|
||||
logging.debug("vm ip " + str(ip))
|
||||
execute("ipset -! " + action + " " + ipsetname + " " + str(ip))
|
||||
|
||||
return result
|
||||
return True
|
||||
|
||||
|
||||
def network_rules_vmSecondaryIp(vm_name, vm_mac, ip_secondary, action):
|
||||
|
|
@ -487,7 +509,7 @@ def network_rules_vmSecondaryIp(vm_name, vm_mac, ip_secondary, action):
|
|||
|
||||
return True
|
||||
|
||||
def ebtables_rules_vmip (vmname, vmmac, ips, action):
|
||||
def ebtables_rules_vmip(vmname, vmmac, ips, action):
|
||||
eb_vm_chain=ebtables_chain_name(vmname)
|
||||
vmchain_inips = eb_vm_chain + "-in-ips"
|
||||
vmchain_outips = eb_vm_chain + "-out-ips"
|
||||
|
|
@ -576,28 +598,21 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se
|
|||
return False
|
||||
|
||||
#add secodnary nic ips to ipset
|
||||
secIpSet = "1"
|
||||
ips = sec_ips.split(';')
|
||||
ips.pop()
|
||||
|
||||
if len(ips) == 0 or ips[0] == "0":
|
||||
secIpSet = "0"
|
||||
ip4s = []
|
||||
ip6s = []
|
||||
|
||||
if secIpSet == "1":
|
||||
logging.debug("Adding ipset for secondary ipv4 addresses")
|
||||
ip4s, ip6s = split_ips_by_family(ips)
|
||||
|
||||
ip4s, ip6s = split_ips_by_family(sec_ips, vm_ip, vm_ip6, ipv6_link_local)
|
||||
if ip4s:
|
||||
logging.debug("Adding ipset for all ipv4 addresses")
|
||||
add_to_ipset(vmipsetName, ip4s, action)
|
||||
|
||||
if not write_secip_log_for_vm(vm_name, sec_ips, vm_id):
|
||||
logging.debug("Failed to log default network rules, ignoring")
|
||||
|
||||
if ip6s:
|
||||
logging.debug("Adding ipset for all ipv6 addresses")
|
||||
add_to_ipset(vmipsetName6, ip6s, action)
|
||||
|
||||
try:
|
||||
execute("iptables -A " + brfw + "-OUT" + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmchain_default)
|
||||
execute("iptables -A " + brfw + "-IN" + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -j " + vmchain_default)
|
||||
execute("iptables -A " + vmchain_default + " -m state --state RELATED,ESTABLISHED -j ACCEPT")
|
||||
#allow dhcp
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -p udp --dport 67 --sport 68 -j ACCEPT")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -p udp --dport 68 --sport 67 -j ACCEPT")
|
||||
|
|
@ -607,12 +622,16 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se
|
|||
if vm_ip:
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set ! --match-set " + vmipsetName + " src -j DROP")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -m set ! --match-set " + vmipsetName + " dst -j DROP")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --match-set " + vmipsetName + " src -p udp --dport 53 -j RETURN ")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --match-set " + vmipsetName + " src -p tcp --dport 53 -j RETURN ")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --match-set " + vmipsetName + " src -p udp --dport 53 -j ACCEPT")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --match-set " + vmipsetName + " src -p tcp --dport 53 -j ACCEPT")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --match-set " + vmipsetName + " src -j " + vmchain_egress)
|
||||
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmchain)
|
||||
execute("iptables -A " + vmchain + " -j DROP")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m state --state ESTABLISHED,RELATED -j ACCEPT")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -m state --state ESTABLISHED,RELATED -j ACCEPT")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -j DROP")
|
||||
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -j DROP")
|
||||
execute("iptables -A " + vmchain + " -j RETURN")
|
||||
except:
|
||||
logging.debug("Failed to program default rules for vm " + vm_name)
|
||||
return False
|
||||
|
|
@ -625,59 +644,45 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se
|
|||
if not write_rule_log_for_vm(vmName, vm_id, vm_ip, domID, '_initial_', '-1'):
|
||||
logging.debug("Failed to log default network rules, ignoring")
|
||||
|
||||
vm_ip6_addr = [ipv6_link_local]
|
||||
try:
|
||||
ip6 = ipaddress.ip_address(vm_ip6)
|
||||
if ip6.version == 6:
|
||||
vm_ip6_addr.append(ip6)
|
||||
except (ipaddress.AddressValueError, ValueError):
|
||||
pass
|
||||
|
||||
add_to_ipset(vmipsetName6, vm_ip6_addr, action)
|
||||
if secIpSet == "1":
|
||||
logging.debug("Adding ipset for secondary ipv6 addresses")
|
||||
add_to_ipset(vmipsetName6, ip6s, action)
|
||||
|
||||
try:
|
||||
execute('ip6tables -A ' + brfw + '-OUT' + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -j ' + vmchain_default)
|
||||
execute('ip6tables -A ' + brfw + '-IN' + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -j ' + vmchain_default)
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m state --state RELATED,ESTABLISHED -j ACCEPT')
|
||||
|
||||
# Allow Instances to receive Router Advertisements, send out solicitations, but block any outgoing Advertisement from a Instance
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' --src fe80::/64 --dst ff02::1 -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' --dst ff02::2 -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' --dst ff02::2 -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type router-advertisement -j DROP')
|
||||
|
||||
# Allow neighbor solicitations and advertisements
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -m set --match-set ' + vmipsetName6 + ' src -m hl --hl-eq 255 -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -m set --match-set ' + vmipsetName6 + ' src -m hl --hl-eq 255 -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT')
|
||||
|
||||
# Packets to allow as per RFC4890
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type packet-too-big -m set --match-set ' + vmipsetName6 + ' src -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type packet-too-big -m set --match-set ' + vmipsetName6 + ' src -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT')
|
||||
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type destination-unreachable -m set --match-set ' + vmipsetName6 + ' src -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type destination-unreachable -m set --match-set ' + vmipsetName6 + ' src -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT')
|
||||
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type time-exceeded -m set --match-set ' + vmipsetName6 + ' src -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type time-exceeded -m set --match-set ' + vmipsetName6 + ' src -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT')
|
||||
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type parameter-problem -m set --match-set ' + vmipsetName6 + ' src -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type parameter-problem -m set --match-set ' + vmipsetName6 + ' src -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT')
|
||||
|
||||
# MLDv2 discovery packets
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --dst ff02::16 -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --dst ff02::16 -j ACCEPT')
|
||||
|
||||
# Allow Instances to send out DHCPv6 client messages, but block server messages
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --sport 546 --dst ff02::1:2 --src ' + str(ipv6_link_local) + ' -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --sport 546 --dst ff02::1:2 --src ' + str(ipv6_link_local) + ' -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p udp --src fe80::/64 --dport 546 --dst ' + str(ipv6_link_local) + ' -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --sport 547 ! --dst fe80::/64 -j DROP')
|
||||
|
||||
# Always allow outbound DNS over UDP and TCP
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --dport 53 -m set --match-set ' + vmipsetName6 + ' src -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p tcp --dport 53 -m set --match-set ' + vmipsetName6 + ' src -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --dport 53 -m set --match-set ' + vmipsetName6 + ' src -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p tcp --dport 53 -m set --match-set ' + vmipsetName6 + ' src -j ACCEPT')
|
||||
|
||||
# Prevent source address spoofing
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -m set ! --match-set ' + vmipsetName6 + ' src -j DROP')
|
||||
|
|
@ -687,8 +692,13 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se
|
|||
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -j ' + vmchain)
|
||||
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -m state --state ESTABLISHED,RELATED -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -m state --state ESTABLISHED,RELATED -j ACCEPT')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -j DROP')
|
||||
execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -j DROP')
|
||||
|
||||
# Drop all other traffic into the Instance
|
||||
execute('ip6tables -A ' + vmchain + ' -j DROP')
|
||||
execute('ip6tables -A ' + vmchain + ' -j RETURN')
|
||||
except:
|
||||
logging.debug('Failed to program default rules for vm ' + vm_name)
|
||||
return False
|
||||
|
|
@ -1106,11 +1116,6 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru
|
|||
logging.debug("Rules already programmed for vm " + vm_name)
|
||||
return True
|
||||
|
||||
if rules == "" or rules == None:
|
||||
lines = []
|
||||
else:
|
||||
lines = rules.split(';')[:-1]
|
||||
|
||||
logging.debug("programming network rules for IP: " + vm_ip + " vmname=%s", vm_name)
|
||||
|
||||
egress_chain_name(vm_name)
|
||||
|
|
@ -1128,7 +1133,38 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru
|
|||
egressrule_v4 = 0
|
||||
egressrule_v6 = 0
|
||||
|
||||
for rule in parse_network_rules(rules):
|
||||
ip4s, ip6s = split_ips_by_family(vm_ip, vm_ip6, sec_ips, str(ipv6_link_local_addr(vmMac)))
|
||||
|
||||
rules = parse_network_rules(rules)
|
||||
conntrack4_not_needed = False
|
||||
conntrack6_not_needed = False
|
||||
for rule in rules:
|
||||
"""
|
||||
If any of the rules has an explicit allow all protocols from 0.0.0.0/0 (ipv4)
|
||||
or ::/0 (ipv6), then that IP family doesn't need its connection tracked
|
||||
Example contents of the rules list:
|
||||
[
|
||||
{'ipv4': ['1.0.0.0/24', '0.0.0.0/0'], 'ipv6': ['::/0'], 'ruletype': 'I', 'start': 0, 'end': 0, 'protocol': 'all'},
|
||||
{'ipv4': ['1.1.1.1/32'], 'ipv6': [], 'ruletype': 'I', 'start': 1, 'end': 65535, 'protocol': 'tcp'},
|
||||
{'ipv4': [], 'ipv6': ['2001:db8::/32'], 'ruletype': 'I', 'start': 2000, 'end': 3000, 'protocol': 'tcp'}
|
||||
]
|
||||
"""
|
||||
if '0.0.0.0/0' in rule['ipv4'] and rule['protocol'].lower() == 'all':
|
||||
conntrack4_not_needed = True
|
||||
if '::/0' in rule['ipv6'] and rule['protocol'].lower() == 'all':
|
||||
conntrack6_not_needed = True
|
||||
|
||||
if conntrack4_not_needed:
|
||||
add_to_ipset(NOTRACK_IPV4_IPSET, ip4s, "add")
|
||||
else:
|
||||
add_to_ipset(NOTRACK_IPV4_IPSET, ip4s, "del")
|
||||
|
||||
if conntrack6_not_needed:
|
||||
add_to_ipset(NOTRACK_IPV6_IPSET, ip6s, "add")
|
||||
else:
|
||||
add_to_ipset(NOTRACK_IPV6_IPSET, ip6s, "del")
|
||||
|
||||
for rule in rules:
|
||||
start = rule['start']
|
||||
end = rule['end']
|
||||
protocol = rule['protocol']
|
||||
|
|
@ -1136,7 +1172,7 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru
|
|||
if rule['ruletype'] == 'E':
|
||||
vmchain = egress_vmchain
|
||||
direction = "-d"
|
||||
action = "RETURN"
|
||||
action = "ACCEPT"
|
||||
if rule['ipv4']:
|
||||
egressrule_v4 =+ 1
|
||||
|
||||
|
|
@ -1155,10 +1191,8 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru
|
|||
|
||||
if protocol != 'all' and protocol != 'icmp' and protocol != 'tcp' and protocol != 'udp':
|
||||
protocol_all = " -p " + protocol
|
||||
protocol_state = " "
|
||||
else:
|
||||
protocol_all = " -p " + protocol + " -m " + protocol
|
||||
protocol_state = " -m state --state NEW "
|
||||
|
||||
if 'icmp' == protocol:
|
||||
range = str(start) + '/' + str(end)
|
||||
|
|
@ -1167,17 +1201,17 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru
|
|||
|
||||
for ip in rule['ipv4']:
|
||||
if protocol == 'all':
|
||||
execute('iptables -I ' + vmchain + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action)
|
||||
execute('iptables -I ' + vmchain + ' ' + direction + ' ' + ip + ' -j ' + action)
|
||||
elif protocol == 'icmp':
|
||||
execute("iptables -I " + vmchain + " -p icmp --icmp-type " + range + " " + direction + " " + ip + " -j " + action)
|
||||
else:
|
||||
execute("iptables -I " + vmchain + protocol_all + dport + protocol_state + direction + " " + ip + " -j "+ action)
|
||||
execute("iptables -I " + vmchain + protocol_all + dport + " " + direction + " " + ip + " -j "+ action)
|
||||
|
||||
for ip in rule['ipv6']:
|
||||
if protocol == 'all':
|
||||
execute('ip6tables -I ' + vmchain + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action)
|
||||
execute('ip6tables -I ' + vmchain + ' ' + direction + ' ' + ip + ' -j ' + action)
|
||||
elif 'icmp' != protocol:
|
||||
execute("ip6tables -I " + vmchain + protocol_all + dport + protocol_state + direction + " " + ip + " -j "+ action)
|
||||
execute("ip6tables -I " + vmchain + protocol_all + dport + ' ' + direction + " " + ip + " -j "+ action)
|
||||
else:
|
||||
# ip6tables does not allow '--icmpv6-type any', allowing all ICMPv6 is done by not allowing a specific type
|
||||
if range == 'any':
|
||||
|
|
@ -1187,19 +1221,19 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru
|
|||
|
||||
egress_vmchain = egress_chain_name(vm_name)
|
||||
if egressrule_v4 == 0 :
|
||||
execute('iptables -A ' + egress_vmchain + ' -j RETURN')
|
||||
execute('iptables -A ' + egress_vmchain + ' -j ACCEPT')
|
||||
else:
|
||||
execute('iptables -A ' + egress_vmchain + ' -j DROP')
|
||||
execute('iptables -A ' + egress_vmchain + ' -j RETURN')
|
||||
|
||||
if egressrule_v6 == 0 :
|
||||
execute('ip6tables -A ' + egress_vmchain + ' -j RETURN')
|
||||
execute('ip6tables -A ' + egress_vmchain + ' -j ACCEPT')
|
||||
else:
|
||||
execute('ip6tables -A ' + egress_vmchain + ' -j DROP')
|
||||
execute('ip6tables -A ' + egress_vmchain + ' -j RETURN')
|
||||
|
||||
vmchain = iptables_chain_name(vm_name)
|
||||
|
||||
execute('iptables -A ' + vmchain + ' -j DROP')
|
||||
execute('ip6tables -A ' + vmchain + ' -j DROP')
|
||||
execute('iptables -A ' + vmchain + ' -j RETURN')
|
||||
execute('ip6tables -A ' + vmchain + ' -j RETURN')
|
||||
|
||||
if not write_rule_log_for_vm(vmName, vm_id, vm_ip, domId, signature, seqno):
|
||||
return False
|
||||
|
|
@ -1288,7 +1322,7 @@ def add_fw_framework(brname):
|
|||
execute("sysctl -w net.bridge.bridge-nf-call-iptables=1")
|
||||
execute("sysctl -w net.bridge.bridge-nf-call-ip6tables=1")
|
||||
except:
|
||||
logging.warn("failed to turn on bridge netfilter")
|
||||
logging.warning("failed to turn on bridge netfilter")
|
||||
|
||||
brfw = get_br_fw(brname)
|
||||
try:
|
||||
|
|
@ -1327,6 +1361,30 @@ def add_fw_framework(brname):
|
|||
|
||||
physdev = get_bridge_physdev(brname)
|
||||
|
||||
# Add notrack ipset. The IP addresses from it will be excluded from connection tracking
|
||||
execute(f"ipset -! create {NOTRACK_IPV4_IPSET} hash:ip family inet")
|
||||
execute(f"ipset -! create {NOTRACK_IPV6_IPSET} hash:ip family inet6")
|
||||
|
||||
# Create IPv4 rules that disable connection tracking
|
||||
try:
|
||||
execute(f"iptables -t raw -n -L PREROUTING | grep -q 'match-set {NOTRACK_IPV4_IPSET} dst NOTRACK'")
|
||||
except:
|
||||
execute(f"iptables -t raw -A PREROUTING -m set --match-set {NOTRACK_IPV4_IPSET} dst -j NOTRACK")
|
||||
try:
|
||||
execute(f"iptables -t raw -n -L PREROUTING | grep -q 'match-set {NOTRACK_IPV4_IPSET} src NOTRACK'")
|
||||
except:
|
||||
execute(f"iptables -t raw -A PREROUTING -m set --match-set {NOTRACK_IPV4_IPSET} src -j NOTRACK")
|
||||
|
||||
# Create IPv6 rules that disable connection tracking
|
||||
try:
|
||||
execute(f"ip6tables -t raw -n -L PREROUTING | grep -q 'match-set {NOTRACK_IPV6_IPSET} dst NOTRACK'")
|
||||
except:
|
||||
execute(f"ip6tables -t raw -A PREROUTING -m set --match-set {NOTRACK_IPV6_IPSET} dst -j NOTRACK")
|
||||
try:
|
||||
execute(f"ip6tables -t raw -n -L PREROUTING | grep -q 'match-set {NOTRACK_IPV6_IPSET} src NOTRACK'")
|
||||
except:
|
||||
execute(f"ip6tables -t raw -A PREROUTING -m set --match-set {NOTRACK_IPV6_IPSET} src -j NOTRACK")
|
||||
|
||||
try:
|
||||
refs = int(execute("""iptables -n -L %s | awk '/%s(.*)references/ {gsub(/\(/, "") ;print $3}'""" % (brfw,brfw)).strip())
|
||||
refs_in = int(execute("""iptables -n -L %s-IN | awk '/%s-IN(.*)references/ {gsub(/\(/, "") ;print $3}'""" % (brfw,brfw)).strip())
|
||||
|
|
@ -1338,7 +1396,6 @@ def add_fw_framework(brname):
|
|||
execute("iptables -I FORWARD -o " + brname + " -j DROP")
|
||||
execute("iptables -I FORWARD -i " + brname + " -m physdev --physdev-is-bridged -j " + brfw)
|
||||
execute("iptables -I FORWARD -o " + brname + " -m physdev --physdev-is-bridged -j " + brfw)
|
||||
execute("iptables -A " + brfw + " -m state --state RELATED,ESTABLISHED -j ACCEPT")
|
||||
execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-is-in -j " + brfwin)
|
||||
execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-is-out -j " + brfwout)
|
||||
execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-out " + physdev + " -j ACCEPT")
|
||||
|
|
@ -1348,7 +1405,6 @@ def add_fw_framework(brname):
|
|||
execute('ip6tables -I FORWARD -o ' + brname + ' -j DROP')
|
||||
execute('ip6tables -I FORWARD -i ' + brname + ' -m physdev --physdev-is-bridged -j ' + brfw)
|
||||
execute('ip6tables -I FORWARD -o ' + brname + ' -m physdev --physdev-is-bridged -j ' + brfw)
|
||||
execute('ip6tables -A ' + brfw + ' -m state --state RELATED,ESTABLISHED -j ACCEPT')
|
||||
execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-is-in -j ' + brfwin)
|
||||
execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-is-out -j ' + brfwout)
|
||||
execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-out ' + physdev + ' -j ACCEPT')
|
||||
|
|
@ -1443,7 +1499,6 @@ def verify_iptables_rules_for_bridge(brname):
|
|||
expected_rules = []
|
||||
expected_rules.append("-A FORWARD -o %s -m physdev --physdev-is-bridged -j %s" % (brname, brfw))
|
||||
expected_rules.append("-A FORWARD -i %s -m physdev --physdev-is-bridged -j %s" % (brname, brfw))
|
||||
expected_rules.append("-A %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % (brfw))
|
||||
expected_rules.append("-A %s -m physdev --physdev-is-in --physdev-is-bridged -j %s" % (brfw, brfwin))
|
||||
expected_rules.append("-A %s -m physdev --physdev-is-out --physdev-is-bridged -j %s" % (brfw, brfwout))
|
||||
phydev = get_bridge_physdev(brname)
|
||||
|
|
@ -1464,16 +1519,19 @@ def verify_default_iptables_rules_for_vm(vm_name, vm_id, vm_ips, vm_ip6, vm_mac,
|
|||
expected_rules = []
|
||||
expected_rules.append("-A %s -m physdev --physdev-in %s --physdev-is-bridged -j %s" % (brfwin, vif, vm_def))
|
||||
expected_rules.append("-A %s -m physdev --physdev-out %s --physdev-is-bridged -j %s" % (brfwout, vif, vm_def))
|
||||
expected_rules.append("-A %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % (vm_def))
|
||||
expected_rules.append("-A %s -p udp -m physdev --physdev-in %s --physdev-is-bridged -m udp --sport 68 --dport 67 -j ACCEPT" % (vm_def, vif))
|
||||
expected_rules.append("-A %s -p udp -m physdev --physdev-out %s --physdev-is-bridged -m udp --sport 67 --dport 68 -j ACCEPT" % (vm_def, vif))
|
||||
expected_rules.append("-A %s -p udp -m physdev --physdev-in %s --physdev-is-bridged -m udp --sport 67 -j DROP" % (vm_def, vif))
|
||||
expected_rules.append("-A %s -m physdev --physdev-in %s --physdev-is-bridged -m set ! --match-set %s src -j DROP" % (vm_def, vif, vm_name))
|
||||
expected_rules.append("-A %s -m physdev --physdev-out %s --physdev-is-bridged -m set ! --match-set %s dst -j DROP" % (vm_def, vif, vm_name))
|
||||
expected_rules.append("-A %s -p udp -m physdev --physdev-in %s --physdev-is-bridged -m set --match-set %s src -m udp --dport 53 -j RETURN" % (vm_def, vif, vm_name))
|
||||
expected_rules.append("-A %s -p tcp -m physdev --physdev-in %s --physdev-is-bridged -m set --match-set %s src -m tcp --dport 53 -j RETURN" % (vm_def, vif, vm_name))
|
||||
expected_rules.append("-A %s -p udp -m physdev --physdev-in %s --physdev-is-bridged -m set --match-set %s src -m udp --dport 53 -j ACCEPT" % (vm_def, vif, vm_name))
|
||||
expected_rules.append("-A %s -p tcp -m physdev --physdev-in %s --physdev-is-bridged -m set --match-set %s src -m tcp --dport 53 -j ACCEPT" % (vm_def, vif, vm_name))
|
||||
expected_rules.append("-A %s -m physdev --physdev-in %s --physdev-is-bridged -m set --match-set %s src -j %s" % (vm_def, vif, vm_name, vmchain_egress))
|
||||
expected_rules.append("-A %s -m physdev --physdev-out %s --physdev-is-bridged -j %s" % (vm_def, vif, vmchain))
|
||||
expected_rules.append("-A %s -m physdev --physdev-in %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % (vm_def, vif))
|
||||
expected_rules.append("-A %s -m physdev --physdev-out %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % (vm_def, vif))
|
||||
expected_rules.append("-A %s -m physdev --physdev-in %s -j DROP" % (vm_def, vif))
|
||||
expected_rules.append("-A %s -m physdev --physdev-out %s -j DROP" % (vm_def, vif))
|
||||
|
||||
rules = execute("iptables-save |grep -E \"%s|%s\" |grep -v \"^:\"" % (vm_name, vm_def)).split('\n')
|
||||
|
||||
|
|
@ -1625,7 +1683,7 @@ if __name__ == '__main__':
|
|||
elif cmd == "default_network_rules" and not args.check:
|
||||
default_network_rules(args.vmName, args.vmID, args.vmIP, args.vmIP6, args.vmMAC, args.vif, args.brname, args.nicSecIps, args.isFirstNic)
|
||||
elif cmd == "destroy_network_rules_for_vm":
|
||||
if args.vmIP is None:
|
||||
if not args.vmIP:
|
||||
destroy_network_rules_for_vm(args.vmName, args.vif)
|
||||
else:
|
||||
destroy_network_rules_for_nic(args.vmName, args.vmIP, args.vmMAC, args.vif, args.nicSecIps)
|
||||
|
|
|
|||
Loading…
Reference in New Issue