This is an automated email from the ASF dual-hosted git repository.
widodh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new bb5da0e49cf security groups: conntrack only if needed (#10594)
bb5da0e49cf is described below
commit bb5da0e49cf6ebaa9b299f617adf3ea0e357ace0
Author: Phsm Qwerty <[email protected]>
AuthorDate: Thu Dec 18 11:49:41 2025 +0100
security groups: conntrack only if needed (#10594)
The conntrack is disabled if the security group allows all traffic.
Also, refactored the code a little.
---
scripts/vm/network/security_group.py | 232 ++++++++++++++++++++++-------------
1 file changed, 145 insertions(+), 87 deletions(-)
diff --git a/scripts/vm/network/security_group.py
b/scripts/vm/network/security_group.py
index e7441202032..e3ca51b0927 100755
--- a/scripts/vm/network/security_group.py
+++ b/scripts/vm/network/security_group.py
@@ -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)