Skip to content

Commit 1064cd5

Browse files
committed
[multi-asic] Enhanced iptable default rules (#6765)
What I did:- For multi-asic platforms added iptable v4 rule to communicate on docker bridge ip For multi-asic platforms extend iptable v4 rule for iptable v6 also For multi-asic program made all internal rules applicable for all protocols (not filter based on tcp/udp). This is done to be consistent same as local host rule For multi-asic platforms made nat rule (to forward traffic from namespace to host) generic for all protocols and also use Source IP if present for matching
1 parent 5822b42 commit 1064cd5

File tree

1 file changed

+91
-48
lines changed

1 file changed

+91
-48
lines changed

files/image_config/caclmgrd/caclmgrd

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,18 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
6363
ACL_SERVICES = {
6464
"NTP": {
6565
"ip_protocols": ["udp"],
66-
"dst_ports": ["123"]
66+
"dst_ports": ["123"],
67+
"multi_asic_ns_to_host_fwd":False
6768
},
6869
"SNMP": {
6970
"ip_protocols": ["tcp", "udp"],
70-
"dst_ports": ["161"]
71+
"dst_ports": ["161"],
72+
"multi_asic_ns_to_host_fwd":True
7173
},
7274
"SSH": {
7375
"ip_protocols": ["tcp"],
74-
"dst_ports": ["22"]
76+
"dst_ports": ["22"],
77+
"multi_asic_ns_to_host_fwd":True
7578
}
7679
}
7780

@@ -222,54 +225,81 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
222225
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
223226
(self.namespace_docker_mgmt_ip[namespace], self.namespace_docker_mgmt_ip[namespace]))
224227

225-
# For namespace docker allow all tcp/udp traffic from host docker bridge to its eth0 management ip
226-
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p tcp -s {} -d {} -j ACCEPT".format
228+
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s {} -d {} -j ACCEPT".format
229+
(self.namespace_docker_mgmt_ipv6[namespace], self.namespace_docker_mgmt_ipv6[namespace]))
230+
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
227231
(self.namespace_mgmt_ip, self.namespace_docker_mgmt_ip[namespace]))
228232

229-
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp -s {} -d {} -j ACCEPT".format
230-
(self.namespace_mgmt_ip, self.namespace_docker_mgmt_ip[namespace]))
233+
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s {} -d {} -j ACCEPT".format
234+
(self.namespace_mgmt_ipv6, self.namespace_docker_mgmt_ipv6[namespace]))
235+
231236
else:
237+
238+
# Also host namespace communication on docker bridge on multi-asic.
239+
if self.namespace_docker_mgmt_ip:
240+
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
241+
(self.namespace_mgmt_ip, self.namespace_mgmt_ip))
242+
243+
if self.namespace_docker_mgmt_ipv6:
244+
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s {} -d {} -j ACCEPT".format
245+
(self.namespace_mgmt_ipv6, self.namespace_mgmt_ipv6))
232246
# In host allow all tcp/udp traffic from namespace docker eth0 management ip to host docker bridge
233247
for docker_mgmt_ip in self.namespace_docker_mgmt_ip.values():
234-
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p tcp -s {} -d {} -j ACCEPT".format
248+
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
235249
(docker_mgmt_ip, self.namespace_mgmt_ip))
236250

237-
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp -s {} -d {} -j ACCEPT".format
238-
(docker_mgmt_ip, self.namespace_mgmt_ip))
251+
for docker_mgmt_ipv6 in list(self.namespace_docker_mgmt_ipv6.values()):
252+
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s {} -d {} -j ACCEPT".format
253+
(docker_mgmt_ipv6, self.namespace_mgmt_ipv6))
254+
239255
return allow_internal_docker_ip_cmds
240256

