From 1094086d4a903c462458c153cdf06a0ae272e653 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Wed, 22 Jul 2020 16:46:51 -0700 Subject: [PATCH 01/13] Support for Control Plane ACL's for Multi-asic Platforms. Following changes were done: 1) Moved from using blocking listen() on Config DB to the select() model via python-swsscommon since we have to wait on event from multiple config db's 2) Since python-swsscommon is not available on host added libswsscommon and python-swsscommon and dependent packages in the base image (host enviroment) 3) Made iptables programmed in all namespace using ip netns exec Signed-off-by: Abhishek Dosi --- .../build_templates/sonic_debian_extension.j2 | 6 + files/image_config/caclmgrd/caclmgrd | 161 ++++++++++-------- slave.mk | 5 +- 3 files changed, 99 insertions(+), 73 deletions(-) diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index 1732580b5ac8..3985c771cc6a 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -214,6 +214,12 @@ if [[ $CONFIGURED_ARCH == amd64 ]]; then sudo DEBIAN_FRONTEND=noninteractive dpkg --root=$FILESYSTEM_ROOT -i $debs_path/kdump-tools_*.deb || \ sudo LANG=C DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true chroot $FILESYSTEM_ROOT apt-get -q --no-install-suggests --no-install-recommends --force-no install fi +#Install python-swss-common package and all it's dependent package +{% if python_swss_debs.strip() -%} +{% for deb in python_swss_debs.strip().split(' ') -%} +sudo dpkg --root=$FILESYSTEM_ROOT -i {{deb}} || sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f +{% endfor %} +{% endif %} # Install custom-built monit package and SONiC configuration files sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/monit_*.deb || \ diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index 927675524ea3..0e93345358c2 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -16,7 +16,9 @@ try: import subprocess import sys import syslog - from swsssdk import ConfigDBConnector + import sonic_device_util + from swsscommon import swsscommon + from swsssdk import SonicDBConfig, ConfigDBConnector except ImportError as err: raise ImportError("%s - required module not found" % str(err)) @@ -88,9 +90,17 @@ class ControlPlaneAclManager(object): } def __init__(self): - # Open a handle to the Config database - self.config_db = ConfigDBConnector() - self.config_db.connect() + SonicDBConfig.load_sonic_global_db_config() + self.config_db_map = {} + self.iptable_cmd_prefix = {} + self.config_db_map[''] = ConfigDBConnector(use_unix_socket_path=True, namespace='') + self.config_db_map[''].connect() + self.iptable_cmd_prefix[''] = "" + namespaces = sonic_device_util.get_all_namespaces() + for front_asic_namespaces in namespaces['front_ns']: + self.config_db_map[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) + self.config_db_map[front_asic_namespaces].connect() + self.iptable_cmd_prefix[front_asic_namespaces] = "ip netns exec " + front_asic_namespaces + " " def run_commands(self, commands): """ @@ -133,7 +143,7 @@ class ControlPlaneAclManager(object): tcp_flags_str = tcp_flags_str[:-1] return tcp_flags_str - def generate_block_ip2me_traffic_iptables_commands(self): + def generate_block_ip2me_traffic_iptables_commands(self, namespace): INTERFACE_TABLE_NAME_LIST = [ "LOOPBACK_INTERFACE", "MGMT_INTERFACE", @@ -146,7 +156,7 @@ class ControlPlaneAclManager(object): # Add iptables rules to drop all packets destined for peer-to-peer interface IP addresses for iface_table_name in INTERFACE_TABLE_NAME_LIST: - iface_table = self.config_db.get_table(iface_table_name) + iface_table = self.config_db_map[namespace].get_table(iface_table_name) if iface_table: for key, _ in iface_table.iteritems(): if not _ip_prefix_in_key(key): @@ -160,9 +170,9 @@ class ControlPlaneAclManager(object): ip_addr = next(ip_ntwrk.hosts()) if iface_table_name == "VLAN_INTERFACE" else ip_ntwrk.network_address if isinstance(ip_ntwrk, ipaddress.IPv4Network): - block_ip2me_cmds.append("iptables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen)) + block_ip2me_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) elif isinstance(ip_ntwrk, ipaddress.IPv6Network): - block_ip2me_cmds.append("ip6tables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen)) + block_ip2me_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) else: log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk)) @@ -182,7 +192,7 @@ class ControlPlaneAclManager(object): else: return False - def get_acl_rules_and_translate_to_iptables_commands(self): + def get_acl_rules_and_translate_to_iptables_commands(self, namespace): """ Retrieves current ACL tables and rules from Config DB, translates control plane ACLs into a list of iptables commands that can be run @@ -197,72 +207,72 @@ class ControlPlaneAclManager(object): # First, add iptables commands to set default policies to accept all # traffic. In case we are connected remotely, the connection will not # drop when we flush the current rules - iptables_cmds.append("iptables -P INPUT ACCEPT") - iptables_cmds.append("iptables -P FORWARD ACCEPT") - iptables_cmds.append("iptables -P OUTPUT ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -P INPUT ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -P FORWARD ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -P OUTPUT ACCEPT")) # Add iptables command to flush the current rules - iptables_cmds.append("iptables -F") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -F")) # Add iptables command to delete all non-default chains - iptables_cmds.append("iptables -X") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -X")) # Add same set of commands for ip6tables - iptables_cmds.append("ip6tables -P INPUT ACCEPT") - iptables_cmds.append("ip6tables -P FORWARD ACCEPT") - iptables_cmds.append("ip6tables -P OUTPUT ACCEPT") - iptables_cmds.append("ip6tables -F") - iptables_cmds.append("ip6tables -X") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -P INPUT ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -P FORWARD ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -P OUTPUT ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -F")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -X")) # Add iptables/ip6tables commands to allow all traffic from localhost - iptables_cmds.append("iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -s ::1 -i lo -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -s ::1 -i lo -j ACCEPT")) # Add iptables/ip6tables commands to allow all incoming packets from established # connections or new connections which are related to established connections - iptables_cmds.append("iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT")) # Add iptables/ip6tables commands to allow bidirectional ICMPv4 ping and traceroute # TODO: Support processing ICMPv4 service ACL rules, and remove this blanket acceptance - iptables_cmds.append("iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT") - iptables_cmds.append("iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT") - iptables_cmds.append("iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT") - iptables_cmds.append("iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT")) # Add iptables/ip6tables commands to allow bidirectional ICMPv6 ping and traceroute # TODO: Support processing ICMPv6 service ACL rules, and remove this blanket acceptance - iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT")) # Add iptables/ip6tables commands to allow all incoming Neighbor Discovery Protocol (NDP) NS/NA/RS/RA messages # TODO: Support processing NDP service ACL rules, and remove this blanket acceptance - iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT")) # Add iptables/ip6tables commands to allow all incoming IPv4 DHCP packets - iptables_cmds.append("iptables -A INPUT -p udp --dport 67:68 -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p udp --dport 67:68 -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT")) # Add iptables/ip6tables commands to allow all incoming IPv6 DHCP packets - iptables_cmds.append("iptables -A INPUT -p udp --dport 546:547 -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p udp --dport 546:547 -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT")) # Add iptables/ip6tables commands to allow all incoming BGP traffic # TODO: Determine BGP ACLs based on configured device sessions, and remove this blanket acceptance - iptables_cmds.append("iptables -A INPUT -p tcp --dport 179 -j ACCEPT") - iptables_cmds.append("iptables -A INPUT -p tcp --sport 179 -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p tcp --dport 179 -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p tcp --sport 179 -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p tcp --dport 179 -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p tcp --sport 179 -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p tcp --dport 179 -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p tcp --sport 179 -j ACCEPT")) # Get current ACL tables and rules from Config DB - self._tables_db_info = self.config_db.get_table(self.ACL_TABLE) - self._rules_db_info = self.config_db.get_table(self.ACL_RULE) + self._tables_db_info = self.config_db_map[namespace].get_table(self.ACL_TABLE) + self._rules_db_info = self.config_db_map[namespace].get_table(self.ACL_RULE) num_ctrl_plane_acl_rules = 0 @@ -363,58 +373,65 @@ class ControlPlaneAclManager(object): # Append the packet action as the jump target rule_cmd += " -j {}".format(rule_props["PACKET_ACTION"]) - iptables_cmds.append(rule_cmd) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + rule_cmd) num_ctrl_plane_acl_rules += 1 # Add iptables commands to block ip2me traffic - iptables_cmds += self.generate_block_ip2me_traffic_iptables_commands() + iptables_cmds += self.generate_block_ip2me_traffic_iptables_commands(namespace) # Add iptables/ip6tables commands to allow all incoming packets with TTL of 0 or 1 # This allows the device to respond to tools like tcptraceroute - iptables_cmds.append("iptables -A INPUT -m ttl --ttl-lt 2 -j ACCEPT") - iptables_cmds.append("ip6tables -A INPUT -p tcp -m hl --hl-lt 2 -j ACCEPT") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -m ttl --ttl-lt 2 -j ACCEPT")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p tcp -m hl --hl-lt 2 -j ACCEPT")) # Finally, if the device has control plane ACLs configured, # add iptables/ip6tables commands to drop all other incoming packets if num_ctrl_plane_acl_rules > 0: - iptables_cmds.append("iptables -A INPUT -j DROP") - iptables_cmds.append("iptables -A FORWARD -j DROP") - iptables_cmds.append("ip6tables -A INPUT -j DROP") - iptables_cmds.append("ip6tables -A FORWARD -j DROP") + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -j DROP")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A FORWARD -j DROP")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -j DROP")) + iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A FORWARD -j DROP")) return iptables_cmds - def update_control_plane_acls(self): + def update_control_plane_acls(self, namespace): """ Convenience wrapper which retrieves current ACL tables and rules from Config DB, translates control plane ACLs into a list of iptables commands and runs them. """ - iptables_cmds = self.get_acl_rules_and_translate_to_iptables_commands() - + iptables_cmds = self.get_acl_rules_and_translate_to_iptables_commands(namespace) log_info("Issuing the following iptables commands:") for cmd in iptables_cmds: log_info(" " + cmd) self.run_commands(iptables_cmds) - def notification_handler(self, key, data): - log_info("ACL configuration changed. Updating iptables rules for control plane ACLs...") - self.update_control_plane_acls() - def run(self): - # Unconditionally update control plane ACLs once at start - self.update_control_plane_acls() - - # Subscribe to notifications when ACL tables or rules change - self.config_db.subscribe(self.ACL_TABLE, - lambda table, key, data: self.notification_handler(key, data)) - self.config_db.subscribe(self.ACL_RULE, - lambda table, key, data: self.notification_handler(key, data)) - - # Indefinitely listen for Config DB notifications - self.config_db.listen() - + # Select Time-out for 10 Seconds + SELECT_TIMEOUT_MS = 1000 * 10 + swsscommon.SonicDBConfig.initializeGlobalConfig() + sel = swsscommon.Select() + config_db_subscriber_table_map = {} + for namespace in self.config_db_map.keys(): + # Program first time to setup default ip table rules + self.update_control_plane_acls(namespace) + acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace) + subscribe_acl_table = swsscommon.SubscriberStateTable(acl_db_connector, swsscommon.CFG_ACL_TABLE_TABLE_NAME) + subscribe_acl_rule_table = swsscommon.SubscriberStateTable(acl_db_connector, swsscommon.CFG_ACL_RULE_TABLE_NAME) + sel.addSelectable(subscribe_acl_table) + sel.addSelectable(subscribe_acl_rule_table) + config_db_subscriber_table_map[namespace] = [] + config_db_subscriber_table_map[namespace].append(subscribe_acl_table) + config_db_subscriber_table_map[namespace].append(subscribe_acl_rule_table) + while True: + (state, c) = sel.select(SELECT_TIMEOUT_MS) + if state != swsscommon.Select.OBJECT: + continue + namespace = c.getDbNamespace() + for table in config_db_subscriber_table_map[namespace]: + table.pop() + self.update_control_plane_acls(namespace) # ============================= Functions ============================= diff --git a/slave.mk b/slave.mk index cd22d665cfb8..69db5c04cb45 100644 --- a/slave.mk +++ b/slave.mk @@ -800,7 +800,8 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \ $(KDUMP_TOOLS) \ $(LIBPAM_TACPLUS) \ $(LIBNSS_TACPLUS) \ - $(MONIT)) \ + $(MONIT) \ + $(PYTHON_SWSSCOMMON)) \ $$(addprefix $(TARGET_PATH)/,$$($$*_DOCKERS)) \ $$(addprefix $(FILES_PATH)/,$$($$*_FILES)) \ $(if $(findstring y,$(ENABLE_ZTP)),$(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$(SONIC_ZTP))) \ @@ -845,6 +846,8 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \ export sonic_yang_models_py3_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_YANG_MODELS_PY3))" export sonic_yang_mgmt_py_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_YANG_MGMT_PY))" export multi_instance="false" + export python_swss_debs="$(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$($(LIBSWSSCOMMON)_RDEPENDS))" + export python_swss_debs+=" $(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$(LIBSWSSCOMMON)) $(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$(PYTHON_SWSSCOMMON))" $(foreach docker, $($*_DOCKERS),\ export docker_image="$(docker)" From a1ac6532d2b25ccf2730f659841192e32527ffc1 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Thu, 23 Jul 2020 08:38:50 -0700 Subject: [PATCH 02/13] Address Review Comments Signed-off-by: Abhishek Dosi --- .../build_templates/sonic_debian_extension.j2 | 2 +- files/image_config/caclmgrd/caclmgrd | 94 +++++++++---------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index 3985c771cc6a..2f45f51c33a6 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -214,7 +214,7 @@ if [[ $CONFIGURED_ARCH == amd64 ]]; then sudo DEBIAN_FRONTEND=noninteractive dpkg --root=$FILESYSTEM_ROOT -i $debs_path/kdump-tools_*.deb || \ sudo LANG=C DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true chroot $FILESYSTEM_ROOT apt-get -q --no-install-suggests --no-install-recommends --force-no install fi -#Install python-swss-common package and all it's dependent package +# Install python-swss-common package and all its dependent packages {% if python_swss_debs.strip() -%} {% for deb in python_swss_debs.strip().split(' ') -%} sudo dpkg --root=$FILESYSTEM_ROOT -i {{deb}} || sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index 0e93345358c2..2c253c3c1355 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -92,15 +92,15 @@ class ControlPlaneAclManager(object): def __init__(self): SonicDBConfig.load_sonic_global_db_config() self.config_db_map = {} - self.iptable_cmd_prefix = {} + self.iptables_cmd_prefix = {} self.config_db_map[''] = ConfigDBConnector(use_unix_socket_path=True, namespace='') self.config_db_map[''].connect() - self.iptable_cmd_prefix[''] = "" + self.iptables_cmd_prefix[''] = "" namespaces = sonic_device_util.get_all_namespaces() for front_asic_namespaces in namespaces['front_ns']: self.config_db_map[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) self.config_db_map[front_asic_namespaces].connect() - self.iptable_cmd_prefix[front_asic_namespaces] = "ip netns exec " + front_asic_namespaces + " " + self.iptables_cmd_prefix[front_asic_namespaces] = "ip netns exec " + front_asic_namespaces + " " def run_commands(self, commands): """ @@ -170,9 +170,9 @@ class ControlPlaneAclManager(object): ip_addr = next(ip_ntwrk.hosts()) if iface_table_name == "VLAN_INTERFACE" else ip_ntwrk.network_address if isinstance(ip_ntwrk, ipaddress.IPv4Network): - block_ip2me_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) + block_ip2me_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) elif isinstance(ip_ntwrk, ipaddress.IPv6Network): - block_ip2me_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) + block_ip2me_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) else: log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk)) @@ -207,67 +207,67 @@ class ControlPlaneAclManager(object): # First, add iptables commands to set default policies to accept all # traffic. In case we are connected remotely, the connection will not # drop when we flush the current rules - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -P INPUT ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -P FORWARD ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -P OUTPUT ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -P INPUT ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -P FORWARD ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -P OUTPUT ACCEPT") # Add iptables command to flush the current rules - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -F")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -F") # Add iptables command to delete all non-default chains - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -X")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -X") # Add same set of commands for ip6tables - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -P INPUT ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -P FORWARD ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -P OUTPUT ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -F")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -X")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -P INPUT ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -P FORWARD ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -P OUTPUT ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -F") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -X") # Add iptables/ip6tables commands to allow all traffic from localhost - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -s ::1 -i lo -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -s ::1 -i lo -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming packets from established # connections or new connections which are related to established connections - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") # Add iptables/ip6tables commands to allow bidirectional ICMPv4 ping and traceroute # TODO: Support processing ICMPv4 service ACL rules, and remove this blanket acceptance - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT") # Add iptables/ip6tables commands to allow bidirectional ICMPv6 ping and traceroute # TODO: Support processing ICMPv6 service ACL rules, and remove this blanket acceptance - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming Neighbor Discovery Protocol (NDP) NS/NA/RS/RA messages # TODO: Support processing NDP service ACL rules, and remove this blanket acceptance - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming IPv4 DHCP packets - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming IPv6 DHCP packets - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming BGP traffic # TODO: Determine BGP ACLs based on configured device sessions, and remove this blanket acceptance - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p tcp --dport 179 -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -p tcp --sport 179 -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p tcp --dport 179 -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p tcp --sport 179 -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p tcp --dport 179 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p tcp --sport 179 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p tcp --dport 179 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p tcp --sport 179 -j ACCEPT") # Get current ACL tables and rules from Config DB @@ -373,7 +373,7 @@ class ControlPlaneAclManager(object): # Append the packet action as the jump target rule_cmd += " -j {}".format(rule_props["PACKET_ACTION"]) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + rule_cmd) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + rule_cmd) num_ctrl_plane_acl_rules += 1 # Add iptables commands to block ip2me traffic @@ -381,16 +381,16 @@ class ControlPlaneAclManager(object): # Add iptables/ip6tables commands to allow all incoming packets with TTL of 0 or 1 # This allows the device to respond to tools like tcptraceroute - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -m ttl --ttl-lt 2 -j ACCEPT")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -p tcp -m hl --hl-lt 2 -j ACCEPT")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -m ttl --ttl-lt 2 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p tcp -m hl --hl-lt 2 -j ACCEPT") # Finally, if the device has control plane ACLs configured, # add iptables/ip6tables commands to drop all other incoming packets if num_ctrl_plane_acl_rules > 0: - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A INPUT -j DROP")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("iptables -A FORWARD -j DROP")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A INPUT -j DROP")) - iptables_cmds.append(self.iptable_cmd_prefix[namespace] + ("ip6tables -A FORWARD -j DROP")) + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -j DROP") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A FORWARD -j DROP") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -j DROP") + iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A FORWARD -j DROP") return iptables_cmds @@ -414,7 +414,7 @@ class ControlPlaneAclManager(object): sel = swsscommon.Select() config_db_subscriber_table_map = {} for namespace in self.config_db_map.keys(): - # Program first time to setup default ip table rules + # Unconditionally update control plane ACLs once at start self.update_control_plane_acls(namespace) acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace) subscribe_acl_table = swsscommon.SubscriberStateTable(acl_db_connector, swsscommon.CFG_ACL_TABLE_TABLE_NAME) From 2137fc0d9d7055c16c436f1cb85ee7c656803054 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Thu, 23 Jul 2020 12:36:49 -0700 Subject: [PATCH 03/13] Fix Review Comments --- files/image_config/caclmgrd/caclmgrd | 93 ++++++++++++++-------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index 2c253c3c1355..dc1a83661d71 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -92,15 +92,15 @@ class ControlPlaneAclManager(object): def __init__(self): SonicDBConfig.load_sonic_global_db_config() self.config_db_map = {} - self.iptables_cmd_prefix = {} + self.iptables_cmd_ns_prefix = {} self.config_db_map[''] = ConfigDBConnector(use_unix_socket_path=True, namespace='') self.config_db_map[''].connect() - self.iptables_cmd_prefix[''] = "" + self.iptables_cmd_ns_prefix[''] = "" namespaces = sonic_device_util.get_all_namespaces() for front_asic_namespaces in namespaces['front_ns']: self.config_db_map[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) self.config_db_map[front_asic_namespaces].connect() - self.iptables_cmd_prefix[front_asic_namespaces] = "ip netns exec " + front_asic_namespaces + " " + self.iptables_cmd_ns_prefix[front_asic_namespaces] = "ip netns exec " + front_asic_namespaces + " " def run_commands(self, commands): """ @@ -170,9 +170,9 @@ class ControlPlaneAclManager(object): ip_addr = next(ip_ntwrk.hosts()) if iface_table_name == "VLAN_INTERFACE" else ip_ntwrk.network_address if isinstance(ip_ntwrk, ipaddress.IPv4Network): - block_ip2me_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) + block_ip2me_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) elif isinstance(ip_ntwrk, ipaddress.IPv6Network): - block_ip2me_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) + block_ip2me_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) else: log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk)) @@ -207,67 +207,67 @@ class ControlPlaneAclManager(object): # First, add iptables commands to set default policies to accept all # traffic. In case we are connected remotely, the connection will not # drop when we flush the current rules - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -P INPUT ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -P FORWARD ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -P OUTPUT ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -P INPUT ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -P FORWARD ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -P OUTPUT ACCEPT") # Add iptables command to flush the current rules - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -F") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -F") # Add iptables command to delete all non-default chains - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -X") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -X") # Add same set of commands for ip6tables - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -P INPUT ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -P FORWARD ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -P OUTPUT ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -F") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -X") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -P INPUT ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -P FORWARD ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -P OUTPUT ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -F") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -X") # Add iptables/ip6tables commands to allow all traffic from localhost - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -s ::1 -i lo -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s ::1 -i lo -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming packets from established # connections or new connections which are related to established connections - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") # Add iptables/ip6tables commands to allow bidirectional ICMPv4 ping and traceroute # TODO: Support processing ICMPv4 service ACL rules, and remove this blanket acceptance - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT") # Add iptables/ip6tables commands to allow bidirectional ICMPv6 ping and traceroute # TODO: Support processing ICMPv6 service ACL rules, and remove this blanket acceptance - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming Neighbor Discovery Protocol (NDP) NS/NA/RS/RA messages # TODO: Support processing NDP service ACL rules, and remove this blanket acceptance - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming IPv4 DHCP packets - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming IPv6 DHCP packets - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming BGP traffic # TODO: Determine BGP ACLs based on configured device sessions, and remove this blanket acceptance - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p tcp --dport 179 -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -p tcp --sport 179 -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p tcp --dport 179 -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p tcp --sport 179 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p tcp --dport 179 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p tcp --sport 179 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p tcp --dport 179 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p tcp --sport 179 -j ACCEPT") # Get current ACL tables and rules from Config DB @@ -373,7 +373,7 @@ class ControlPlaneAclManager(object): # Append the packet action as the jump target rule_cmd += " -j {}".format(rule_props["PACKET_ACTION"]) - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + rule_cmd) + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + rule_cmd) num_ctrl_plane_acl_rules += 1 # Add iptables commands to block ip2me traffic @@ -381,16 +381,16 @@ class ControlPlaneAclManager(object): # Add iptables/ip6tables commands to allow all incoming packets with TTL of 0 or 1 # This allows the device to respond to tools like tcptraceroute - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -m ttl --ttl-lt 2 -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -p tcp -m hl --hl-lt 2 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -m ttl --ttl-lt 2 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p tcp -m hl --hl-lt 2 -j ACCEPT") # Finally, if the device has control plane ACLs configured, # add iptables/ip6tables commands to drop all other incoming packets if num_ctrl_plane_acl_rules > 0: - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A INPUT -j DROP") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "iptables -A FORWARD -j DROP") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A INPUT -j DROP") - iptables_cmds.append(self.iptables_cmd_prefix[namespace] + "ip6tables -A FORWARD -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] + "iptables -A FORWARD -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 FORWARD -j DROP") return iptables_cmds @@ -416,6 +416,7 @@ class ControlPlaneAclManager(object): for namespace in self.config_db_map.keys(): # Unconditionally update control plane ACLs once at start self.update_control_plane_acls(namespace) + acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace) subscribe_acl_table = swsscommon.SubscriberStateTable(acl_db_connector, swsscommon.CFG_ACL_TABLE_TABLE_NAME) subscribe_acl_rule_table = swsscommon.SubscriberStateTable(acl_db_connector, swsscommon.CFG_ACL_RULE_TABLE_NAME) From 9a45fb544483db5dd729e65881fdb30e31b89fdf Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Thu, 23 Jul 2020 13:48:35 -0700 Subject: [PATCH 04/13] Fix Comments --- files/image_config/caclmgrd/caclmgrd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index dc1a83661d71..b8cc4e7f4264 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -255,12 +255,12 @@ class ControlPlaneAclManager(object): iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming IPv4 DHCP packets - iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp --dport 67:68 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 67:68 -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming IPv6 DHCP packets - iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT") - iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp --dport 546:547 -j ACCEPT") + iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p udp --dport 546:547 -j ACCEPT") # Add iptables/ip6tables commands to allow all incoming BGP traffic # TODO: Determine BGP ACLs based on configured device sessions, and remove this blanket acceptance From 47a0699949af32912f25c418160ea888fcf6989a Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Thu, 23 Jul 2020 17:17:40 -0700 Subject: [PATCH 05/13] Added Change for Multi-asic to have iptables rules to accept internal docker tcp/udp traffic needed for syslog and redis-tcp connection. Signed-off-by: Abhishek Dosi --- files/image_config/caclmgrd/caclmgrd | 45 ++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index b8cc4e7f4264..7a83a684ccd0 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -96,11 +96,26 @@ class ControlPlaneAclManager(object): self.config_db_map[''] = ConfigDBConnector(use_unix_socket_path=True, namespace='') self.config_db_map[''].connect() self.iptables_cmd_ns_prefix[''] = "" + self.docker_bridge_ip = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[''], '') + self.namespace_docker_mgmt_ip = {} namespaces = sonic_device_util.get_all_namespaces() for front_asic_namespaces in namespaces['front_ns']: self.config_db_map[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) self.config_db_map[front_asic_namespaces].connect() self.iptables_cmd_ns_prefix[front_asic_namespaces] = "ip netns exec " + front_asic_namespaces + " " + self.namespace_docker_mgmt_ip[front_asic_namespaces] = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[front_asic_namespaces], + front_asic_namespaces) + + for back_asic_namespaces in namespaces['back_ns']: + self.iptables_cmd_ns_prefix[back_asic_namespaces] = "ip netns exec " + back_asic_namespaces + " " + self.namespace_docker_mgmt_ip[back_asic_namespaces] = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[back_asic_namespaces], + back_asic_namespaces) + def get_docker_ip_address(self, iptable_ns_cmd_prefix, namespace): + + ip_address_get_command = iptable_ns_cmd_prefix + "ip -4 -o addr show " + ("eth0" if namespace else "docker0") +\ + " | awk '{print $4}' | cut -d'/' -f1 | head -1" + + return self.run_commands([ip_address_get_command]) def run_commands(self, commands): """ @@ -110,12 +125,14 @@ class ControlPlaneAclManager(object): commands: List of strings, each string is a shell command """ for cmd in commands: - proc = subprocess.Popen(cmd, shell=True) + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) (stdout, stderr) = proc.communicate() if proc.returncode != 0: log_error("Error running command '{}'".format(cmd)) + elif stdout: + return stdout.rstrip('\n') def parse_int_to_tcp_flags(self, hex_value): tcp_flags_str = "" @@ -170,13 +187,32 @@ class ControlPlaneAclManager(object): ip_addr = next(ip_ntwrk.hosts()) if iface_table_name == "VLAN_INTERFACE" else ip_ntwrk.network_address if isinstance(ip_ntwrk, ipaddress.IPv4Network): - block_ip2me_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) + block_ip2me_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen)) elif isinstance(ip_ntwrk, ipaddress.IPv6Network): - block_ip2me_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen))) + block_ip2me_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -d {}/{} -j DROP".format(ip_addr, ip_ntwrk.max_prefixlen)) else: log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk)) return block_ip2me_cmds + def generate_allow_internal_docker_ip_traffic_commands(self, namespace): + allow_internal_docker_ip_cmds = [] + + # For namespace docker all tcp/udp traffic from host docker bridge to its eth0 management ip + if namespace: + allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p tcp -s {} -d {} -j ACCEPT".format\ + (self.docker_bridge_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\ + (self.docker_bridge_ip, self.namespace_docker_mgmt_ip[namespace])) + else: + # In host allow all tcp/udp traffic from namespace docker eth0 management to host docker bridge + for docker_mgmt_ip in 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\ + (docker_mgmt_ip, self.docker_bridge_ip)) + + allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp -s {} -d {} -j ACCEPT".format\ + (docker_mgmt_ip, self.docker_bridge_ip)) + return allow_internal_docker_ip_cmds def is_rule_ipv4(self, rule_props): if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or @@ -228,6 +264,9 @@ class ControlPlaneAclManager(object): iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT") iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -s ::1 -i lo -j ACCEPT") + # Add iptables commands to allow internal docker traffic + iptables_cmds += self.generate_allow_internal_docker_ip_traffic_commands(namespace) + # Add iptables/ip6tables commands to allow all incoming packets from established # connections or new connections which are related to established connections iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") From 32de19d1e71218ebb90475f190cd7f13738c02de Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Thu, 23 Jul 2020 18:50:34 -0700 Subject: [PATCH 06/13] Fix Review Comments --- files/image_config/caclmgrd/caclmgrd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index 7a83a684ccd0..a63158aa1652 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -194,18 +194,19 @@ class ControlPlaneAclManager(object): log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk)) return block_ip2me_cmds + def generate_allow_internal_docker_ip_traffic_commands(self, namespace): allow_internal_docker_ip_cmds = [] - # For namespace docker all tcp/udp traffic from host docker bridge to its eth0 management ip if 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] + "iptables -A INPUT -p tcp -s {} -d {} -j ACCEPT".format\ (self.docker_bridge_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\ (self.docker_bridge_ip, self.namespace_docker_mgmt_ip[namespace])) else: - # In host allow all tcp/udp traffic from namespace docker eth0 management 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 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\ (docker_mgmt_ip, self.docker_bridge_ip)) From 941039867525b85539f7b0632417ba9f4633a605 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Fri, 24 Jul 2020 18:06:39 -0700 Subject: [PATCH 07/13] Added more comments on logic. --- files/image_config/caclmgrd/caclmgrd | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index adf4431b3f75..b0963e3db0d7 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -16,6 +16,7 @@ try: import subprocess import sys import syslog + import sonic_device_util from swsscommon import swsscommon from swsssdk import SonicDBConfig, ConfigDBConnector @@ -448,28 +449,41 @@ class ControlPlaneAclManager(object): def run(self): # Select Time-out for 10 Seconds SELECT_TIMEOUT_MS = 1000 * 10 + # Initlaize Global config that loads all database*.json swsscommon.SonicDBConfig.initializeGlobalConfig() + # Create the Select object sel = swsscommon.Select() + # Map of Namespace <--> susbcriber table's object config_db_subscriber_table_map = {} + # Loop through all asic namespaces (if present) and host (namespace='') for namespace in self.config_db_map.keys(): - # Unconditionally update control plane ACLs once at start + # Unconditionally update control plane ACLs once at start on given namespace self.update_control_plane_acls(namespace) - + # Connect to Config DB of given namespace acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace) + # Subscribe to notifications when ACL tables changes subscribe_acl_table = swsscommon.SubscriberStateTable(acl_db_connector, swsscommon.CFG_ACL_TABLE_TABLE_NAME) + # Subscribe to notifications when ACL rule tables changes subscribe_acl_rule_table = swsscommon.SubscriberStateTable(acl_db_connector, swsscommon.CFG_ACL_RULE_TABLE_NAME) + # Add both tables to the selectable object sel.addSelectable(subscribe_acl_table) sel.addSelectable(subscribe_acl_rule_table) + # Update the map config_db_subscriber_table_map[namespace] = [] config_db_subscriber_table_map[namespace].append(subscribe_acl_table) config_db_subscriber_table_map[namespace].append(subscribe_acl_rule_table) + #Loop on select to see if any event happen on config db of any namespace while True: (state, c) = sel.select(SELECT_TIMEOUT_MS) + # Continue if select is timeout or selectable object is not return if state != swsscommon.Select.OBJECT: continue + # Get the corresponding namespace from selectable object namespace = c.getDbNamespace() + # Pop data of both Subscriber Table object of namespace that got config db acl table event for table in config_db_subscriber_table_map[namespace]: table.pop() + # Update the Control Plane ACL of the namespace that got config db acl table event self.update_control_plane_acls(namespace) # ============================= Functions ============================= From 083bac4a318e02cfa0a9784cec0b09009d0e2767 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Fri, 24 Jul 2020 18:42:03 -0700 Subject: [PATCH 08/13] Fixed all warning/errors reported by http://pep8online.com/ other than line > 80 characters. --- files/image_config/caclmgrd/caclmgrd | 49 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index b0963e3db0d7..b88b170bd96e 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -16,7 +16,7 @@ try: import subprocess import sys import syslog - + import sonic_device_util from swsscommon import swsscommon from swsssdk import SonicDBConfig, ConfigDBConnector @@ -49,6 +49,7 @@ def log_error(msg): # ========================== Helper Functions ========================= + def _ip_prefix_in_key(key): """ Function to check if IP prefix is present in a Redis database key. @@ -59,12 +60,12 @@ def _ip_prefix_in_key(key): # ============================== Classes ============================== + class ControlPlaneAclManager(object): """ Class which reads control plane ACL tables and rules from Config DB, translates them into equivalent iptables commands and runs those commands in order to apply the control plane ACLs. - Attributes: config_db: Handle to Config Redis database via SwSS SDK """ @@ -104,15 +105,15 @@ class ControlPlaneAclManager(object): self.config_db_map[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) self.config_db_map[front_asic_namespaces].connect() self.iptables_cmd_ns_prefix[front_asic_namespaces] = "ip netns exec " + front_asic_namespaces + " " - self.namespace_docker_mgmt_ip[front_asic_namespaces] = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[front_asic_namespaces], - front_asic_namespaces) + self.namespace_docker_mgmt_ip[front_asic_namespaces] = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[front_asic_namespaces], + front_asic_namespaces) for back_asic_namespaces in namespaces['back_ns']: self.iptables_cmd_ns_prefix[back_asic_namespaces] = "ip netns exec " + back_asic_namespaces + " " self.namespace_docker_mgmt_ip[back_asic_namespaces] = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[back_asic_namespaces], back_asic_namespaces) + def get_docker_ip_address(self, iptable_ns_cmd_prefix, namespace): - ip_address_get_command = iptable_ns_cmd_prefix + "ip -4 -o addr show " + ("eth0" if namespace else "docker0") +\ " | awk '{print $4}' | cut -d'/' -f1 | head -1" @@ -121,7 +122,6 @@ class ControlPlaneAclManager(object): def run_commands(self, commands): """ Given a list of shell commands, run them in order - Args: commands: List of strings, each string is a shell command """ @@ -198,34 +198,34 @@ class ControlPlaneAclManager(object): def generate_allow_internal_docker_ip_traffic_commands(self, namespace): allow_internal_docker_ip_cmds = [] - + if 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] + "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 -p tcp -s {} -d {} -j ACCEPT".format (self.docker_bridge_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] + "iptables -A INPUT -p udp -s {} -d {} -j ACCEPT".format (self.docker_bridge_ip, self.namespace_docker_mgmt_ip[namespace])) else: # In host allow all tcp/udp traffic from namespace docker eth0 management ip to host docker bridge for docker_mgmt_ip in 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\ - (docker_mgmt_ip, self.docker_bridge_ip)) + allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p tcp -s {} -d {} -j ACCEPT".format + (docker_mgmt_ip, self.docker_bridge_ip)) - allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp -s {} -d {} -j ACCEPT".format\ - (docker_mgmt_ip, self.docker_bridge_ip)) + allow_internal_docker_ip_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -A INPUT -p udp -s {} -d {} -j ACCEPT".format + (docker_mgmt_ip, self.docker_bridge_ip)) return allow_internal_docker_ip_cmds def is_rule_ipv4(self, rule_props): if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or - ("DST_IP" in rule_props and rule_props["DST_IP"])): + ("DST_IP" in rule_props and rule_props["DST_IP"])): return True else: return False def is_rule_ipv6(self, rule_props): if (("SRC_IPV6" in rule_props and rule_props["SRC_IPV6"]) or - ("DST_IPV6" in rule_props and rule_props["DST_IPV6"])): + ("DST_IPV6" in rule_props and rule_props["DST_IPV6"])): return True else: return False @@ -235,10 +235,8 @@ class ControlPlaneAclManager(object): Retrieves current ACL tables and rules from Config DB, translates control plane ACLs into a list of iptables commands that can be run in order to install ACL rules. - Returns: A list of strings, each string is an iptables shell command - """ iptables_cmds = [] @@ -310,7 +308,6 @@ class ControlPlaneAclManager(object): iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p tcp --dport 179 -j ACCEPT") iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -A INPUT -p tcp --sport 179 -j ACCEPT") - # Get current ACL tables and rules from Config DB self._tables_db_info = self.config_db_map[namespace].get_table(self.ACL_TABLE) self._rules_db_info = self.config_db_map[namespace].get_table(self.ACL_RULE) @@ -331,11 +328,11 @@ class ControlPlaneAclManager(object): for acl_service in acl_services: if acl_service not in self.ACL_SERVICES: log_warning("Ignoring control plane ACL '{}' with unrecognized service '{}'" - .format(table_name, acl_service)) + .format(table_name, acl_service)) continue log_info("Translating ACL rules for control plane ACL '{}' (service: '{}')" - .format(table_name, acl_service)) + .format(table_name, acl_service)) # Obtain default IP protocol(s) and destination port(s) for this service ip_protocols = self.ACL_SERVICES[acl_service]["ip_protocols"] @@ -366,18 +363,18 @@ class ControlPlaneAclManager(object): if (self.is_rule_ipv6(rule_props) and (table_ip_version == 4)): log_error("CtrlPlane ACL table {} is a IPv4 based table and rule {} is a IPV6 rule! Ignoring rule." - .format(table_name, rule_id)) + .format(table_name, rule_id)) acl_rules.pop(rule_props["PRIORITY"]) elif (self.is_rule_ipv4(rule_props) and (table_ip_version == 6)): log_error("CtrlPlane ACL table {} is a IPv6 based table and rule {} is a IPV4 rule! Ignroing rule." - .format(table_name, rule_id)) + .format(table_name, rule_id)) acl_rules.pop(rule_props["PRIORITY"]) # If we were unable to determine whether this ACL table contains # IPv4 or IPv6 rules, log a message and skip processing this table. if not table_ip_version: log_warning("Unable to determine if ACL table '{}' contains IPv4 or IPv6 rules. Skipping table..." - .format(table_name)) + .format(table_name)) continue # For each ACL rule in this table (in descending order of priority) @@ -409,7 +406,7 @@ class ControlPlaneAclManager(object): tcp_flags_mask = int(tcp_flags_mask, 16) if tcp_flags_mask > 0: - rule_cmd += " --tcp-flags {mask} {flags}".format(mask = self.parse_int_to_tcp_flags(tcp_flags_mask), flags = self.parse_int_to_tcp_flags(tcp_flags)) + rule_cmd += " --tcp-flags {mask} {flags}".format(mask=self.parse_int_to_tcp_flags(tcp_flags_mask), flags=self.parse_int_to_tcp_flags(tcp_flags)) # Append the packet action as the jump target rule_cmd += " -j {}".format(rule_props["PACKET_ACTION"]) @@ -472,7 +469,7 @@ class ControlPlaneAclManager(object): config_db_subscriber_table_map[namespace] = [] config_db_subscriber_table_map[namespace].append(subscribe_acl_table) config_db_subscriber_table_map[namespace].append(subscribe_acl_rule_table) - #Loop on select to see if any event happen on config db of any namespace + # Loop on select to see if any event happen on config db of any namespace while True: (state, c) = sel.select(SELECT_TIMEOUT_MS) # Continue if select is timeout or selectable object is not return @@ -488,6 +485,7 @@ class ControlPlaneAclManager(object): # ============================= Functions ============================= + def main(): log_info("Starting up...") @@ -503,3 +501,4 @@ def main(): if __name__ == "__main__": main() + From ecfe3395386416fee08cb7b82b0c9824ee0dd134 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Sun, 26 Jul 2020 12:03:00 -0700 Subject: [PATCH 09/13] Fix Comment Signed-off-by: Abhishek Dosi --- files/image_config/caclmgrd/caclmgrd | 1 - 1 file changed, 1 deletion(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index b88b170bd96e..f170ec5dca5a 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -501,4 +501,3 @@ def main(): if __name__ == "__main__": main() - From 590fe91e84c00ce090f3c4327d71782f1a76f44e Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Mon, 27 Jul 2020 12:33:33 -0700 Subject: [PATCH 10/13] Verified with swsscommon package. Fix issue for single asic platforms. --- files/image_config/caclmgrd/caclmgrd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index f170ec5dca5a..d3a8c6ecfe37 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -446,8 +446,10 @@ class ControlPlaneAclManager(object): def run(self): # Select Time-out for 10 Seconds SELECT_TIMEOUT_MS = 1000 * 10 + # Initlaize Global config that loads all database*.json - swsscommon.SonicDBConfig.initializeGlobalConfig() + if sonic_device_util.is_multi_npu(): + swsscommon.SonicDBConfig.initializeGlobalConfig() # Create the Select object sel = swsscommon.Select() # Map of Namespace <--> susbcriber table's object From d2f8b6ff247e71d5b72101394e7792e2effaa19a Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Mon, 27 Jul 2020 15:16:14 -0700 Subject: [PATCH 11/13] Moved to new python package --- files/image_config/caclmgrd/caclmgrd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index d3a8c6ecfe37..2a8148a72b46 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -17,7 +17,7 @@ try: import sys import syslog - import sonic_device_util + from sonic_py_common import device_info from swsscommon import swsscommon from swsssdk import SonicDBConfig, ConfigDBConnector except ImportError as err: @@ -100,7 +100,7 @@ class ControlPlaneAclManager(object): self.iptables_cmd_ns_prefix[''] = "" self.docker_bridge_ip = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[''], '') self.namespace_docker_mgmt_ip = {} - namespaces = sonic_device_util.get_all_namespaces() + namespaces = device_info.get_all_namespaces() for front_asic_namespaces in namespaces['front_ns']: self.config_db_map[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) self.config_db_map[front_asic_namespaces].connect() @@ -448,7 +448,7 @@ class ControlPlaneAclManager(object): SELECT_TIMEOUT_MS = 1000 * 10 # Initlaize Global config that loads all database*.json - if sonic_device_util.is_multi_npu(): + if device_info.is_multi_npu(): swsscommon.SonicDBConfig.initializeGlobalConfig() # Create the Select object sel = swsscommon.Select() From 1e8cdc4d2e4a83dc334ce80aa762e91b70fe75ee Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Wed, 19 Aug 2020 07:24:07 -0700 Subject: [PATCH 12/13] Address Review Comments. Signed-off-by: Abhishek Dosi --- files/image_config/caclmgrd/caclmgrd | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index 2a8148a72b46..860ff7fda553 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -98,22 +98,22 @@ class ControlPlaneAclManager(object): self.config_db_map[''] = ConfigDBConnector(use_unix_socket_path=True, namespace='') self.config_db_map[''].connect() self.iptables_cmd_ns_prefix[''] = "" - self.docker_bridge_ip = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[''], '') + self.namespace_mgmt_ip = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[''], '') self.namespace_docker_mgmt_ip = {} namespaces = device_info.get_all_namespaces() - for front_asic_namespaces in namespaces['front_ns']: - self.config_db_map[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) - self.config_db_map[front_asic_namespaces].connect() - self.iptables_cmd_ns_prefix[front_asic_namespaces] = "ip netns exec " + front_asic_namespaces + " " - self.namespace_docker_mgmt_ip[front_asic_namespaces] = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[front_asic_namespaces], - front_asic_namespaces) - - for back_asic_namespaces in namespaces['back_ns']: - self.iptables_cmd_ns_prefix[back_asic_namespaces] = "ip netns exec " + back_asic_namespaces + " " - self.namespace_docker_mgmt_ip[back_asic_namespaces] = self.get_docker_ip_address(self.iptables_cmd_ns_prefix[back_asic_namespaces], - back_asic_namespaces) - - def get_docker_ip_address(self, iptable_ns_cmd_prefix, namespace): + for front_asic_namespace in namespaces['front_ns']: + self.config_db_map[front_asic_namespace] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespace) + self.config_db_map[front_asic_namespace].connect() + self.iptables_cmd_ns_prefix[front_asic_namespace] = "ip netns exec " + front_asic_namespace + " " + self.namespace_docker_mgmt_ip[front_asic_namespace] = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[front_asic_namespace], + front_asic_namespace) + + for back_asic_namespace in namespaces['back_ns']: + self.iptables_cmd_ns_prefix[back_asic_namespace] = "ip netns exec " + back_asic_namespace + " " + self.namespace_docker_mgmt_ip[back_asic_namespace] = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[back_asic_namespace], + back_asic_namespace) + + def get_namespace_mgmt_ip(self, iptable_ns_cmd_prefix, namespace): ip_address_get_command = iptable_ns_cmd_prefix + "ip -4 -o addr show " + ("eth0" if namespace else "docker0") +\ " | awk '{print $4}' | cut -d'/' -f1 | head -1" @@ -202,18 +202,18 @@ class ControlPlaneAclManager(object): if 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] + "iptables -A INPUT -p tcp -s {} -d {} -j ACCEPT".format - (self.docker_bridge_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 - (self.docker_bridge_ip, self.namespace_docker_mgmt_ip[namespace])) + (self.namespace_mgmt_ip, self.namespace_docker_mgmt_ip[namespace])) else: # In host allow all tcp/udp traffic from namespace docker eth0 management ip to host docker bridge for docker_mgmt_ip in 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 - (docker_mgmt_ip, self.docker_bridge_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 - (docker_mgmt_ip, self.docker_bridge_ip)) + (docker_mgmt_ip, self.namespace_mgmt_ip)) return allow_internal_docker_ip_cmds def is_rule_ipv4(self, rule_props): From 087163c1417b05cec433ebac150583a381b24b52 Mon Sep 17 00:00:00 2001 From: Abhishek Dosi Date: Wed, 19 Aug 2020 11:10:02 -0700 Subject: [PATCH 13/13] Address Review Comments. --- files/build_templates/sonic_debian_extension.j2 | 1 + files/image_config/caclmgrd/caclmgrd | 3 +++ 2 files changed, 4 insertions(+) diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index 2f45f51c33a6..05f72b2319f5 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -214,6 +214,7 @@ if [[ $CONFIGURED_ARCH == amd64 ]]; then sudo DEBIAN_FRONTEND=noninteractive dpkg --root=$FILESYSTEM_ROOT -i $debs_path/kdump-tools_*.deb || \ sudo LANG=C DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true chroot $FILESYSTEM_ROOT apt-get -q --no-install-suggests --no-install-recommends --force-no install fi + # Install python-swss-common package and all its dependent packages {% if python_swss_debs.strip() -%} {% for deb in python_swss_debs.strip().split(' ') -%} diff --git a/files/image_config/caclmgrd/caclmgrd b/files/image_config/caclmgrd/caclmgrd index 860ff7fda553..6b1cd6cc49ad 100755 --- a/files/image_config/caclmgrd/caclmgrd +++ b/files/image_config/caclmgrd/caclmgrd @@ -450,10 +450,12 @@ class ControlPlaneAclManager(object): # Initlaize Global config that loads all database*.json if device_info.is_multi_npu(): swsscommon.SonicDBConfig.initializeGlobalConfig() + # Create the Select object sel = swsscommon.Select() # Map of Namespace <--> susbcriber table's object config_db_subscriber_table_map = {} + # Loop through all asic namespaces (if present) and host (namespace='') for namespace in self.config_db_map.keys(): # Unconditionally update control plane ACLs once at start on given namespace @@ -471,6 +473,7 @@ class ControlPlaneAclManager(object): config_db_subscriber_table_map[namespace] = [] config_db_subscriber_table_map[namespace].append(subscribe_acl_table) config_db_subscriber_table_map[namespace].append(subscribe_acl_rule_table) + # Loop on select to see if any event happen on config db of any namespace while True: (state, c) = sel.select(SELECT_TIMEOUT_MS)