[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
This commit is contained in:
parent
a9af05d1a7
commit
82093fd669
@ -63,19 +63,23 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
ACL_SERVICES = {
|
ACL_SERVICES = {
|
||||||
"NTP": {
|
"NTP": {
|
||||||
"ip_protocols": ["udp"],
|
"ip_protocols": ["udp"],
|
||||||
"dst_ports": ["123"]
|
"dst_ports": ["123"],
|
||||||
|
"multi_asic_ns_to_host_fwd":False
|
||||||
},
|
},
|
||||||
"SNMP": {
|
"SNMP": {
|
||||||
"ip_protocols": ["tcp", "udp"],
|
"ip_protocols": ["tcp", "udp"],
|
||||||
"dst_ports": ["161"]
|
"dst_ports": ["161"],
|
||||||
|
"multi_asic_ns_to_host_fwd":True
|
||||||
},
|
},
|
||||||
"SSH": {
|
"SSH": {
|
||||||
"ip_protocols": ["tcp"],
|
"ip_protocols": ["tcp"],
|
||||||
"dst_ports": ["22"]
|
"dst_ports": ["22"],
|
||||||
|
"multi_asic_ns_to_host_fwd":True
|
||||||
},
|
},
|
||||||
"ANY": {
|
"ANY": {
|
||||||
"ip_protocols": ["any"],
|
"ip_protocols": ["any"],
|
||||||
"dst_ports": ["0"]
|
"dst_ports": ["0"],
|
||||||
|
"multi_asic_ns_to_host_fwd":False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,54 +230,81 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
|
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
|
||||||
(self.namespace_docker_mgmt_ip[namespace], self.namespace_docker_mgmt_ip[namespace]))
|
(self.namespace_docker_mgmt_ip[namespace], self.namespace_docker_mgmt_ip[namespace]))
|
||||||
|
|
||||||
# For namespace docker allow all tcp/udp traffic from host docker bridge to its eth0 management ip
|
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s {} -d {} -j ACCEPT".format
|
||||||
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p tcp -s {} -d {} -j ACCEPT".format
|
(self.namespace_docker_mgmt_ipv6[namespace], self.namespace_docker_mgmt_ipv6[namespace]))
|
||||||
|
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
|
||||||
(self.namespace_mgmt_ip, self.namespace_docker_mgmt_ip[namespace]))
|
(self.namespace_mgmt_ip, self.namespace_docker_mgmt_ip[namespace]))
|
||||||
|
|
||||||
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp -s {} -d {} -j ACCEPT".format
|
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s {} -d {} -j ACCEPT".format
|
||||||
(self.namespace_mgmt_ip, self.namespace_docker_mgmt_ip[namespace]))
|
(self.namespace_mgmt_ipv6, self.namespace_docker_mgmt_ipv6[namespace]))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
# Also host namespace communication on docker bridge on multi-asic.
|
||||||
|
if self.namespace_docker_mgmt_ip:
|
||||||
|
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
|
||||||
|
(self.namespace_mgmt_ip, self.namespace_mgmt_ip))
|
||||||
|
|
||||||
|
if self.namespace_docker_mgmt_ipv6:
|
||||||
|
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s {} -d {} -j ACCEPT".format
|
||||||
|
(self.namespace_mgmt_ipv6, self.namespace_mgmt_ipv6))
|
||||||
# In host allow all tcp/udp traffic from namespace docker eth0 management ip to host docker bridge
|
# In host allow all tcp/udp traffic from namespace docker eth0 management ip to host docker bridge
|
||||||
for docker_mgmt_ip in list(self.namespace_docker_mgmt_ip.values()):
|
for docker_mgmt_ip in list(self.namespace_docker_mgmt_ip.values()):
|
||||||
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p tcp -s {} -d {} -j ACCEPT".format
|
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s {} -d {} -j ACCEPT".format
|
||||||
(docker_mgmt_ip, self.namespace_mgmt_ip))
|
(docker_mgmt_ip, self.namespace_mgmt_ip))
|
||||||
|
|
||||||
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp -s {} -d {} -j ACCEPT".format
|
for docker_mgmt_ipv6 in list(self.namespace_docker_mgmt_ipv6.values()):
|
||||||
(docker_mgmt_ip, self.namespace_mgmt_ip))
|
allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s {} -d {} -j ACCEPT".format
|
||||||
|
(docker_mgmt_ipv6, self.namespace_mgmt_ipv6))
|
||||||
|
|
||||||
return allow_internal_docker_ip_cmds
|
return allow_internal_docker_ip_cmds
|
||||||
|
|
||||||
def generate_fwd_snmp_traffic_from_namespace_to_host_commands(self, namespace):
|
def generate_fwd_traffic_from_namespace_to_host_commands(self, namespace, acl_source_ip_map):
|
||||||
"""
|
"""
|
||||||
The below SNAT and DNAT rules are added in asic namespace in multi-ASIC platforms. It helps to forward the SNMP request coming
|
The below SNAT and DNAT rules are added in asic namespace in multi-ASIC platforms. It helps to forward request coming
|
||||||
in through the front panel interfaces created/present in the asic namespace to the SNMP Agent running in SNMP container in
|
in through the front panel interfaces created/present in the asic namespace for the servie running in linux host network namespace.
|
||||||
linux host network namespace. The external IP addresses are NATed to the internal docker IP addresses for the SNMP Agent to respond.
|
The external IP addresses are NATed to the internal docker IP addresses for the Host service to respond.
|
||||||
"""
|
"""
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds = []
|
|
||||||
|
|
||||||
if namespace:
|
if not namespace:
|
||||||
|
return []
|
||||||
|
|
||||||
|
fwd_traffic_from_namespace_to_host_cmds = []
|
||||||
|
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -X")
|
||||||
|
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -F")
|
||||||
|
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -X")
|
||||||
|
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -F")
|
||||||
|
|
||||||
|
for acl_service in self.ACL_SERVICES:
|
||||||
|
if self.ACL_SERVICES[acl_service]["multi_asic_ns_to_host_fwd"]:
|
||||||
|
# Get the Source IP Set if exists else use default source ip prefix
|
||||||
|
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" }
|
||||||
|
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" }
|
||||||
|
|
||||||
|
for ip_protocol in self.ACL_SERVICES[acl_service]["ip_protocols"]:
|
||||||
|
for dst_port in self.ACL_SERVICES[acl_service]["dst_ports"]:
|
||||||
|
for ipv4_src_ip in nat_source_ipv4_set:
|
||||||
# IPv4 rules
|
# IPv4 rules
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -X")
|
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -F")
|
"iptables -t nat -A PREROUTING -p {} -s {} --dport {} -j DNAT --to-destination {}".format
|
||||||
|
(ip_protocol, ipv4_src_ip, dst_port,
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
self.namespace_mgmt_ip))
|
||||||
"iptables -t nat -A PREROUTING -p udp --dport {} -j DNAT --to-destination {}".format
|
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
||||||
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_mgmt_ip))
|
"iptables -t nat -A POSTROUTING -p {} -s {} --dport {} -j SNAT --to-source {}".format
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
(ip_protocol, ipv4_src_ip, dst_port,
|
||||||
"iptables -t nat -A POSTROUTING -p udp --dport {} -j SNAT --to-source {}".format
|
self.namespace_docker_mgmt_ip[namespace]))
|
||||||
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_docker_mgmt_ip[namespace]))
|
for ipv6_src_ip in nat_source_ipv6_set:
|
||||||
|
|
||||||
# IPv6 rules
|
# IPv6 rules
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -X")
|
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -F")
|
"ip6tables -t nat -A PREROUTING -p {} -s {} --dport {} -j DNAT --to-destination {}".format
|
||||||
|
(ip_protocol, ipv6_src_ip, dst_port,
|
||||||
|
self.namespace_mgmt_ipv6))
|
||||||
|
fwd_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
||||||
|
"ip6tables -t nat -A POSTROUTING -p {} -s {} --dport {} -j SNAT --to-source {}".format
|
||||||
|
(ip_protocol,ipv6_src_ip, dst_port,
|
||||||
|
self.namespace_docker_mgmt_ipv6[namespace]))
|
||||||
|
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
return fwd_traffic_from_namespace_to_host_cmds
|
||||||
"ip6tables -t nat -A PREROUTING -p udp --dport {} -j DNAT --to-destination {}".format
|
|
||||||
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_mgmt_ipv6))
|
|
||||||
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
|
||||||
"ip6tables -t nat -A POSTROUTING -p udp --dport {} -j SNAT --to-source {}".format
|
|
||||||
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_docker_mgmt_ipv6[namespace]))
|
|
||||||
|
|
||||||
return fwd_snmp_traffic_from_namespace_to_host_cmds
|
|
||||||
|
|
||||||
def is_rule_ipv4(self, rule_props):
|
def is_rule_ipv4(self, rule_props):
|
||||||
if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or
|
if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or
|
||||||
@ -298,6 +329,7 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
A list of strings, each string is an iptables shell command
|
A list of strings, each string is an iptables shell command
|
||||||
"""
|
"""
|
||||||
iptables_cmds = []
|
iptables_cmds = []
|
||||||
|
service_to_source_ip_map = {}
|
||||||
|
|
||||||
# First, add iptables commands to set default policies to accept all
|
# First, add iptables commands to set default policies to accept all
|
||||||
# traffic. In case we are connected remotely, the connection will not
|
# traffic. In case we are connected remotely, the connection will not
|
||||||
@ -436,7 +468,8 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
self.log_warning("Unable to determine if ACL table '{}' contains IPv4 or IPv6 rules. Skipping table..."
|
self.log_warning("Unable to determine if ACL table '{}' contains IPv4 or IPv6 rules. Skipping table..."
|
||||||
.format(table_name))
|
.format(table_name))
|
||||||
continue
|
continue
|
||||||
|
ipv4_src_ip_set = set()
|
||||||
|
ipv6_src_ip_set = set()
|
||||||
# For each ACL rule in this table (in descending order of priority)
|
# For each ACL rule in this table (in descending order of priority)
|
||||||
for priority in sorted(iter(acl_rules.keys()), reverse=True):
|
for priority in sorted(iter(acl_rules.keys()), reverse=True):
|
||||||
rule_props = acl_rules[priority]
|
rule_props = acl_rules[priority]
|
||||||
@ -456,8 +489,12 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
|
|
||||||
if "SRC_IPV6" in rule_props and rule_props["SRC_IPV6"]:
|
if "SRC_IPV6" in rule_props and rule_props["SRC_IPV6"]:
|
||||||
rule_cmd += " -s {}".format(rule_props["SRC_IPV6"])
|
rule_cmd += " -s {}".format(rule_props["SRC_IPV6"])
|
||||||
|
if rule_props["PACKET_ACTION"] == "ACCEPT":
|
||||||
|
ipv6_src_ip_set.add(rule_props["SRC_IPV6"])
|
||||||
elif "SRC_IP" in rule_props and rule_props["SRC_IP"]:
|
elif "SRC_IP" in rule_props and rule_props["SRC_IP"]:
|
||||||
rule_cmd += " -s {}".format(rule_props["SRC_IP"])
|
rule_cmd += " -s {}".format(rule_props["SRC_IP"])
|
||||||
|
if rule_props["PACKET_ACTION"] == "ACCEPT":
|
||||||
|
ipv4_src_ip_set.add(rule_props["SRC_IP"])
|
||||||
|
|
||||||
# Destination port 0 is reserved/unused port, so, using it to apply the rule to all ports.
|
# Destination port 0 is reserved/unused port, so, using it to apply the rule to all ports.
|
||||||
if dst_port != "0":
|
if dst_port != "0":
|
||||||
@ -479,6 +516,9 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + rule_cmd)
|
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + rule_cmd)
|
||||||
num_ctrl_plane_acl_rules += 1
|
num_ctrl_plane_acl_rules += 1
|
||||||
|
|
||||||
|
|
||||||
|
service_to_source_ip_map.update({ acl_service:{ "ipv4":ipv4_src_ip_set, "ipv6":ipv6_src_ip_set } })
|
||||||
|
|
||||||
# Add iptables commands to block ip2me traffic
|
# Add iptables commands to block ip2me traffic
|
||||||
iptables_cmds += self.generate_block_ip2me_traffic_iptables_commands(namespace)
|
iptables_cmds += self.generate_block_ip2me_traffic_iptables_commands(namespace)
|
||||||
|
|
||||||
@ -493,7 +533,7 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -j DROP")
|
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -j DROP")
|
||||||
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -j DROP")
|
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -j DROP")
|
||||||
|
|
||||||
return iptables_cmds
|
return iptables_cmds, service_to_source_ip_map
|
||||||
|
|
||||||
def update_control_plane_acls(self, namespace):
|
def update_control_plane_acls(self, namespace):
|
||||||
"""
|
"""
|
||||||
@ -501,20 +541,25 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
Config DB, translates control plane ACLs into a list of iptables
|
Config DB, translates control plane ACLs into a list of iptables
|
||||||
commands and runs them.
|
commands and runs them.
|
||||||
"""
|
"""
|
||||||
iptables_cmds = self.get_acl_rules_and_translate_to_iptables_commands(namespace)
|
iptables_cmds, service_to_source_ip_map = self.get_acl_rules_and_translate_to_iptables_commands(namespace)
|
||||||
self.log_info("Issuing the following iptables commands:")
|
self.log_info("Issuing the following iptables commands:")
|
||||||
for cmd in iptables_cmds:
|
for cmd in iptables_cmds:
|
||||||
self.log_info(" " + cmd)
|
self.log_info(" " + cmd)
|
||||||
|
|
||||||
self.run_commands(iptables_cmds)
|
self.run_commands(iptables_cmds)
|
||||||
|
|
||||||
def update_control_plane_nat_acls(self, namespace):
|
self.update_control_plane_nat_acls(namespace, service_to_source_ip_map)
|
||||||
|
|
||||||
|
def update_control_plane_nat_acls(self, namespace, service_to_source_ip_map):
|
||||||
"""
|
"""
|
||||||
Convenience wrapper which programs the NAT rules for allowing the
|
Convenience wrapper for multi-asic platforms
|
||||||
snmp traffic coming on the front panel interface
|
which programs the NAT rules for redirecting the
|
||||||
|
traffic coming on the front panel interface map to namespace
|
||||||
|
to the host.
|
||||||
"""
|
"""
|
||||||
# Add iptables commands to allow front panel snmp traffic
|
# Add iptables commands to allow front panel traffic
|
||||||
iptables_cmds = self.generate_fwd_snmp_traffic_from_namespace_to_host_commands(namespace)
|
iptables_cmds = self.generate_fwd_traffic_from_namespace_to_host_commands(namespace, service_to_source_ip_map)
|
||||||
|
|
||||||
self.log_info("Issuing the following iptables commands:")
|
self.log_info("Issuing the following iptables commands:")
|
||||||
for cmd in iptables_cmds:
|
for cmd in iptables_cmds:
|
||||||
self.log_info(" " + cmd)
|
self.log_info(" " + cmd)
|
||||||
@ -580,7 +625,6 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
|||||||
for namespace in list(self.config_db_map.keys()):
|
for namespace in list(self.config_db_map.keys()):
|
||||||
# Unconditionally update control plane ACLs once at start on given namespace
|
# Unconditionally update control plane ACLs once at start on given namespace
|
||||||
self.update_control_plane_acls(namespace)
|
self.update_control_plane_acls(namespace)
|
||||||
self.update_control_plane_nat_acls(namespace)
|
|
||||||
# Connect to Config DB of given namespace
|
# Connect to Config DB of given namespace
|
||||||
acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace)
|
acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace)
|
||||||
# Subscribe to notifications when ACL tables changes
|
# Subscribe to notifications when ACL tables changes
|
||||||
|
Loading…
Reference in New Issue
Block a user