241-
def generate_fwd_snmp_traffic_from_namespace_to_host_commands(self, namespace):
257+
def generate_fwd_traffic_from_namespace_to_host_commands(self, namespace, acl_source_ip_map):
242258
"""
243-
The below SNAT and DNAT rules are added in asic namespace in multi-ASIC platforms. It helps to forward the SNMP request coming
244-
in through the front panel interfaces created/present in the asic namespace to the SNMP Agent running in SNMP container in
245-
linux host network namespace. The external IP addresses are NATed to the internal docker IP addresses for the SNMP Agent to respond.
259+
The below SNAT and DNAT rules are added in asic namespace in multi-ASIC platforms. It helps to forward request coming
260+
in through the front panel interfaces created/present in the asic namespace for the servie running in linux host network namespace.
261+
The external IP addresses are NATed to the internal docker IP addresses for the Host service to respond.
246262
"""
247-
fwd_snmp_traffic_from_namespace_to_host_cmds = []
248263

249-
if namespace:
250-
# IPv4 rules
251-
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -X")
252-
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -F")
253-
254-
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
255-
"iptables -t nat -A PREROUTING -p udp --dport {} -j DNAT --to-destination {}".format
256-
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_mgmt_ip))
257-
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
258-
"iptables -t nat -A POSTROUTING -p udp --dport {} -j SNAT --to-source {}".format
259-
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_docker_mgmt_ip[namespace]))
260-
261-
# IPv6 rules
262-
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -X")
263-
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -F")
264-
265-
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
266-
"ip6tables -t nat -A PREROUTING -p udp --dport {} -j DNAT --to-destination {}".format
267-
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_mgmt_ipv6))
268-
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
269-
"ip6tables -t nat -A POSTROUTING -p udp --dport {} -j SNAT --to-source {}".format
270-
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_docker_mgmt_ipv6[namespace]))
271-
272-
return fwd_snmp_traffic_from_namespace_to_host_cmds
264+
if not namespace:
265+
return []
266+
267+
fwd_traffic_from_namespace_to_host_cmds = []
268+
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -X")
269+
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -F")
270+
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -X")
271+
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -F")
272+
273+
for acl_service in self.ACL_SERVICES:
274+
if self.ACL_SERVICES[acl_service]["multi_asic_ns_to_host_fwd"]:
275+
# Get the Source IP Set if exists else use default source ip prefix
276+
nat_source_ipv4_set = acl_source_ip_map[acl_service]["ipv4"] if acl_source_ip_map and acl_source_ip_map[acl_service]["ipv4"] else { "0.0.0.0/0" }
277+
nat_source_ipv6_set = acl_source_ip_map[acl_service]["ipv6"] if acl_source_ip_map and acl_source_ip_map[acl_service]["ipv6"] else { "::/0" }
278+
279+
for ip_protocol in self.ACL_SERVICES[acl_service]["ip_protocols"]:
280+
for dst_port in self.ACL_SERVICES[acl_service]["dst_ports"]:
281+
for ipv4_src_ip in nat_source_ipv4_set:
282+
# IPv4 rules
283+
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
284+
"iptables -t nat -A PREROUTING -p {} -s {} --dport {} -j DNAT --to-destination {}".format
285+
(ip_protocol, ipv4_src_ip, dst_port,
286+
self.namespace_mgmt_ip))
287+
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
288+
"iptables -t nat -A POSTROUTING -p {} -s {} --dport {} -j SNAT --to-source {}".format
289+
(ip_protocol, ipv4_src_ip, dst_port,
290+
self.namespace_docker_mgmt_ip[namespace]))
291+
for ipv6_src_ip in nat_source_ipv6_set:
292+
# IPv6 rules
293+
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
294+
"ip6tables -t nat -A PREROUTING -p {} -s {} --dport {} -j DNAT --to-destination {}".format
295+
(ip_protocol, ipv6_src_ip, dst_port,
296+
self.namespace_mgmt_ipv6))
297+
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
298+
"ip6tables -t nat -A POSTROUTING -p {} -s {} --dport {} -j SNAT --to-source {}".format
299+
(ip_protocol,ipv6_src_ip, dst_port,
300+
self.namespace_docker_mgmt_ipv6[namespace]))
301+
302+
return fwd_traffic_from_namespace_to_host_cmds
273303

