Skip to content

Commit

Permalink
Merge pull request #9 from ZhaohuiS/feature/caclmgrd_external_client
Browse files Browse the repository at this point in the history
Support Restapi/gnmi control plane acls
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.

Signed-off-by: Zhaohui Sun [email protected]
  • Loading branch information
ZhaohuiS authored Aug 30, 2022
2 parents f9af7ae + 7c0b56a commit 91c4c42
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 1 deletion.
20 changes: 19 additions & 1 deletion scripts/caclmgrd
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down Expand Up @@ -545,7 +549,8 @@ 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"]

acl_rules = {}

Expand All @@ -571,6 +576,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))
Expand Down
44 changes: 44 additions & 0 deletions tests/caclmgrd/caclmgrd_external_client_acl_test.py
Original file line number Diff line number Diff line change
@@ -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)
167 changes: 167 additions & 0 deletions tests/caclmgrd/test_external_client_acl_vectors.py
Original file line number Diff line number Diff line change
@@ -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",
],
}
]
]

0 comments on commit 91c4c42

Please sign in to comment.