Support Restapi/gnmi control plane acls in 202012 branch (#11903)
For the Restapi/gnmi use-cases, Sonic has to support a new Table: EXTERNAL_CLIENT of type CTRLPLANE, stage ingress This shall match on 'src ip prefix' and dst port '8080'. Caclmgrd must parse this from acl.json and install as in the below example: iptables -A INPUT -s 20.20.20.20/27 -p tcp --dport 8080 -j ACCEPT or ip6tables if the 'src ip prefix' is IPv6. This change for master branch is in PR sonic-net/sonic-host-services#9 Signed-off-by: Zhaohui Sun <zhaohuisun@microsoft.com>
This commit is contained in:
parent
586a623422
commit
5357bdb643
@ -80,6 +80,10 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
||||
"dst_ports": ["22"],
|
||||
"multi_asic_ns_to_host_fwd":True
|
||||
},
|
||||
"EXTERNAL_CLIENT": {
|
||||
"ip_protocols": ["tcp"],
|
||||
"multi_asic_ns_to_host_fwd":True
|
||||
},
|
||||
"ANY": {
|
||||
"ip_protocols": ["any"],
|
||||
"dst_ports": ["0"],
|
||||
@ -538,7 +542,10 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
||||
|
||||
# Obtain default IP protocol(s) and destination port(s) for this service
|
||||
ip_protocols = self.ACL_SERVICES[acl_service]["ip_protocols"]
|
||||
dst_ports = self.ACL_SERVICES[acl_service]["dst_ports"]
|
||||
if "dst_ports" in self.ACL_SERVICES[acl_service]:
|
||||
dst_ports = self.ACL_SERVICES[acl_service]["dst_ports"]
|
||||
else:
|
||||
dst_ports = []
|
||||
|
||||
acl_rules = {}
|
||||
|
||||
@ -564,6 +571,19 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
||||
elif self.is_rule_ipv4(rule_props):
|
||||
table_ip_version = 4
|
||||
|
||||
# Read DST_PORT info from Config DB, insert it back to ACL_SERVICES
|
||||
if acl_service == 'EXTERNAL_CLIENT' and "L4_DST_PORT" in rule_props:
|
||||
dst_ports = [rule_props["L4_DST_PORT"]]
|
||||
self.ACL_SERVICES[acl_service]["dst_ports"] = dst_ports
|
||||
elif acl_service == 'EXTERNAL_CLIENT' and "L4_DST_PORT_RANGE" in rule_props:
|
||||
dst_ports = []
|
||||
port_ranges = rule_props["L4_DST_PORT_RANGE"].split("-")
|
||||
port_start = int(port_ranges[0])
|
||||
port_end = int(port_ranges[1])
|
||||
for port in range(port_start, port_end + 1):
|
||||
dst_ports.append(port)
|
||||
self.ACL_SERVICES[acl_service]["dst_ports"] = dst_ports
|
||||
|
||||
if (self.is_rule_ipv6(rule_props) and (table_ip_version == 4)):
|
||||
self.log_error("CtrlPlane ACL table {} is a IPv4 based table and rule {} is a IPV6 rule! Ignoring rule."
|
||||
.format(table_name, rule_id))
|
||||
@ -579,6 +599,12 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
||||
self.log_warning("Unable to determine if ACL table '{}' contains IPv4 or IPv6 rules. Skipping table..."
|
||||
.format(table_name))
|
||||
continue
|
||||
# If no destination port found for this ACL table,
|
||||
# log a message and skip processing this table.
|
||||
if len(dst_ports) == 0:
|
||||
self.log_warning("Required destination port not found for ACL table '{}'. Skipping table..."
|
||||
.format(table_name))
|
||||
continue
|
||||
ipv4_src_ip_set = set()
|
||||
ipv6_src_ip_set = set()
|
||||
# For each ACL rule in this table (in descending order of priority)
|
||||
|
@ -0,0 +1,44 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from swsscommon import swsscommon
|
||||
from parameterized import parameterized
|
||||
from sonic_py_common.general import load_module_from_source
|
||||
from unittest import TestCase, mock
|
||||
from pyfakefs.fake_filesystem_unittest import patchfs
|
||||
|
||||
from .test_external_client_acl_vectors import EXTERNAL_CLIENT_ACL_TEST_VECTOR
|
||||
from tests.common.mock_configdb import MockConfigDb
|
||||
|
||||
|
||||
DBCONFIG_PATH = '/var/run/redis/sonic-db/database_config.json'
|
||||
|
||||
|
||||
class TestCaclmgrdExternalClientAcl(TestCase):
|
||||
"""
|
||||
Test caclmgrd EXTERNAL_CLIENT_ACL
|
||||
"""
|
||||
def setUp(self):
|
||||
swsscommon.ConfigDBConnector = MockConfigDb
|
||||
test_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
modules_path = os.path.dirname(test_path)
|
||||
scripts_path = os.path.join(modules_path, "scripts")
|
||||
sys.path.insert(0, modules_path)
|
||||
caclmgrd_path = os.path.join(scripts_path, 'caclmgrd')
|
||||
self.caclmgrd = load_module_from_source('caclmgrd', caclmgrd_path)
|
||||
|
||||
@parameterized.expand(EXTERNAL_CLIENT_ACL_TEST_VECTOR)
|
||||
@patchfs
|
||||
def test_caclmgrd_external_client_acl(self, test_name, test_data, fs):
|
||||
if not os.path.exists(DBCONFIG_PATH):
|
||||
fs.create_file(DBCONFIG_PATH) # fake database_config.json
|
||||
|
||||
MockConfigDb.set_config_db(test_data["config_db"])
|
||||
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ip = mock.MagicMock()
|
||||
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ipv6 = mock.MagicMock()
|
||||
self.caclmgrd.ControlPlaneAclManager.generate_block_ip2me_traffic_iptables_commands = mock.MagicMock(return_value=[])
|
||||
self.caclmgrd.ControlPlaneAclManager.get_chain_list = mock.MagicMock(return_value=["INPUT", "FORWARD", "OUTPUT"])
|
||||
caclmgrd_daemon = self.caclmgrd.ControlPlaneAclManager("caclmgrd")
|
||||
|
||||
iptables_rules_ret, _ = caclmgrd_daemon.get_acl_rules_and_translate_to_iptables_commands('')
|
||||
self.assertEqual(set(test_data["return"]).issubset(set(iptables_rules_ret)), True)
|
@ -0,0 +1,167 @@
|
||||
from unittest.mock import call
|
||||
|
||||
"""
|
||||
caclmgrd test external_client_acl vector
|
||||
"""
|
||||
EXTERNAL_CLIENT_ACL_TEST_VECTOR = [
|
||||
[
|
||||
"Test single IPv4 dst port + src ip for EXTERNAL_CLIENT_ACL",
|
||||
{
|
||||
"config_db": {
|
||||
"ACL_TABLE": {
|
||||
"EXTERNAL_CLIENT_ACL": {
|
||||
"stage": "INGRESS",
|
||||
"type": "CTRLPLANE",
|
||||
"services": [
|
||||
"EXTERNAL_CLIENT"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ACL_RULE": {
|
||||
"EXTERNAL_CLIENT_ACL|DEFAULT_RULE": {
|
||||
"ETHER_TYPE": "2048",
|
||||
"PACKET_ACTION": "DROP",
|
||||
"PRIORITY": "1"
|
||||
},
|
||||
"EXTERNAL_CLIENT_ACL|RULE_1": {
|
||||
"L4_DST_PORT": "8081",
|
||||
"PACKET_ACTION": "ACCEPT",
|
||||
"PRIORITY": "9998",
|
||||
"SRC_IP": "20.0.0.55/32"
|
||||
},
|
||||
},
|
||||
"DEVICE_METADATA": {
|
||||
"localhost": {
|
||||
}
|
||||
},
|
||||
"FEATURE": {},
|
||||
},
|
||||
"return": [
|
||||
"iptables -A INPUT -p tcp -s 20.0.0.55/32 --dport 8081 -j ACCEPT",
|
||||
"iptables -A INPUT -p tcp --dport 8081 -j DROP"
|
||||
],
|
||||
}
|
||||
],
|
||||
[
|
||||
"Test IPv4 dst port range + src ip forEXTERNAL_CLIENT_ACL",
|
||||
{
|
||||
"config_db": {
|
||||
"ACL_TABLE": {
|
||||
"EXTERNAL_CLIENT_ACL": {
|
||||
"stage": "INGRESS",
|
||||
"type": "CTRLPLANE",
|
||||
"services": [
|
||||
"EXTERNAL_CLIENT"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ACL_RULE": {
|
||||
"EXTERNAL_CLIENT_ACL|DEFAULT_RULE": {
|
||||
"ETHER_TYPE": "2048",
|
||||
"PACKET_ACTION": "DROP",
|
||||
"PRIORITY": "1"
|
||||
},
|
||||
"EXTERNAL_CLIENT_ACL|RULE_1": {
|
||||
"L4_DST_PORT_RANGE": "8081-8083",
|
||||
"PACKET_ACTION": "ACCEPT",
|
||||
"PRIORITY": "9998",
|
||||
"SRC_IP": "20.0.0.55/32"
|
||||
},
|
||||
},
|
||||
"DEVICE_METADATA": {
|
||||
"localhost": {
|
||||
}
|
||||
},
|
||||
"FEATURE": {},
|
||||
},
|
||||
"return": [
|
||||
"iptables -A INPUT -p tcp -s 20.0.0.55/32 --dport 8081 -j ACCEPT",
|
||||
"iptables -A INPUT -p tcp -s 20.0.0.55/32 --dport 8082 -j ACCEPT",
|
||||
"iptables -A INPUT -p tcp -s 20.0.0.55/32 --dport 8083 -j ACCEPT",
|
||||
"iptables -A INPUT -p tcp --dport 8081 -j DROP",
|
||||
"iptables -A INPUT -p tcp --dport 8082 -j DROP",
|
||||
"iptables -A INPUT -p tcp --dport 8083 -j DROP",
|
||||
],
|
||||
}
|
||||
],
|
||||
[
|
||||
"Test IPv6 single dst port range + src ip forEXTERNAL_CLIENT_ACL",
|
||||
{
|
||||
"config_db": {
|
||||
"ACL_TABLE": {
|
||||
"EXTERNAL_CLIENT_ACL": {
|
||||
"stage": "INGRESS",
|
||||
"type": "CTRLPLANE",
|
||||
"services": [
|
||||
"EXTERNAL_CLIENT"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ACL_RULE": {
|
||||
"EXTERNAL_CLIENT_ACL|DEFAULT_RULE": {
|
||||
"ETHER_TYPE": "2048",
|
||||
"PACKET_ACTION": "DROP",
|
||||
"PRIORITY": "1"
|
||||
},
|
||||
"EXTERNAL_CLIENT_ACL|RULE_1": {
|
||||
"L4_DST_PORT": "8081",
|
||||
"PACKET_ACTION": "ACCEPT",
|
||||
"PRIORITY": "9998",
|
||||
"SRC_IP": "2001::2/128"
|
||||
},
|
||||
},
|
||||
"DEVICE_METADATA": {
|
||||
"localhost": {
|
||||
}
|
||||
},
|
||||
"FEATURE": {},
|
||||
},
|
||||
"return": [
|
||||
"iptables -A INPUT -p tcp -s 2001::2/128 --dport 8081 -j ACCEPT",
|
||||
"iptables -A INPUT -p tcp --dport 8081 -j DROP"
|
||||
],
|
||||
}
|
||||
],
|
||||
[
|
||||
"Test IPv6 dst port range + src ip forEXTERNAL_CLIENT_ACL",
|
||||
{
|
||||
"config_db": {
|
||||
"ACL_TABLE": {
|
||||
"EXTERNAL_CLIENT_ACL": {
|
||||
"stage": "INGRESS",
|
||||
"type": "CTRLPLANE",
|
||||
"services": [
|
||||
"EXTERNAL_CLIENT"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ACL_RULE": {
|
||||
"EXTERNAL_CLIENT_ACL|DEFAULT_RULE": {
|
||||
"ETHER_TYPE": "2048",
|
||||
"PACKET_ACTION": "DROP",
|
||||
"PRIORITY": "1"
|
||||
},
|
||||
"EXTERNAL_CLIENT_ACL|RULE_1": {
|
||||
"L4_DST_PORT_RANGE": "8081-8083",
|
||||
"PACKET_ACTION": "ACCEPT",
|
||||
"PRIORITY": "9998",
|
||||
"SRC_IP": "2001::2/128"
|
||||
},
|
||||
},
|
||||
"DEVICE_METADATA": {
|
||||
"localhost": {
|
||||
}
|
||||
},
|
||||
"FEATURE": {},
|
||||
},
|
||||
"return": [
|
||||
"iptables -A INPUT -p tcp -s 2001::2/128 --dport 8081 -j ACCEPT",
|
||||
"iptables -A INPUT -p tcp -s 2001::2/128 --dport 8082 -j ACCEPT",
|
||||
"iptables -A INPUT -p tcp -s 2001::2/128 --dport 8083 -j ACCEPT",
|
||||
"iptables -A INPUT -p tcp --dport 8081 -j DROP",
|
||||
"iptables -A INPUT -p tcp --dport 8082 -j DROP",
|
||||
"iptables -A INPUT -p tcp --dport 8083 -j DROP",
|
||||
],
|
||||
}
|
||||
]
|
||||
]
|
@ -42,7 +42,10 @@ class MockConfigDb(object):
|
||||
MockConfigDb.CONFIG_DB[key][field] = data
|
||||
|
||||
def get_table(self, table_name):
|
||||
return MockConfigDb.CONFIG_DB[table_name]
|
||||
data = {}
|
||||
for k, v in MockConfigDb.CONFIG_DB[table_name].items():
|
||||
data[self.deserialize_key(k)] = v
|
||||
return data
|
||||
|
||||
class MockSelect():
|
||||
|
||||
|
Reference in New Issue
Block a user