274304
def is_rule_ipv4(self, rule_props):
275305
if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or
@@ -294,6 +324,7 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
294324
A list of strings, each string is an iptables shell command
295325
"""
296326
iptables_cmds = []
327+
service_to_source_ip_map = {}
297328

298329
# First, add iptables commands to set default policies to accept all
299330
# traffic. In case we are connected remotely, the connection will not
@@ -432,7 +463,8 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
432463
self.log_warning("Unable to determine if ACL table '{}' contains IPv4 or IPv6 rules. Skipping table..."
433464
.format(table_name))
434465
continue
435-
466+
ipv4_src_ip_set = set()
467+
ipv6_src_ip_set = set()
436468
# For each ACL rule in this table (in descending order of priority)
437469
for priority in sorted(acl_rules.iterkeys(), reverse=True):
438470
rule_props = acl_rules[priority]
@@ -449,8 +481,12 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
449481

450482
if "SRC_IPV6" in rule_props and rule_props["SRC_IPV6"]:
451483
rule_cmd += " -s {}".format(rule_props["SRC_IPV6"])
484+
if rule_props["PACKET_ACTION"] == "ACCEPT":
485+
ipv6_src_ip_set.add(rule_props["SRC_IPV6"])
452486
elif "SRC_IP" in rule_props and rule_props["SRC_IP"]:
453487
rule_cmd += " -s {}".format(rule_props["SRC_IP"])
488+
if rule_props["PACKET_ACTION"] == "ACCEPT":
489+
ipv4_src_ip_set.add(rule_props["SRC_IP"])
454490

455491
rule_cmd += " --dport {}".format(dst_port)
456492

@@ -470,6 +506,9 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
470506
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + rule_cmd)
471507
num_ctrl_plane_acl_rules += 1
472508

509+
510+
service_to_source_ip_map.update({ acl_service:{ "ipv4":ipv4_src_ip_set, "ipv6":ipv6_src_ip_set } })
511+
473512
# Add iptables commands to block ip2me traffic
474513
iptables_cmds += self.generate_block_ip2me_traffic_iptables_commands(namespace)
475514

@@ -484,28 +523,33 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
484523
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -j DROP")
485524
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -j DROP")
486525

487-
return iptables_cmds
526+
return iptables_cmds, service_to_source_ip_map
488527

489528
def update_control_plane_acls(self, namespace):
490529
"""
491530
Convenience wrapper which retrieves current ACL tables and rules from
492531
Config DB, translates control plane ACLs into a list of iptables
493532
commands and runs them.
494533
"""
495-
iptables_cmds = self.get_acl_rules_and_translate_to_iptables_commands(namespace)
534+
iptables_cmds, service_to_source_ip_map = self.get_acl_rules_and_translate_to_iptables_commands(namespace)
496535
self.log_info("Issuing the following iptables commands:")
497536
for cmd in iptables_cmds:
498537
self.log_info(" " + cmd)
499538

500539
self.run_commands(iptables_cmds)
501540

502-
def update_control_plane_nat_acls(self, namespace):
541+
self.update_control_plane_nat_acls(namespace, service_to_source_ip_map)
542+
543+
def update_control_plane_nat_acls(self, namespace, service_to_source_ip_map):
503544
"""
504-
Convenience wrapper which programs the NAT rules for allowing the
505-
snmp traffic coming on the front panel interface
545+
Convenience wrapper for multi-asic platforms
546+
which programs the NAT rules for redirecting the
547+
traffic coming on the front panel interface map to namespace
548+
to the host.
506549
"""
507-
# Add iptables commands to allow front panel snmp traffic
508-
iptables_cmds = self.generate_fwd_snmp_traffic_from_namespace_to_host_commands(namespace)
550+
# Add iptables commands to allow front panel traffic
551+
iptables_cmds = self.generate_fwd_traffic_from_namespace_to_host_commands(namespace, service_to_source_ip_map)
552+
509553
self.log_info("Issuing the following iptables commands:")
510554
for cmd in iptables_cmds:
511555
self.log_info(" " + cmd)
@@ -571,7 +615,6 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
571615
for namespace in self.config_db_map.keys():
572616
# Unconditionally update control plane ACLs once at start on given namespace
573617
self.update_control_plane_acls(namespace)
574-
self.update_control_plane_nat_acls(namespace)
575618
# Connect to Config DB of given namespace
576619
acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace)
577620
# Subscribe to notifications when ACL tables changes

0 commit comments

Comments
 (0)