From e5605296391dcec5b8ffab1a15e8a31d6452fb8c Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 12:06:23 +0000 Subject: [PATCH 01/27] #2418 Add Printer and Wireless router to config parser --- src/primaite/game/game.py | 15 +++++++++++++-- .../network/hardware/nodes/host/server.py | 6 ++++++ tests/assets/configs/test_primaite_session.yaml | 6 ++++++ .../test_primaite_session.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index 05b76679..6ba7e63c 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -15,10 +15,11 @@ from primaite.game.science import graph_has_cycle, topological_sort from primaite.simulator.network.hardware.base import NodeOperatingState from primaite.simulator.network.hardware.nodes.host.computer import Computer from primaite.simulator.network.hardware.nodes.host.host_node import NIC -from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.network.hardware.nodes.host.server import Printer, Server from primaite.simulator.network.hardware.nodes.network.firewall import Firewall from primaite.simulator.network.hardware.nodes.network.router import Router from primaite.simulator.network.hardware.nodes.network.switch import Switch +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter from primaite.simulator.network.nmne import set_nmne_config from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.sim_container import Simulation @@ -273,8 +274,18 @@ class PrimaiteGame: new_node = Router.from_config(node_cfg) elif n_type == "firewall": new_node = Firewall.from_config(node_cfg) + elif n_type == "wireless_router": + new_node = WirelessRouter.from_config(node_cfg) + elif n_type == "printer": + new_node = Printer( + hostname=node_cfg["hostname"], + ip_address=node_cfg["ip_address"], + subnet_mask=node_cfg["subnet_mask"], + ) else: - _LOGGER.warning(f"invalid node type {n_type} in config") + msg = f"invalid node type {n_type} in config" + _LOGGER.error(msg) + raise ValueError(msg) if "services" in node_cfg: for service_cfg in node_cfg["services"]: new_service = None diff --git a/src/primaite/simulator/network/hardware/nodes/host/server.py b/src/primaite/simulator/network/hardware/nodes/host/server.py index 9f5157ad..593cd0dd 100644 --- a/src/primaite/simulator/network/hardware/nodes/host/server.py +++ b/src/primaite/simulator/network/hardware/nodes/host/server.py @@ -28,3 +28,9 @@ class Server(HostNode): * Applications: * Web Browser """ + + +class Printer(HostNode): + """Printer? I don't even know her!.""" + + # TODO: Implement printer-specific behaviour diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index 121cc7f1..f4ae4783 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -681,6 +681,12 @@ simulation: - ref: client_2_dns_client type: DNSClient + - ref: HP_LaserJet_Pro_4102fdn_printer + type: printer + hostname: HP_LaserJet_Pro_4102fdn_printer + ip_address: 192.168.10.99 + subnet_mask: 255.255.255.0 + links: - ref: router_1___switch_1 endpoint_a_ref: router_1 diff --git a/tests/e2e_integration_tests/test_primaite_session.py b/tests/e2e_integration_tests/test_primaite_session.py index c45a4690..7febe39a 100644 --- a/tests/e2e_integration_tests/test_primaite_session.py +++ b/tests/e2e_integration_tests/test_primaite_session.py @@ -29,7 +29,7 @@ class TestPrimaiteSession: assert session.env assert session.env.game.simulation.network - assert len(session.env.game.simulation.network.nodes) == 10 + assert len(session.env.game.simulation.network.nodes) == 11 @pytest.mark.skip(reason="Session is not being maintained and will be removed in the subsequent beta release.") @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) From dca26b832db8a0674a87df4409eab286c5362890 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 13:21:22 +0000 Subject: [PATCH 02/27] #2418 Fix wireless router from config --- .../network/hardware/nodes/network/router.py | 2 +- .../hardware/nodes/network/wireless_router.py | 69 ++++++++++++++++++- .../assets/configs/test_primaite_session.yaml | 27 ++++++++ .../test_primaite_session.py | 9 ++- 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/primaite/simulator/network/hardware/nodes/network/router.py b/src/primaite/simulator/network/hardware/nodes/network/router.py index d2b47c1a..de308547 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/router.py @@ -1418,7 +1418,7 @@ class Router(NetworkNode): :return: Configured router. :rtype: Router """ - router = Router( + router = cls( hostname=cfg["hostname"], num_ports=int(cfg.get("num_ports", "5")), operating_state=NodeOperatingState.ON diff --git a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py index 3e8d715f..4bd3d101 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py @@ -1,10 +1,14 @@ +from ipaddress import IPv4Address from typing import Any, Dict, Union from pydantic import validate_call from primaite.simulator.network.airspace import AirSpaceFrequency, IPWirelessNetworkInterface -from primaite.simulator.network.hardware.nodes.network.router import Router, RouterInterface +from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router, RouterInterface from primaite.simulator.network.transmission.data_link_layer import Frame +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port from primaite.utils.validators import IPV4Address @@ -209,3 +213,66 @@ class WirelessRouter(Router): raise NotImplementedError( "Please use the 'configure_wireless_access_point' and 'configure_router_interface' functions." ) + + @classmethod + def from_config(cls, cfg: Dict) -> "WirelessRouter": + """Generate the wireless router from config. + + Schema: + - hostname (str): unique name for this router. + - router_interface (dict): The values should be another dict specifying + - ip_address (str) + - subnet_mask (str) + - wireless_access_point (dict): Dict with + - ip address, + - subnet mask, + - frequency, (string: either WIFI_2_4 or WIFI_5) + - acl (dict): Dict with integers from 1 - max_acl_rules as keys. The key defines the position within the ACL + where the rule will be added (lower number is resolved first). The values should describe valid ACL + Rules as: + - action (str): either PERMIT or DENY + - src_port (str, optional): the named port such as HTTP, HTTPS, or POSTGRES_SERVER + - dst_port (str, optional): the named port such as HTTP, HTTPS, or POSTGRES_SERVER + - protocol (str, optional): the named IP protocol such as ICMP, TCP, or UDP + - src_ip_address (str, optional): IP address octet written in base 10 + - dst_ip_address (str, optional): IP address octet written in base 10 + + :param cfg: Config dictionary + :type cfg: Dict + :return: WirelessRouter instance. + :rtype: WirelessRouter + """ + operating_state = ( + NodeOperatingState.ON if not (p := cfg.get("operating_state")) else NodeOperatingState[p.upper()] + ) + router = cls(hostname=cfg["hostname"], operating_state=operating_state) + if "router_interface" in cfg: + ip_address = cfg["router_interface"]["ip_address"] + subnet_mask = cfg["router_interface"]["subnet_mask"] + router.configure_router_interface(ip_address=ip_address, subnet_mask=subnet_mask) + if "wireless_access_point" in cfg: + ip_address = cfg["wireless_access_point"]["ip_address"] + subnet_mask = cfg["wireless_access_point"]["subnet_mask"] + frequency = AirSpaceFrequency[cfg["wireless_access_point"]["frequency"]] + router.configure_wireless_access_point(ip_address=ip_address, subnet_mask=subnet_mask, frequency=frequency) + + if "acl" in cfg: + for r_num, r_cfg in cfg["acl"].items(): + router.acl.add_rule( + action=ACLAction[r_cfg["action"]], + src_port=None if not (p := r_cfg.get("src_port")) else Port[p], + dst_port=None if not (p := r_cfg.get("dst_port")) else Port[p], + protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], + src_ip_address=r_cfg.get("src_ip"), + dst_ip_address=r_cfg.get("dst_ip"), + position=r_num, + ) + if "routes" in cfg: + for route in cfg.get("routes"): + router.route_table.add_route( + address=IPv4Address(route.get("address")), + subnet_mask=IPv4Address(route.get("subnet_mask", "255.255.255.0")), + next_hop_ip_address=IPv4Address(route.get("next_hop_ip_address")), + metric=float(route.get("metric", 0)), + ) + return router diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index f4ae4783..11db08a7 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -687,6 +687,33 @@ simulation: ip_address: 192.168.10.99 subnet_mask: 255.255.255.0 + - ref: router_2 + type: wireless_router + hostname: router_2 + router_interface: + ip_address: 192.169.1.1 + subnet_mask: 255.255.255.0 + wireless_access_point: + ip_address: 192.169.1.1 + subnet_mask: 255.255.255.0 + frequency: WIFI_2_4 + acl: + 0: + action: PERMIT + src_port: POSTGRES_SERVER + dst_port: POSTGRES_SERVER + 1: + action: PERMIT + src_port: DNS + dst_port: DNS + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + links: - ref: router_1___switch_1 endpoint_a_ref: router_1 diff --git a/tests/e2e_integration_tests/test_primaite_session.py b/tests/e2e_integration_tests/test_primaite_session.py index 7febe39a..32f134a3 100644 --- a/tests/e2e_integration_tests/test_primaite_session.py +++ b/tests/e2e_integration_tests/test_primaite_session.py @@ -1,6 +1,8 @@ import pydantic import pytest +from primaite.simulator.network.hardware.nodes.host.server import Printer +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter from tests import TEST_ASSETS_ROOT from tests.conftest import TempPrimaiteSession @@ -11,7 +13,6 @@ MISCONFIGURED_PATH = TEST_ASSETS_ROOT / "configs/bad_primaite_session.yaml" MULTI_AGENT_PATH = TEST_ASSETS_ROOT / "configs/multi_agent_session.yaml" -# @pytest.mark.skip(reason="no way of currently testing this") class TestPrimaiteSession: @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) def test_creating_session(self, temp_primaite_session): @@ -29,7 +30,11 @@ class TestPrimaiteSession: assert session.env assert session.env.game.simulation.network - assert len(session.env.game.simulation.network.nodes) == 11 + assert len(session.env.game.simulation.network.nodes) == 12 + wireless = session.env.game.simulation.network.get_node_by_hostname("router_2") + assert isinstance(wireless, WirelessRouter) + printer = session.env.game.simulation.network.get_node_by_hostname("HP_LaserJet_Pro_4102fdn_printer") + assert isinstance(printer, Printer) @pytest.mark.skip(reason="Session is not being maintained and will be removed in the subsequent beta release.") @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) From 0e90e6a262024a81411fc332c2fbd1767604831b Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 16:23:39 +0000 Subject: [PATCH 03/27] #2418 - Change test config --- tests/assets/configs/test_primaite_session.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index 11db08a7..4adbb225 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -694,7 +694,7 @@ simulation: ip_address: 192.169.1.1 subnet_mask: 255.255.255.0 wireless_access_point: - ip_address: 192.169.1.1 + ip_address: 192.170.1.1 subnet_mask: 255.255.255.0 frequency: WIFI_2_4 acl: From 9a8a42f3ecf8ee57b09de465d26e91b494e98c73 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 21:48:17 +0000 Subject: [PATCH 04/27] #2418 - add wildcard masks and from_config tests to routers --- .../network/hardware/nodes/network/router.py | 2 + .../hardware/nodes/network/wireless_router.py | 2 + .../_network/_hardware/nodes/test_router.py | 111 ++++++++++++++++++ .../_hardware/nodes/test_wireless_router.py | 97 +++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py create mode 100644 tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py diff --git a/src/primaite/simulator/network/hardware/nodes/network/router.py b/src/primaite/simulator/network/hardware/nodes/network/router.py index de308547..102eb7dc 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/router.py @@ -1441,6 +1441,8 @@ class Router(NetworkNode): protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], src_ip_address=r_cfg.get("src_ip"), dst_ip_address=r_cfg.get("dst_ip"), + src_wildcard_mask=r_cfg.get("src_wildcard_mask"), + dst_wildcard_mask=r_cfg.get("dst_wildcard_mask"), position=r_num, ) if "routes" in cfg: diff --git a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py index 4bd3d101..62332269 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py @@ -265,6 +265,8 @@ class WirelessRouter(Router): protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], src_ip_address=r_cfg.get("src_ip"), dst_ip_address=r_cfg.get("dst_ip"), + src_wildcard_mask=r_cfg.get("src_wildcard_mask"), + dst_wildcard_mask=r_cfg.get("dst_wildcard_mask"), position=r_num, ) if "routes" in cfg: diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py new file mode 100644 index 00000000..be74a721 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py @@ -0,0 +1,111 @@ +from ipaddress import IPv4Address + +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port + + +def test_wireless_router_from_config(): + cfg = { + "ref": "router_1", + "type": "router", + "hostname": "router_1", + "num_ports": 6, + "ports": { + 1: { + "ip_address": "192.168.1.1", + "subnet_mask": "255.255.255.0", + }, + 2: { + "ip_address": "192.168.2.1", + "subnet_mask": "255.255.255.0", + }, + }, + "acl": { + 0: { + "action": "PERMIT", + "src_port": "POSTGRES_SERVER", + "dst_port": "POSTGRES_SERVER", + }, + 1: { + "action": "PERMIT", + "protocol": "ICMP", + }, + 2: { + "action": "PERMIT", + "src_ip": "100.100.100.1", + "dst_ip": "100.100.101.1", + }, + 3: { + "action": "PERMIT", + "src_ip": "100.100.102.0", + "dst_ip": "100.100.103.0", + "src_wildcard_mask": "0.0.0.255", + "dst_wildcard_mask": "0.0.0.255", + }, + 20: { + "action": "DENY", + }, + }, + } + + rt = Router.from_config(cfg=cfg) + + assert rt.num_ports == 6 + + assert rt.network_interface[1].ip_address == IPv4Address("192.168.1.1") + assert rt.network_interface[1].subnet_mask == IPv4Address("255.255.255.0") + + assert rt.network_interface[2].ip_address == IPv4Address("192.168.2.1") + assert rt.network_interface[2].subnet_mask == IPv4Address("255.255.255.0") + + assert not rt.network_interface[3].enabled + assert not rt.network_interface[4].enabled + assert not rt.network_interface[5].enabled + assert not rt.network_interface[6].enabled + + r0 = rt.acl.acl[0] + assert r0.action == ACLAction.PERMIT + assert r0.src_port == r0.dst_port == Port.POSTGRES_SERVER + assert r0.src_ip_address == r0.dst_ip_address == r0.dst_wildcard_mask == r0.src_wildcard_mask == r0.protocol == None + + r1 = rt.acl.acl[1] + assert r1.action == ACLAction.PERMIT + assert r1.protocol == IPProtocol.ICMP + assert ( + r1.src_ip_address + == r1.dst_ip_address + == r1.dst_wildcard_mask + == r1.src_wildcard_mask + == r1.src_port + == r1.dst_port + == None + ) + + r2 = rt.acl.acl[2] + assert r2.action == ACLAction.PERMIT + assert r2.src_ip_address == IPv4Address("100.100.100.1") + assert r2.dst_ip_address == IPv4Address("100.100.101.1") + assert r2.src_wildcard_mask == r2.dst_wildcard_mask == None + assert r2.src_port == r2.dst_port == r2.protocol == None + + r3 = rt.acl.acl[3] + assert r3.action == ACLAction.PERMIT + assert r3.src_ip_address == IPv4Address("100.100.102.0") + assert r3.dst_ip_address == IPv4Address("100.100.103.0") + assert r3.src_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.dst_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.src_port == r3.dst_port == r3.protocol == None + + r20 = rt.acl.acl[20] + assert r20.action == ACLAction.DENY + assert ( + r20.src_ip_address + == r20.dst_ip_address + == r20.src_wildcard_mask + == r20.dst_wildcard_mask + == r20.src_port + == r20.dst_port + == r20.protocol + == None + ) diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py new file mode 100644 index 00000000..494f5a15 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py @@ -0,0 +1,97 @@ +from ipaddress import IPv4Address + +from primaite.simulator.network.hardware.nodes.network.router import ACLAction +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port + + +def test_wireless_router_from_config(): + cfg = { + "ref": "router_2", + "type": "wireless_router", + "hostname": "router_2", + "router_interface": { + "ip_address": "192.168.1.1", + "subnet_mask": "255.255.255.0", + }, + "wireless_access_point": { + "ip_address": "192.170.1.1", + "subnet_mask": "255.255.255.0", + "frequency": "WIFI_2_4", + }, + "acl": { + 0: { + "action": "PERMIT", + "src_port": "POSTGRES_SERVER", + "dst_port": "POSTGRES_SERVER", + }, + 1: { + "action": "PERMIT", + "protocol": "ICMP", + }, + 2: { + "action": "PERMIT", + "src_ip": "100.100.100.1", + "dst_ip": "100.100.101.1", + }, + 3: { + "action": "PERMIT", + "src_ip": "100.100.102.0", + "dst_ip": "100.100.103.0", + "src_wildcard_mask": "0.0.0.255", + "dst_wildcard_mask": "0.0.0.255", + }, + 20: { + "action": "DENY", + }, + }, + } + + rt = WirelessRouter.from_config(cfg=cfg) + + r0 = rt.acl.acl[0] + assert r0.action == ACLAction.PERMIT + assert r0.src_port == r0.dst_port == Port.POSTGRES_SERVER + assert r0.src_ip_address == r0.dst_ip_address == r0.dst_wildcard_mask == r0.src_wildcard_mask == r0.protocol == None + + r1 = rt.acl.acl[1] + assert r1.action == ACLAction.PERMIT + assert r1.protocol == IPProtocol.ICMP + assert ( + r1.src_ip_address + == r1.dst_ip_address + == r1.dst_wildcard_mask + == r1.src_wildcard_mask + == r1.src_port + == r1.dst_port + == None + ) + + r2 = rt.acl.acl[2] + assert r2.action == ACLAction.PERMIT + assert r2.src_ip_address == IPv4Address("100.100.100.1") + assert r2.dst_ip_address == IPv4Address("100.100.101.1") + assert r2.src_wildcard_mask == r2.dst_wildcard_mask == None + assert r2.src_port == r2.dst_port == r2.protocol == None + + r3 = rt.acl.acl[3] + assert r3.action == ACLAction.PERMIT + assert r3.src_ip_address == IPv4Address("100.100.102.0") + assert r3.dst_ip_address == IPv4Address("100.100.103.0") + assert r3.src_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.dst_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.src_port == r3.dst_port == r3.protocol == None + + r20 = rt.acl.acl[20] + assert r20.action == ACLAction.DENY + assert ( + r20.src_ip_address + == r20.dst_ip_address + == r20.src_wildcard_mask + == r20.dst_wildcard_mask + == r20.src_port + == r20.dst_port + == r20.protocol + == None + ) From 368542df03361a721fba64149174f626cf9e4637 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 21:51:27 +0000 Subject: [PATCH 05/27] #2418 - Add printer and wireless router as node types in network show --- src/primaite/simulator/network/container.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index 0e970c3d..5ec47052 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -8,6 +8,7 @@ from prettytable import MARKDOWN, PrettyTable from primaite import getLogger from primaite.simulator.core import RequestManager, RequestType, SimComponent from primaite.simulator.network.hardware.base import Link, Node, WiredNetworkInterface +from primaite.simulator.network.hardware.nodes.host.server import Printer from primaite.simulator.system.applications.application import Application from primaite.simulator.system.services.service import Service @@ -110,6 +111,16 @@ class Network(SimComponent): """The Firewalls in the Network.""" return [node for node in self.nodes.values() if node.__class__.__name__ == "Firewall"] + @property + def printer_nodes(self) -> List[Node]: + """The printers on the network.""" + return [node for node in self.nodes.values() if isinstance(node, Printer)] + + @property + def wireless_router_nodes(self) -> List[Node]: + """The Routers in the Network.""" + return [node for node in self.nodes.values() if node.__class__.__name__ == "WirelessRouter"] + def show(self, nodes: bool = True, ip_addresses: bool = True, links: bool = True, markdown: bool = False): """ Print tables describing the Network. @@ -128,6 +139,8 @@ class Network(SimComponent): "Switch": self.switch_nodes, "Server": self.server_nodes, "Computer": self.computer_nodes, + "Printer": self.printer_nodes, + "Wireless Router": self.wireless_routers, } if nodes: table = PrettyTable(["Node", "Type", "Operating State"]) From 27bee58bf755cdaa555c998cc4bee6463fc56b3f Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Wed, 27 Mar 2024 00:00:06 +0000 Subject: [PATCH 06/27] #2418 Fix broken property --- src/primaite/simulator/network/container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index 5ec47052..a4079fb8 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -140,7 +140,7 @@ class Network(SimComponent): "Server": self.server_nodes, "Computer": self.computer_nodes, "Printer": self.printer_nodes, - "Wireless Router": self.wireless_routers, + "Wireless Router": self.wireless_router_nodes, } if nodes: table = PrettyTable(["Node", "Type", "Operating State"]) From 8bb7f8a1775b8269c5d07ecb6b0969fb41a235e7 Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Wed, 27 Mar 2024 17:07:12 +0000 Subject: [PATCH 07/27] #2405 add application install and remove actions --- src/primaite/game/agent/actions.py | 46 +++++++ .../simulator/network/hardware/base.py | 121 +++++++++++++++++- src/primaite/simulator/system/software.py | 2 +- tests/conftest.py | 12 +- .../game_layer/test_actions.py | 26 ++++ 5 files changed, 203 insertions(+), 4 deletions(-) diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index b79fc985..7c31ae7e 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -219,6 +219,50 @@ class NodeApplicationFixAction(NodeApplicationAbstractAction): self.verb: str = "fix" +class NodeApplicationInstallAction(AbstractAction): + """Action which installs an application.""" + + def __init__( + self, manager: "ActionManager", num_nodes: int, application_name: str, ip_address: str, **kwargs + ) -> None: + super().__init__(manager=manager) + self.shape: Dict[str, int] = {"node_id": num_nodes} + self.application_name = application_name + self.ip_address = ip_address + + def form_request(self, node_id: int) -> List[str]: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + node_name = self.manager.get_node_name_by_idx(node_id) + if node_name is None: + return ["do_nothing"] + return [ + "network", + "node", + node_name, + "software_manager", + "application", + "install", + self.application_name, + self.ip_address, + ] + + +class NodeApplicationRemoveAction(AbstractAction): + """Action which removes/uninstalls an application.""" + + def __init__(self, manager: "ActionManager", num_nodes: int, application_name: str, **kwargs) -> None: + super().__init__(manager=manager) + self.shape: Dict[str, int] = {"node_id": num_nodes} + self.application_name = application_name + + def form_request(self, node_id: int) -> List[str]: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + node_name = self.manager.get_node_name_by_idx(node_id) + if node_name is None: + return ["do_nothing"] + return ["network", "node", node_name, "software_manager", "application", "uninstall", self.application_name] + + class NodeFolderAbstractAction(AbstractAction): """ Base class for folder actions. @@ -658,6 +702,8 @@ class ActionManager: "NODE_APPLICATION_SCAN": NodeApplicationScanAction, "NODE_APPLICATION_CLOSE": NodeApplicationCloseAction, "NODE_APPLICATION_FIX": NodeApplicationFixAction, + "NODE_APPLICATION_INSTALL": NodeApplicationInstallAction, + "NODE_APPLICATION_REMOVE": NodeApplicationRemoveAction, "NODE_FILE_SCAN": NodeFileScanAction, "NODE_FILE_CHECKHASH": NodeFileCheckhashAction, "NODE_FILE_DELETE": NodeFileDeleteAction, diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 38d20e1f..132fc8b1 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -5,7 +5,7 @@ import secrets from abc import ABC, abstractmethod from ipaddress import IPv4Address, IPv4Network from pathlib import Path -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Optional, Type, TypeVar, Union from prettytable import MARKDOWN, PrettyTable from pydantic import BaseModel, Field @@ -35,8 +35,11 @@ from primaite.simulator.system.core.software_manager import SoftwareManager from primaite.simulator.system.core.sys_log import SysLog from primaite.simulator.system.processes.process import Process from primaite.simulator.system.services.service import Service +from primaite.simulator.system.software import IOSoftware from primaite.utils.validators import IPV4Address +IOSoftwareClass = TypeVar("IOSoftwareClass", bound=IOSoftware) + _LOGGER = getLogger(__name__) @@ -843,12 +846,56 @@ class Node(SimComponent): ) rm.add_request("os", RequestType(func=self._os_request_manager, validator=_node_is_on)) + self._software_manager = RequestManager() + rm.add_request("software_manager", RequestType(func=self._software_manager, validator=_node_is_on)) + self._application_manager = RequestManager() + self._software_manager.add_request(name="application", request_type=RequestType(func=self._application_manager)) + + self._application_manager.add_request( + name="install", + request_type=RequestType( + func=lambda request, context: RequestResponse.from_bool( + self.application_install_action( + application=self._read_application_type(request[0]), ip_address=request[1] + ) + ) + ), + ) + + self._application_manager.add_request( + name="uninstall", + request_type=RequestType( + func=lambda request, context: RequestResponse.from_bool( + self.application_uninstall_action(application=self._read_application_type(request[0])) + ) + ), + ) + return rm def _install_system_software(self): """Install System Software - software that is usually provided with the OS.""" pass + def _read_application_type(self, application_class_str: str) -> Type[IOSoftwareClass]: + """Wrapper that converts the string from the request manager into the appropriate class for the application.""" + if application_class_str.lower() == "DoSBot".lower(): + from primaite.simulator.system.applications.red_applications.dos_bot import DoSBot + + return DoSBot + elif application_class_str.lower() == "DataManipulationBot".lower(): + from primaite.simulator.system.applications.red_applications.data_manipulation_bot import ( + DataManipulationBot, + ) + + return DataManipulationBot + elif application_class_str.lower() == "WebBrowser".lower(): + from primaite.simulator.system.applications.web_browser import WebBrowser + + return WebBrowser + else: + return 0 + def describe_state(self) -> Dict: """ Produce a dictionary describing the current state of this object. @@ -1257,6 +1304,78 @@ class Node(SimComponent): _LOGGER.info(f"Removed application {application.name} from node {self.hostname}") self._application_request_manager.remove_request(application.name) + def application_install_action(self, application: Application, ip_address: Optional[str] = None) -> bool: + """ + Install an application on this node and configure it. + + This method is useful for allowing agents to take this action. + + :param application: Application instance that has not been installed on any node yet. + :type application: Application + :parm + """ + if application in self: + _LOGGER.warning( + f"Can't add application {application.__name__}" + f"to node {self.hostname}. It's already installed." + ) + self.software_manager.install(application) + + application_instance = self.software_manager.software.get(str(application.__name__)) + self.applications[application_instance.uuid] = application_instance + application.parent = self + self.sys_log.info(f"Installed application {application.__name__}") + _LOGGER.debug(f"Added application {application.__name__} to node {self.hostname}") + self._application_request_manager.add_request( + application_instance.name, RequestType(func=application_instance._request_manager) + ) + + # Configure application if additional parameters are given + if ip_address: + from primaite.simulator.system.applications.red_applications.data_manipulation_bot import ( + DataManipulationBot, + ) + from primaite.simulator.system.applications.red_applications.dos_bot import DoSBot + + if application == DoSBot: + application_instance.configure(target_ip_address=IPv4Address(ip_address)) + elif application == DataManipulationBot: + application_instance.configure(server_ip_address=IPv4Address(ip_address)) + else: + pass + + if application in self: + return True + else: + return False + + def application_uninstall_action(self, application: Application) -> bool: + """ + Uninstall and completely remove application from this node. + + This method is useful for allowing agents to take this action. + + :param application: Application object that is currently associated with this node. + :type application: Application + """ + if application.__name__ not in self.software_manager.software: + _LOGGER.warning( + f"Can't remove application {application.__name__}" + f"from node {self.hostname}. It's not installed." + ) + return True + application_instance = self.software_manager.software.get( + str(application.__name__) + ) # This works because we can't have two applications with the same name on the same node + self.applications.pop(application_instance.uuid) + application.parent = None + self.sys_log.info(f"Uninstalled application {application.__name__}") + _LOGGER.info(f"Removed application {application.__name__} from node {self.hostname}") + self._application_request_manager.remove_request(application_instance.name) + self.software_manager.uninstall(application_instance.name) + if application_instance.name not in self.software_manager.software: + return True + else: + return False + def _shut_down_actions(self): """Actions to perform when the node is shut down.""" # Turn off all the services in the node diff --git a/src/primaite/simulator/system/software.py b/src/primaite/simulator/system/software.py index ab60adde..3ab32bc6 100644 --- a/src/primaite/simulator/system/software.py +++ b/src/primaite/simulator/system/software.py @@ -88,7 +88,7 @@ class Software(SimComponent): "The count of times the software has been scanned, defaults to 0." revealed_to_red: bool = False "Indicates if the software has been revealed to red agent, defaults is False." - software_manager: "SoftwareManager" = None + software_manager: Optional["SoftwareManager"] = None "An instance of Software Manager that is used by the parent node." sys_log: SysLog = None "An instance of SysLog that is used by the parent node." diff --git a/tests/conftest.py b/tests/conftest.py index 078a78bd..be76fc92 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -480,6 +480,8 @@ def game_and_agent(): {"type": "NODE_APPLICATION_SCAN"}, {"type": "NODE_APPLICATION_CLOSE"}, {"type": "NODE_APPLICATION_FIX"}, + {"type": "NODE_APPLICATION_INSTALL", "options": {"application_name": "DoSBot", "ip_address": "192.168.1.14"}}, + {"type": "NODE_APPLICATION_REMOVE", "options": {"application_name": "DoSBot"}}, {"type": "NODE_FILE_SCAN"}, {"type": "NODE_FILE_CHECKHASH"}, {"type": "NODE_FILE_DELETE"}, @@ -507,10 +509,16 @@ def game_and_agent(): nodes=[ { "node_name": "client_1", - "applications": [{"application_name": "WebBrowser"}], + "applications": [ + {"application_name": "WebBrowser"}, + {"application_name": "DoSBot"}, + ], "folders": [{"folder_name": "downloads", "files": [{"file_name": "cat.png"}]}], }, - {"node_name": "server_1", "services": [{"service_name": "DNSServer"}]}, + { + "node_name": "server_1", + "services": [{"service_name": "DNSServer"}], + }, {"node_name": "server_2", "services": [{"service_name": "WebServer"}]}, {"node_name": "router"}, ], diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index b3a52cd8..5ba58ee5 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -10,6 +10,7 @@ # 4. Check that the simulation has changed in the way that I expect. # 5. Repeat for all actions. +from ipaddress import IPv4Address from typing import Tuple import pytest @@ -455,3 +456,28 @@ def test_node_application_close_integration(game_and_agent: Tuple[PrimaiteGame, game.step() assert browser.operating_state == ApplicationOperatingState.CLOSED + + +def test_node_application_install_and_uninstall_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): + """Test that the NodeApplicationInstallAction and NodeApplicationRemoveAction can form a request and that + it is accepted by the simulation. + + When you initiate a install action, the Application will be installed and configured on the node. + The remove action will uninstall the application from the node.""" + game, agent = game_and_agent + + client_1 = game.simulation.network.get_node_by_hostname("client_1") + + assert client_1.software_manager.software.get("DoSBot") is None + + action = ("NODE_APPLICATION_INSTALL", {"node_id": 0}) + agent.store_action(action) + game.step() + + assert client_1.software_manager.software.get("DoSBot") is not None + + action = ("NODE_APPLICATION_REMOVE", {"node_id": 0}) + agent.store_action(action) + game.step() + + assert client_1.software_manager.software.get("DoSBot") is None From cddb39e8e90709de243e2b8e9b2fb938ed37900e Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Thu, 28 Mar 2024 10:43:57 +0000 Subject: [PATCH 08/27] #2405 update docstrings --- src/primaite/simulator/network/hardware/base.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 132fc8b1..1e29ceb6 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -1312,7 +1312,10 @@ class Node(SimComponent): :param application: Application instance that has not been installed on any node yet. :type application: Application - :parm + :param ip_address: IP address used to configure the application + (target IP for the DoSBot or server IP for the DataManipulationBot) + :type ip_address: str + :return: True if the application is installed successfully, otherwise False. """ if application in self: _LOGGER.warning( @@ -1356,6 +1359,7 @@ class Node(SimComponent): :param application: Application object that is currently associated with this node. :type application: Application + :return: True if the application is uninstalled successfully, otherwise False. """ if application.__name__ not in self.software_manager.software: _LOGGER.warning( From d5b5c7d47a84768a512006226680536a7be6862c Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Thu, 28 Mar 2024 11:02:26 +0000 Subject: [PATCH 09/27] #2405 simplify implementation --- .../simulator/network/hardware/base.py | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 1e29ceb6..c464e9bf 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -1325,23 +1325,17 @@ class Node(SimComponent): application_instance = self.software_manager.software.get(str(application.__name__)) self.applications[application_instance.uuid] = application_instance - application.parent = self - self.sys_log.info(f"Installed application {application.__name__}") - _LOGGER.debug(f"Added application {application.__name__} to node {self.hostname}") + self.sys_log.info(f"Installed application {application_instance.name}") + _LOGGER.debug(f"Added application {application_instance.name} to node {self.hostname}") self._application_request_manager.add_request( application_instance.name, RequestType(func=application_instance._request_manager) ) # Configure application if additional parameters are given if ip_address: - from primaite.simulator.system.applications.red_applications.data_manipulation_bot import ( - DataManipulationBot, - ) - from primaite.simulator.system.applications.red_applications.dos_bot import DoSBot - - if application == DoSBot: + if application_instance.name == "DoSBot": application_instance.configure(target_ip_address=IPv4Address(ip_address)) - elif application == DataManipulationBot: + elif application_instance.name == "DataManipulationBot": application_instance.configure(server_ip_address=IPv4Address(ip_address)) else: pass @@ -1370,11 +1364,12 @@ class Node(SimComponent): str(application.__name__) ) # This works because we can't have two applications with the same name on the same node self.applications.pop(application_instance.uuid) - application.parent = None - self.sys_log.info(f"Uninstalled application {application.__name__}") - _LOGGER.info(f"Removed application {application.__name__} from node {self.hostname}") + application_instance.parent = None + self.sys_log.info(f"Uninstalled application {application_instance.name}") + _LOGGER.info(f"Removed application {application_instance.name} from node {self.hostname}") self._application_request_manager.remove_request(application_instance.name) self.software_manager.uninstall(application_instance.name) + if application_instance.name not in self.software_manager.software: return True else: From 9b6135524efcf4f1aa0f05a9c0f02f9429e27579 Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Thu, 28 Mar 2024 11:08:30 +0000 Subject: [PATCH 10/27] #2504 update application_install_action docstring --- src/primaite/simulator/network/hardware/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index c464e9bf..239ef687 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -1310,7 +1310,7 @@ class Node(SimComponent): This method is useful for allowing agents to take this action. - :param application: Application instance that has not been installed on any node yet. + :param application: Application object that has not been installed on any node yet. :type application: Application :param ip_address: IP address used to configure the application (target IP for the DoSBot or server IP for the DataManipulationBot) From 4301f3fdba18a013c518e20c7e56d91ff4a63344 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 12:06:23 +0000 Subject: [PATCH 11/27] #2418 Add Printer and Wireless router to config parser --- src/primaite/game/game.py | 15 +++++++++++++-- .../network/hardware/nodes/host/server.py | 6 ++++++ tests/assets/configs/test_primaite_session.yaml | 6 ++++++ .../test_primaite_session.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index 05b76679..6ba7e63c 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -15,10 +15,11 @@ from primaite.game.science import graph_has_cycle, topological_sort from primaite.simulator.network.hardware.base import NodeOperatingState from primaite.simulator.network.hardware.nodes.host.computer import Computer from primaite.simulator.network.hardware.nodes.host.host_node import NIC -from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.network.hardware.nodes.host.server import Printer, Server from primaite.simulator.network.hardware.nodes.network.firewall import Firewall from primaite.simulator.network.hardware.nodes.network.router import Router from primaite.simulator.network.hardware.nodes.network.switch import Switch +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter from primaite.simulator.network.nmne import set_nmne_config from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.sim_container import Simulation @@ -273,8 +274,18 @@ class PrimaiteGame: new_node = Router.from_config(node_cfg) elif n_type == "firewall": new_node = Firewall.from_config(node_cfg) + elif n_type == "wireless_router": + new_node = WirelessRouter.from_config(node_cfg) + elif n_type == "printer": + new_node = Printer( + hostname=node_cfg["hostname"], + ip_address=node_cfg["ip_address"], + subnet_mask=node_cfg["subnet_mask"], + ) else: - _LOGGER.warning(f"invalid node type {n_type} in config") + msg = f"invalid node type {n_type} in config" + _LOGGER.error(msg) + raise ValueError(msg) if "services" in node_cfg: for service_cfg in node_cfg["services"]: new_service = None diff --git a/src/primaite/simulator/network/hardware/nodes/host/server.py b/src/primaite/simulator/network/hardware/nodes/host/server.py index 9f5157ad..593cd0dd 100644 --- a/src/primaite/simulator/network/hardware/nodes/host/server.py +++ b/src/primaite/simulator/network/hardware/nodes/host/server.py @@ -28,3 +28,9 @@ class Server(HostNode): * Applications: * Web Browser """ + + +class Printer(HostNode): + """Printer? I don't even know her!.""" + + # TODO: Implement printer-specific behaviour diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index a8b33032..e1b0ac7b 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -681,6 +681,12 @@ simulation: - ref: client_2_dns_client type: DNSClient + - ref: HP_LaserJet_Pro_4102fdn_printer + type: printer + hostname: HP_LaserJet_Pro_4102fdn_printer + ip_address: 192.168.10.99 + subnet_mask: 255.255.255.0 + links: - ref: router_1___switch_1 endpoint_a_ref: router_1 diff --git a/tests/e2e_integration_tests/test_primaite_session.py b/tests/e2e_integration_tests/test_primaite_session.py index c45a4690..7febe39a 100644 --- a/tests/e2e_integration_tests/test_primaite_session.py +++ b/tests/e2e_integration_tests/test_primaite_session.py @@ -29,7 +29,7 @@ class TestPrimaiteSession: assert session.env assert session.env.game.simulation.network - assert len(session.env.game.simulation.network.nodes) == 10 + assert len(session.env.game.simulation.network.nodes) == 11 @pytest.mark.skip(reason="Session is not being maintained and will be removed in the subsequent beta release.") @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) From b12dee73ba5ac4e4b29713ed49cee5d9c929d47f Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 13:21:22 +0000 Subject: [PATCH 12/27] #2418 Fix wireless router from config --- .../network/hardware/nodes/network/router.py | 2 +- .../hardware/nodes/network/wireless_router.py | 69 ++++++++++++++++++- .../assets/configs/test_primaite_session.yaml | 27 ++++++++ .../test_primaite_session.py | 9 ++- 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/primaite/simulator/network/hardware/nodes/network/router.py b/src/primaite/simulator/network/hardware/nodes/network/router.py index d2b47c1a..de308547 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/router.py @@ -1418,7 +1418,7 @@ class Router(NetworkNode): :return: Configured router. :rtype: Router """ - router = Router( + router = cls( hostname=cfg["hostname"], num_ports=int(cfg.get("num_ports", "5")), operating_state=NodeOperatingState.ON diff --git a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py index 3e8d715f..4bd3d101 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py @@ -1,10 +1,14 @@ +from ipaddress import IPv4Address from typing import Any, Dict, Union from pydantic import validate_call from primaite.simulator.network.airspace import AirSpaceFrequency, IPWirelessNetworkInterface -from primaite.simulator.network.hardware.nodes.network.router import Router, RouterInterface +from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router, RouterInterface from primaite.simulator.network.transmission.data_link_layer import Frame +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port from primaite.utils.validators import IPV4Address @@ -209,3 +213,66 @@ class WirelessRouter(Router): raise NotImplementedError( "Please use the 'configure_wireless_access_point' and 'configure_router_interface' functions." ) + + @classmethod + def from_config(cls, cfg: Dict) -> "WirelessRouter": + """Generate the wireless router from config. + + Schema: + - hostname (str): unique name for this router. + - router_interface (dict): The values should be another dict specifying + - ip_address (str) + - subnet_mask (str) + - wireless_access_point (dict): Dict with + - ip address, + - subnet mask, + - frequency, (string: either WIFI_2_4 or WIFI_5) + - acl (dict): Dict with integers from 1 - max_acl_rules as keys. The key defines the position within the ACL + where the rule will be added (lower number is resolved first). The values should describe valid ACL + Rules as: + - action (str): either PERMIT or DENY + - src_port (str, optional): the named port such as HTTP, HTTPS, or POSTGRES_SERVER + - dst_port (str, optional): the named port such as HTTP, HTTPS, or POSTGRES_SERVER + - protocol (str, optional): the named IP protocol such as ICMP, TCP, or UDP + - src_ip_address (str, optional): IP address octet written in base 10 + - dst_ip_address (str, optional): IP address octet written in base 10 + + :param cfg: Config dictionary + :type cfg: Dict + :return: WirelessRouter instance. + :rtype: WirelessRouter + """ + operating_state = ( + NodeOperatingState.ON if not (p := cfg.get("operating_state")) else NodeOperatingState[p.upper()] + ) + router = cls(hostname=cfg["hostname"], operating_state=operating_state) + if "router_interface" in cfg: + ip_address = cfg["router_interface"]["ip_address"] + subnet_mask = cfg["router_interface"]["subnet_mask"] + router.configure_router_interface(ip_address=ip_address, subnet_mask=subnet_mask) + if "wireless_access_point" in cfg: + ip_address = cfg["wireless_access_point"]["ip_address"] + subnet_mask = cfg["wireless_access_point"]["subnet_mask"] + frequency = AirSpaceFrequency[cfg["wireless_access_point"]["frequency"]] + router.configure_wireless_access_point(ip_address=ip_address, subnet_mask=subnet_mask, frequency=frequency) + + if "acl" in cfg: + for r_num, r_cfg in cfg["acl"].items(): + router.acl.add_rule( + action=ACLAction[r_cfg["action"]], + src_port=None if not (p := r_cfg.get("src_port")) else Port[p], + dst_port=None if not (p := r_cfg.get("dst_port")) else Port[p], + protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], + src_ip_address=r_cfg.get("src_ip"), + dst_ip_address=r_cfg.get("dst_ip"), + position=r_num, + ) + if "routes" in cfg: + for route in cfg.get("routes"): + router.route_table.add_route( + address=IPv4Address(route.get("address")), + subnet_mask=IPv4Address(route.get("subnet_mask", "255.255.255.0")), + next_hop_ip_address=IPv4Address(route.get("next_hop_ip_address")), + metric=float(route.get("metric", 0)), + ) + return router diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index e1b0ac7b..0ce3fee7 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -687,6 +687,33 @@ simulation: ip_address: 192.168.10.99 subnet_mask: 255.255.255.0 + - ref: router_2 + type: wireless_router + hostname: router_2 + router_interface: + ip_address: 192.169.1.1 + subnet_mask: 255.255.255.0 + wireless_access_point: + ip_address: 192.169.1.1 + subnet_mask: 255.255.255.0 + frequency: WIFI_2_4 + acl: + 0: + action: PERMIT + src_port: POSTGRES_SERVER + dst_port: POSTGRES_SERVER + 1: + action: PERMIT + src_port: DNS + dst_port: DNS + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + links: - ref: router_1___switch_1 endpoint_a_ref: router_1 diff --git a/tests/e2e_integration_tests/test_primaite_session.py b/tests/e2e_integration_tests/test_primaite_session.py index 7febe39a..32f134a3 100644 --- a/tests/e2e_integration_tests/test_primaite_session.py +++ b/tests/e2e_integration_tests/test_primaite_session.py @@ -1,6 +1,8 @@ import pydantic import pytest +from primaite.simulator.network.hardware.nodes.host.server import Printer +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter from tests import TEST_ASSETS_ROOT from tests.conftest import TempPrimaiteSession @@ -11,7 +13,6 @@ MISCONFIGURED_PATH = TEST_ASSETS_ROOT / "configs/bad_primaite_session.yaml" MULTI_AGENT_PATH = TEST_ASSETS_ROOT / "configs/multi_agent_session.yaml" -# @pytest.mark.skip(reason="no way of currently testing this") class TestPrimaiteSession: @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) def test_creating_session(self, temp_primaite_session): @@ -29,7 +30,11 @@ class TestPrimaiteSession: assert session.env assert session.env.game.simulation.network - assert len(session.env.game.simulation.network.nodes) == 11 + assert len(session.env.game.simulation.network.nodes) == 12 + wireless = session.env.game.simulation.network.get_node_by_hostname("router_2") + assert isinstance(wireless, WirelessRouter) + printer = session.env.game.simulation.network.get_node_by_hostname("HP_LaserJet_Pro_4102fdn_printer") + assert isinstance(printer, Printer) @pytest.mark.skip(reason="Session is not being maintained and will be removed in the subsequent beta release.") @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) From e21c59dff1d149abd21abca2a73e42e66d54ac95 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 16:23:39 +0000 Subject: [PATCH 13/27] #2418 - Change test config --- tests/assets/configs/test_primaite_session.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index 0ce3fee7..b131c1b7 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -694,7 +694,7 @@ simulation: ip_address: 192.169.1.1 subnet_mask: 255.255.255.0 wireless_access_point: - ip_address: 192.169.1.1 + ip_address: 192.170.1.1 subnet_mask: 255.255.255.0 frequency: WIFI_2_4 acl: From c29c3971fab30467cf6b770a6ce7c7f1b1cb3bb9 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 21:48:17 +0000 Subject: [PATCH 14/27] #2418 - add wildcard masks and from_config tests to routers --- .../network/hardware/nodes/network/router.py | 2 + .../hardware/nodes/network/wireless_router.py | 2 + .../_network/_hardware/nodes/test_router.py | 111 ++++++++++++++++++ .../_hardware/nodes/test_wireless_router.py | 97 +++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py create mode 100644 tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py diff --git a/src/primaite/simulator/network/hardware/nodes/network/router.py b/src/primaite/simulator/network/hardware/nodes/network/router.py index de308547..102eb7dc 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/router.py @@ -1441,6 +1441,8 @@ class Router(NetworkNode): protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], src_ip_address=r_cfg.get("src_ip"), dst_ip_address=r_cfg.get("dst_ip"), + src_wildcard_mask=r_cfg.get("src_wildcard_mask"), + dst_wildcard_mask=r_cfg.get("dst_wildcard_mask"), position=r_num, ) if "routes" in cfg: diff --git a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py index 4bd3d101..62332269 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py @@ -265,6 +265,8 @@ class WirelessRouter(Router): protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], src_ip_address=r_cfg.get("src_ip"), dst_ip_address=r_cfg.get("dst_ip"), + src_wildcard_mask=r_cfg.get("src_wildcard_mask"), + dst_wildcard_mask=r_cfg.get("dst_wildcard_mask"), position=r_num, ) if "routes" in cfg: diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py new file mode 100644 index 00000000..be74a721 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py @@ -0,0 +1,111 @@ +from ipaddress import IPv4Address + +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port + + +def test_wireless_router_from_config(): + cfg = { + "ref": "router_1", + "type": "router", + "hostname": "router_1", + "num_ports": 6, + "ports": { + 1: { + "ip_address": "192.168.1.1", + "subnet_mask": "255.255.255.0", + }, + 2: { + "ip_address": "192.168.2.1", + "subnet_mask": "255.255.255.0", + }, + }, + "acl": { + 0: { + "action": "PERMIT", + "src_port": "POSTGRES_SERVER", + "dst_port": "POSTGRES_SERVER", + }, + 1: { + "action": "PERMIT", + "protocol": "ICMP", + }, + 2: { + "action": "PERMIT", + "src_ip": "100.100.100.1", + "dst_ip": "100.100.101.1", + }, + 3: { + "action": "PERMIT", + "src_ip": "100.100.102.0", + "dst_ip": "100.100.103.0", + "src_wildcard_mask": "0.0.0.255", + "dst_wildcard_mask": "0.0.0.255", + }, + 20: { + "action": "DENY", + }, + }, + } + + rt = Router.from_config(cfg=cfg) + + assert rt.num_ports == 6 + + assert rt.network_interface[1].ip_address == IPv4Address("192.168.1.1") + assert rt.network_interface[1].subnet_mask == IPv4Address("255.255.255.0") + + assert rt.network_interface[2].ip_address == IPv4Address("192.168.2.1") + assert rt.network_interface[2].subnet_mask == IPv4Address("255.255.255.0") + + assert not rt.network_interface[3].enabled + assert not rt.network_interface[4].enabled + assert not rt.network_interface[5].enabled + assert not rt.network_interface[6].enabled + + r0 = rt.acl.acl[0] + assert r0.action == ACLAction.PERMIT + assert r0.src_port == r0.dst_port == Port.POSTGRES_SERVER + assert r0.src_ip_address == r0.dst_ip_address == r0.dst_wildcard_mask == r0.src_wildcard_mask == r0.protocol == None + + r1 = rt.acl.acl[1] + assert r1.action == ACLAction.PERMIT + assert r1.protocol == IPProtocol.ICMP + assert ( + r1.src_ip_address + == r1.dst_ip_address + == r1.dst_wildcard_mask + == r1.src_wildcard_mask + == r1.src_port + == r1.dst_port + == None + ) + + r2 = rt.acl.acl[2] + assert r2.action == ACLAction.PERMIT + assert r2.src_ip_address == IPv4Address("100.100.100.1") + assert r2.dst_ip_address == IPv4Address("100.100.101.1") + assert r2.src_wildcard_mask == r2.dst_wildcard_mask == None + assert r2.src_port == r2.dst_port == r2.protocol == None + + r3 = rt.acl.acl[3] + assert r3.action == ACLAction.PERMIT + assert r3.src_ip_address == IPv4Address("100.100.102.0") + assert r3.dst_ip_address == IPv4Address("100.100.103.0") + assert r3.src_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.dst_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.src_port == r3.dst_port == r3.protocol == None + + r20 = rt.acl.acl[20] + assert r20.action == ACLAction.DENY + assert ( + r20.src_ip_address + == r20.dst_ip_address + == r20.src_wildcard_mask + == r20.dst_wildcard_mask + == r20.src_port + == r20.dst_port + == r20.protocol + == None + ) diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py new file mode 100644 index 00000000..494f5a15 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py @@ -0,0 +1,97 @@ +from ipaddress import IPv4Address + +from primaite.simulator.network.hardware.nodes.network.router import ACLAction +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port + + +def test_wireless_router_from_config(): + cfg = { + "ref": "router_2", + "type": "wireless_router", + "hostname": "router_2", + "router_interface": { + "ip_address": "192.168.1.1", + "subnet_mask": "255.255.255.0", + }, + "wireless_access_point": { + "ip_address": "192.170.1.1", + "subnet_mask": "255.255.255.0", + "frequency": "WIFI_2_4", + }, + "acl": { + 0: { + "action": "PERMIT", + "src_port": "POSTGRES_SERVER", + "dst_port": "POSTGRES_SERVER", + }, + 1: { + "action": "PERMIT", + "protocol": "ICMP", + }, + 2: { + "action": "PERMIT", + "src_ip": "100.100.100.1", + "dst_ip": "100.100.101.1", + }, + 3: { + "action": "PERMIT", + "src_ip": "100.100.102.0", + "dst_ip": "100.100.103.0", + "src_wildcard_mask": "0.0.0.255", + "dst_wildcard_mask": "0.0.0.255", + }, + 20: { + "action": "DENY", + }, + }, + } + + rt = WirelessRouter.from_config(cfg=cfg) + + r0 = rt.acl.acl[0] + assert r0.action == ACLAction.PERMIT + assert r0.src_port == r0.dst_port == Port.POSTGRES_SERVER + assert r0.src_ip_address == r0.dst_ip_address == r0.dst_wildcard_mask == r0.src_wildcard_mask == r0.protocol == None + + r1 = rt.acl.acl[1] + assert r1.action == ACLAction.PERMIT + assert r1.protocol == IPProtocol.ICMP + assert ( + r1.src_ip_address + == r1.dst_ip_address + == r1.dst_wildcard_mask + == r1.src_wildcard_mask + == r1.src_port + == r1.dst_port + == None + ) + + r2 = rt.acl.acl[2] + assert r2.action == ACLAction.PERMIT + assert r2.src_ip_address == IPv4Address("100.100.100.1") + assert r2.dst_ip_address == IPv4Address("100.100.101.1") + assert r2.src_wildcard_mask == r2.dst_wildcard_mask == None + assert r2.src_port == r2.dst_port == r2.protocol == None + + r3 = rt.acl.acl[3] + assert r3.action == ACLAction.PERMIT + assert r3.src_ip_address == IPv4Address("100.100.102.0") + assert r3.dst_ip_address == IPv4Address("100.100.103.0") + assert r3.src_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.dst_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.src_port == r3.dst_port == r3.protocol == None + + r20 = rt.acl.acl[20] + assert r20.action == ACLAction.DENY + assert ( + r20.src_ip_address + == r20.dst_ip_address + == r20.src_wildcard_mask + == r20.dst_wildcard_mask + == r20.src_port + == r20.dst_port + == r20.protocol + == None + ) From 09caa55c6524865be855ae72f2bf24261f513d1c Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 21:51:27 +0000 Subject: [PATCH 15/27] #2418 - Add printer and wireless router as node types in network show --- src/primaite/simulator/network/container.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index 0e970c3d..5ec47052 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -8,6 +8,7 @@ from prettytable import MARKDOWN, PrettyTable from primaite import getLogger from primaite.simulator.core import RequestManager, RequestType, SimComponent from primaite.simulator.network.hardware.base import Link, Node, WiredNetworkInterface +from primaite.simulator.network.hardware.nodes.host.server import Printer from primaite.simulator.system.applications.application import Application from primaite.simulator.system.services.service import Service @@ -110,6 +111,16 @@ class Network(SimComponent): """The Firewalls in the Network.""" return [node for node in self.nodes.values() if node.__class__.__name__ == "Firewall"] + @property + def printer_nodes(self) -> List[Node]: + """The printers on the network.""" + return [node for node in self.nodes.values() if isinstance(node, Printer)] + + @property + def wireless_router_nodes(self) -> List[Node]: + """The Routers in the Network.""" + return [node for node in self.nodes.values() if node.__class__.__name__ == "WirelessRouter"] + def show(self, nodes: bool = True, ip_addresses: bool = True, links: bool = True, markdown: bool = False): """ Print tables describing the Network. @@ -128,6 +139,8 @@ class Network(SimComponent): "Switch": self.switch_nodes, "Server": self.server_nodes, "Computer": self.computer_nodes, + "Printer": self.printer_nodes, + "Wireless Router": self.wireless_routers, } if nodes: table = PrettyTable(["Node", "Type", "Operating State"]) From 350b98831851518f26aa3b34c94f124e1a1041f7 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Wed, 27 Mar 2024 00:00:06 +0000 Subject: [PATCH 16/27] #2418 Fix broken property --- src/primaite/simulator/network/container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index 5ec47052..a4079fb8 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -140,7 +140,7 @@ class Network(SimComponent): "Server": self.server_nodes, "Computer": self.computer_nodes, "Printer": self.printer_nodes, - "Wireless Router": self.wireless_routers, + "Wireless Router": self.wireless_router_nodes, } if nodes: table = PrettyTable(["Node", "Type", "Operating State"]) From 8612842b74f7520c58fa37401925a17e5747ed2c Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Thu, 28 Mar 2024 12:01:36 +0000 Subject: [PATCH 17/27] #2405 remove .lower from _read_application_type, rename _software_manager to _software_request_manager in base.py --- src/primaite/simulator/network/hardware/base.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 239ef687..721bc1cd 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -846,10 +846,10 @@ class Node(SimComponent): ) rm.add_request("os", RequestType(func=self._os_request_manager, validator=_node_is_on)) - self._software_manager = RequestManager() - rm.add_request("software_manager", RequestType(func=self._software_manager, validator=_node_is_on)) + self._software_request_manager = RequestManager() + rm.add_request("software_manager", RequestType(func=self._software_request_manager, validator=_node_is_on)) self._application_manager = RequestManager() - self._software_manager.add_request(name="application", request_type=RequestType(func=self._application_manager)) + self._software_request_manager.add_request(name="application", request_type=RequestType(func=self._application_manager)) self._application_manager.add_request( name="install", @@ -879,17 +879,17 @@ class Node(SimComponent): def _read_application_type(self, application_class_str: str) -> Type[IOSoftwareClass]: """Wrapper that converts the string from the request manager into the appropriate class for the application.""" - if application_class_str.lower() == "DoSBot".lower(): + if application_class_str == "DoSBot": from primaite.simulator.system.applications.red_applications.dos_bot import DoSBot return DoSBot - elif application_class_str.lower() == "DataManipulationBot".lower(): + elif application_class_str == "DataManipulationBot": from primaite.simulator.system.applications.red_applications.data_manipulation_bot import ( DataManipulationBot, ) return DataManipulationBot - elif application_class_str.lower() == "WebBrowser".lower(): + elif application_class_str == "WebBrowser": from primaite.simulator.system.applications.web_browser import WebBrowser return WebBrowser From f83d9cb1b01c5ddef20337a80de4da36ae176050 Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Thu, 28 Mar 2024 12:14:05 +0000 Subject: [PATCH 18/27] #2405 refactor application_uninstall_action to re-use existing code in uninstall_application --- src/primaite/simulator/network/hardware/base.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 721bc1cd..754c7a24 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -849,7 +849,9 @@ class Node(SimComponent): self._software_request_manager = RequestManager() rm.add_request("software_manager", RequestType(func=self._software_request_manager, validator=_node_is_on)) self._application_manager = RequestManager() - self._software_request_manager.add_request(name="application", request_type=RequestType(func=self._application_manager)) + self._software_request_manager.add_request( + name="application", request_type=RequestType(func=self._application_manager) + ) self._application_manager.add_request( name="install", @@ -1321,8 +1323,9 @@ class Node(SimComponent): _LOGGER.warning( f"Can't add application {application.__name__}" + f"to node {self.hostname}. It's already installed." ) - self.software_manager.install(application) + return True + self.software_manager.install(application) application_instance = self.software_manager.software.get(str(application.__name__)) self.applications[application_instance.uuid] = application_instance self.sys_log.info(f"Installed application {application_instance.name}") @@ -1360,14 +1363,11 @@ class Node(SimComponent): f"Can't remove application {application.__name__}" + f"from node {self.hostname}. It's not installed." ) return True + application_instance = self.software_manager.software.get( str(application.__name__) ) # This works because we can't have two applications with the same name on the same node - self.applications.pop(application_instance.uuid) - application_instance.parent = None - self.sys_log.info(f"Uninstalled application {application_instance.name}") - _LOGGER.info(f"Removed application {application_instance.name} from node {self.hostname}") - self._application_request_manager.remove_request(application_instance.name) + self.uninstall_application(application_instance) self.software_manager.uninstall(application_instance.name) if application_instance.name not in self.software_manager.software: From 1e1eea47f139ae24c1b413cef9041b399b7210df Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Thu, 28 Mar 2024 14:08:08 +0000 Subject: [PATCH 19/27] #2405 add e2e test for application install and uninstall, refactor input params --- src/primaite/game/agent/actions.py | 19 +- .../configs/test_application_install.yaml | 986 ++++++++++++++++++ tests/conftest.py | 4 +- .../test_uc2_data_manipulation_scenario.py | 29 + .../game_layer/test_actions.py | 4 +- 5 files changed, 1026 insertions(+), 16 deletions(-) create mode 100644 tests/assets/configs/test_application_install.yaml diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index 7c31ae7e..e22c882c 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -222,15 +222,11 @@ class NodeApplicationFixAction(NodeApplicationAbstractAction): class NodeApplicationInstallAction(AbstractAction): """Action which installs an application.""" - def __init__( - self, manager: "ActionManager", num_nodes: int, application_name: str, ip_address: str, **kwargs - ) -> None: + def __init__(self, manager: "ActionManager", num_nodes: int, **kwargs) -> None: super().__init__(manager=manager) self.shape: Dict[str, int] = {"node_id": num_nodes} - self.application_name = application_name - self.ip_address = ip_address - def form_request(self, node_id: int) -> List[str]: + def form_request(self, node_id: int, application_name: str, ip_address: str) -> List[str]: """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" node_name = self.manager.get_node_name_by_idx(node_id) if node_name is None: @@ -242,25 +238,24 @@ class NodeApplicationInstallAction(AbstractAction): "software_manager", "application", "install", - self.application_name, - self.ip_address, + application_name, + ip_address, ] class NodeApplicationRemoveAction(AbstractAction): """Action which removes/uninstalls an application.""" - def __init__(self, manager: "ActionManager", num_nodes: int, application_name: str, **kwargs) -> None: + def __init__(self, manager: "ActionManager", num_nodes: int, **kwargs) -> None: super().__init__(manager=manager) self.shape: Dict[str, int] = {"node_id": num_nodes} - self.application_name = application_name - def form_request(self, node_id: int) -> List[str]: + def form_request(self, node_id: int, application_name: str) -> List[str]: """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" node_name = self.manager.get_node_name_by_idx(node_id) if node_name is None: return ["do_nothing"] - return ["network", "node", node_name, "software_manager", "application", "uninstall", self.application_name] + return ["network", "node", node_name, "software_manager", "application", "uninstall", application_name] class NodeFolderAbstractAction(AbstractAction): diff --git a/tests/assets/configs/test_application_install.yaml b/tests/assets/configs/test_application_install.yaml new file mode 100644 index 00000000..c1908fc4 --- /dev/null +++ b/tests/assets/configs/test_application_install.yaml @@ -0,0 +1,986 @@ +training_config: + rl_framework: SB3 + rl_algorithm: PPO + seed: 333 + n_learn_episodes: 1 + n_eval_episodes: 5 + max_steps_per_episode: 128 + deterministic_eval: false + n_agents: 1 + agent_references: + - defender + +io_settings: + save_agent_actions: true + save_step_metadata: false + save_pcap_logs: false + save_sys_logs: false + + +game: + max_episode_length: 128 + ports: + - HTTP + - POSTGRES_SERVER + protocols: + - ICMP + - TCP + - UDP + thresholds: + nmne: + high: 10 + medium: 5 + low: 0 + +agents: + - ref: client_2_green_user + team: GREEN + type: ProbabilisticAgent + agent_settings: + action_probabilities: + 0: 0.3 + 1: 0.6 + 2: 0.1 + observation_space: + type: UC2GreenObservation + action_space: + action_list: + - type: DONOTHING + - type: NODE_APPLICATION_EXECUTE + options: + nodes: + - node_name: client_2 + applications: + - application_name: WebBrowser + - application_name: DatabaseClient + max_folders_per_node: 1 + max_files_per_folder: 1 + max_services_per_node: 1 + max_applications_per_node: 2 + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 + 2: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 1 + + reward_function: + reward_components: + - type: WEBPAGE_UNAVAILABLE_PENALTY + weight: 0.25 + options: + node_hostname: client_2 + - type: GREEN_ADMIN_DATABASE_UNREACHABLE_PENALTY + weight: 0.05 + options: + node_hostname: client_2 + + - ref: client_1_green_user + team: GREEN + type: ProbabilisticAgent + agent_settings: + action_probabilities: + 0: 0.3 + 1: 0.6 + 2: 0.1 + observation_space: + type: UC2GreenObservation + action_space: + action_list: + - type: DONOTHING + - type: NODE_APPLICATION_EXECUTE + options: + nodes: + - node_name: client_1 + applications: + - application_name: WebBrowser + - application_name: DatabaseClient + max_folders_per_node: 1 + max_files_per_folder: 1 + max_services_per_node: 1 + max_applications_per_node: 2 + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 + 2: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 1 + + reward_function: + reward_components: + - type: WEBPAGE_UNAVAILABLE_PENALTY + weight: 0.25 + options: + node_hostname: client_1 + - type: GREEN_ADMIN_DATABASE_UNREACHABLE_PENALTY + weight: 0.05 + options: + node_hostname: client_1 + + + + + + - ref: data_manipulation_attacker + team: RED + type: RedDatabaseCorruptingAgent + + observation_space: + type: UC2RedObservation + options: + nodes: {} + + action_space: + action_list: + - type: DONOTHING + - type: NODE_APPLICATION_EXECUTE + options: + nodes: + - node_name: client_1 + applications: + - application_name: DataManipulationBot + - node_name: client_2 + applications: + - application_name: DataManipulationBot + max_folders_per_node: 1 + max_files_per_folder: 1 + max_services_per_node: 1 + + reward_function: + reward_components: + - type: DUMMY + + agent_settings: # options specific to this particular agent type, basically args of __init__(self) + start_settings: + start_step: 25 + frequency: 20 + variance: 5 + + - ref: defender + team: BLUE + type: ProxyAgent + + observation_space: + type: UC2BlueObservation + options: + num_services_per_node: 1 + num_folders_per_node: 1 + num_files_per_folder: 1 + num_nics_per_node: 2 + nodes: + - node_hostname: domain_controller + services: + - service_name: DNSServer + - node_hostname: web_server + services: + - service_name: WebServer + - node_hostname: database_server + folders: + - folder_name: database + files: + - file_name: database.db + - node_hostname: backup_server + - node_hostname: security_suite + - node_hostname: client_1 + - node_hostname: client_2 + links: + - link_ref: router_1___switch_1 + - link_ref: router_1___switch_2 + - link_ref: switch_1___domain_controller + - link_ref: switch_1___web_server + - link_ref: switch_1___database_server + - link_ref: switch_1___backup_server + - link_ref: switch_1___security_suite + - link_ref: switch_2___client_1 + - link_ref: switch_2___client_2 + - link_ref: switch_2___security_suite + acl: + options: + max_acl_rules: 10 + router_hostname: router_1 + ip_address_order: + - node_hostname: domain_controller + nic_num: 1 + - node_hostname: web_server + nic_num: 1 + - node_hostname: database_server + nic_num: 1 + - node_hostname: backup_server + nic_num: 1 + - node_hostname: security_suite + nic_num: 1 + - node_hostname: client_1 + nic_num: 1 + - node_hostname: client_2 + nic_num: 1 + - node_hostname: security_suite + nic_num: 2 + ics: null + + action_space: + action_list: + - type: DONOTHING + - type: NODE_SERVICE_SCAN + - type: NODE_SERVICE_STOP + - type: NODE_SERVICE_START + - type: NODE_SERVICE_PAUSE + - type: NODE_SERVICE_RESUME + - type: NODE_SERVICE_RESTART + - type: NODE_SERVICE_DISABLE + - type: NODE_SERVICE_ENABLE + - type: NODE_SERVICE_FIX + - type: NODE_FILE_SCAN + - type: NODE_FILE_CHECKHASH + - type: NODE_FILE_DELETE + - type: NODE_FILE_REPAIR + - type: NODE_FILE_RESTORE + - type: NODE_FOLDER_SCAN + - type: NODE_FOLDER_CHECKHASH + - type: NODE_FOLDER_REPAIR + - type: NODE_FOLDER_RESTORE + - type: NODE_OS_SCAN + - type: NODE_SHUTDOWN + - type: NODE_STARTUP + - type: NODE_RESET + - type: NETWORK_ACL_ADDRULE + options: + target_router_hostname: router_1 + - type: NETWORK_ACL_REMOVERULE + options: + target_router_hostname: router_1 + - type: NETWORK_NIC_ENABLE + - type: NETWORK_NIC_DISABLE + - type: NODE_APPLICATION_INSTALL + - type: NODE_APPLICATION_REMOVE + - type: NODE_APPLICATION_EXECUTE + + action_map: + 0: + action: DONOTHING + options: {} + # scan webapp service + 1: + action: NODE_SERVICE_SCAN + options: + node_id: 1 + service_id: 0 + # stop webapp service + 2: + action: NODE_SERVICE_STOP + options: + node_id: 1 + service_id: 0 + # start webapp service + 3: + action: "NODE_SERVICE_START" + options: + node_id: 1 + service_id: 0 + 4: + action: "NODE_SERVICE_PAUSE" + options: + node_id: 1 + service_id: 0 + 5: + action: "NODE_SERVICE_RESUME" + options: + node_id: 1 + service_id: 0 + 6: + action: "NODE_SERVICE_RESTART" + options: + node_id: 1 + service_id: 0 + 7: + action: "NODE_SERVICE_DISABLE" + options: + node_id: 1 + service_id: 0 + 8: + action: "NODE_SERVICE_ENABLE" + options: + node_id: 1 + service_id: 0 + 9: # check database.db file + action: "NODE_FILE_SCAN" + options: + node_id: 2 + folder_id: 0 + file_id: 0 + 10: + action: "NODE_FILE_SCAN" # CHECKHASH replaced by SCAN - but the behaviour is the same in this context. + options: + node_id: 2 + folder_id: 0 + file_id: 0 + 11: + action: "NODE_FILE_DELETE" + options: + node_id: 2 + folder_id: 0 + file_id: 0 + 12: + action: "NODE_FILE_REPAIR" + options: + node_id: 2 + folder_id: 0 + file_id: 0 + 13: + action: "NODE_SERVICE_FIX" + options: + node_id: 2 + service_id: 0 + 14: + action: "NODE_FOLDER_SCAN" + options: + node_id: 2 + folder_id: 0 + 15: + action: "NODE_FOLDER_SCAN" # CHECKHASH replaced by SCAN - but the behaviour is the same in this context. + options: + node_id: 2 + folder_id: 0 + 16: + action: "NODE_FOLDER_REPAIR" + options: + node_id: 2 + folder_id: 0 + 17: + action: "NODE_FOLDER_RESTORE" + options: + node_id: 2 + folder_id: 0 + 18: + action: "NODE_OS_SCAN" + options: + node_id: 0 + 19: + action: "NODE_SHUTDOWN" + options: + node_id: 0 + 20: + action: NODE_STARTUP + options: + node_id: 0 + 21: + action: NODE_RESET + options: + node_id: 0 + 22: + action: "NODE_OS_SCAN" + options: + node_id: 1 + 23: + action: "NODE_SHUTDOWN" + options: + node_id: 1 + 24: + action: NODE_STARTUP + options: + node_id: 1 + 25: + action: NODE_RESET + options: + node_id: 1 + 26: # old action num: 18 + action: "NODE_OS_SCAN" + options: + node_id: 2 + 27: + action: "NODE_SHUTDOWN" + options: + node_id: 2 + 28: + action: NODE_STARTUP + options: + node_id: 2 + 29: + action: NODE_RESET + options: + node_id: 2 + 30: + action: "NODE_OS_SCAN" + options: + node_id: 3 + 31: + action: "NODE_SHUTDOWN" + options: + node_id: 3 + 32: + action: NODE_STARTUP + options: + node_id: 3 + 33: + action: NODE_RESET + options: + node_id: 3 + 34: + action: "NODE_OS_SCAN" + options: + node_id: 4 + 35: + action: "NODE_SHUTDOWN" + options: + node_id: 4 + 36: + action: NODE_STARTUP + options: + node_id: 4 + 37: + action: NODE_RESET + options: + node_id: 4 + 38: + action: "NODE_OS_SCAN" + options: + node_id: 5 + 39: # old action num: 19 # shutdown client 1 + action: "NODE_SHUTDOWN" + options: + node_id: 5 + 40: # old action num: 20 + action: NODE_STARTUP + options: + node_id: 5 + 41: # old action num: 21 + action: NODE_RESET + options: + node_id: 5 + 42: + action: "NODE_OS_SCAN" + options: + node_id: 6 + 43: + action: "NODE_SHUTDOWN" + options: + node_id: 6 + 44: + action: NODE_STARTUP + options: + node_id: 6 + 45: + action: NODE_RESET + options: + node_id: 6 + + 46: # old action num: 22 # "ACL: ADDRULE - Block outgoing traffic from client 1" + action: "NETWORK_ACL_ADDRULE" + options: + position: 1 + permission: 2 + source_ip_id: 7 # client 1 + dest_ip_id: 1 # ALL + source_port_id: 1 + dest_port_id: 1 + protocol_id: 1 + 47: # old action num: 23 # "ACL: ADDRULE - Block outgoing traffic from client 2" + action: "NETWORK_ACL_ADDRULE" + options: + position: 2 + permission: 2 + source_ip_id: 8 # client 2 + dest_ip_id: 1 # ALL + source_port_id: 1 + dest_port_id: 1 + protocol_id: 1 + 48: # old action num: 24 # block tcp traffic from client 1 to web app + action: "NETWORK_ACL_ADDRULE" + options: + position: 3 + permission: 2 + source_ip_id: 7 # client 1 + dest_ip_id: 3 # web server + source_port_id: 1 + dest_port_id: 1 + protocol_id: 3 + 49: # old action num: 25 # block tcp traffic from client 2 to web app + action: "NETWORK_ACL_ADDRULE" + options: + position: 4 + permission: 2 + source_ip_id: 8 # client 2 + dest_ip_id: 3 # web server + source_port_id: 1 + dest_port_id: 1 + protocol_id: 3 + 50: # old action num: 26 + action: "NETWORK_ACL_ADDRULE" + options: + position: 5 + permission: 2 + source_ip_id: 7 # client 1 + dest_ip_id: 4 # database + source_port_id: 1 + dest_port_id: 1 + protocol_id: 3 + 51: # old action num: 27 + action: "NETWORK_ACL_ADDRULE" + options: + position: 6 + permission: 2 + source_ip_id: 8 # client 2 + dest_ip_id: 4 # database + source_port_id: 1 + dest_port_id: 1 + protocol_id: 3 + 52: # old action num: 28 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 0 + 53: # old action num: 29 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 1 + 54: # old action num: 30 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 2 + 55: # old action num: 31 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 3 + 56: # old action num: 32 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 4 + 57: # old action num: 33 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 5 + 58: # old action num: 34 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 6 + 59: # old action num: 35 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 7 + 60: # old action num: 36 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 8 + 61: # old action num: 37 + action: "NETWORK_ACL_REMOVERULE" + options: + position: 9 + 62: # old action num: 38 + action: "NETWORK_NIC_DISABLE" + options: + node_id: 0 + nic_id: 0 + 63: # old action num: 39 + action: "NETWORK_NIC_ENABLE" + options: + node_id: 0 + nic_id: 0 + 64: # old action num: 40 + action: "NETWORK_NIC_DISABLE" + options: + node_id: 1 + nic_id: 0 + 65: # old action num: 41 + action: "NETWORK_NIC_ENABLE" + options: + node_id: 1 + nic_id: 0 + 66: # old action num: 42 + action: "NETWORK_NIC_DISABLE" + options: + node_id: 2 + nic_id: 0 + 67: # old action num: 43 + action: "NETWORK_NIC_ENABLE" + options: + node_id: 2 + nic_id: 0 + 68: # old action num: 44 + action: "NETWORK_NIC_DISABLE" + options: + node_id: 3 + nic_id: 0 + 69: # old action num: 45 + action: "NETWORK_NIC_ENABLE" + options: + node_id: 3 + nic_id: 0 + 70: # old action num: 46 + action: "NETWORK_NIC_DISABLE" + options: + node_id: 4 + nic_id: 0 + 71: # old action num: 47 + action: "NETWORK_NIC_ENABLE" + options: + node_id: 4 + nic_id: 0 + 72: # old action num: 48 + action: "NETWORK_NIC_DISABLE" + options: + node_id: 4 + nic_id: 1 + 73: # old action num: 49 + action: "NETWORK_NIC_ENABLE" + options: + node_id: 4 + nic_id: 1 + 74: # old action num: 50 + action: "NETWORK_NIC_DISABLE" + options: + node_id: 5 + nic_id: 0 + 75: # old action num: 51 + action: "NETWORK_NIC_ENABLE" + options: + node_id: 5 + nic_id: 0 + 76: # old action num: 52 + action: "NETWORK_NIC_DISABLE" + options: + node_id: 6 + nic_id: 0 + 77: # old action num: 53 + action: "NETWORK_NIC_ENABLE" + options: + node_id: 6 + nic_id: 0 + 78: + action: NODE_APPLICATION_INSTALL + options: + node_id: 0 + application_name: DoSBot + ip_address: 192.168.1.14 + 79: + action: NODE_APPLICATION_REMOVE + options: + node_id: 0 + application_name: DoSBot + 80: + action: NODE_APPLICATION_REMOVE + options: + node_id: 0 + application_name: WebBrowser + 81: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 + + + + options: + nodes: + - node_name: domain_controller + applications: + - application_name: DoSBot + - node_name: web_server + applications: + - application_name: DatabaseClient + services: + - service_name: WebServer + - node_name: database_server + folders: + - folder_name: database + files: + - file_name: database.db + services: + - service_name: DatabaseService + - node_name: backup_server + - node_name: security_suite + - node_name: client_1 + - node_name: client_2 + + max_folders_per_node: 2 + max_files_per_folder: 2 + max_services_per_node: 2 + max_nics_per_node: 8 + max_acl_rules: 10 + ip_address_order: + - node_name: domain_controller + nic_num: 1 + - node_name: web_server + nic_num: 1 + - node_name: database_server + nic_num: 1 + - node_name: backup_server + nic_num: 1 + - node_name: security_suite + nic_num: 1 + - node_name: client_1 + nic_num: 1 + - node_name: client_2 + nic_num: 1 + - node_name: security_suite + nic_num: 2 + + + reward_function: + reward_components: + - type: DATABASE_FILE_INTEGRITY + weight: 0.40 + options: + node_hostname: database_server + folder_name: database + file_name: database.db + - type: SHARED_REWARD + weight: 1.0 + options: + agent_name: client_1_green_user + - type: SHARED_REWARD + weight: 1.0 + options: + agent_name: client_2_green_user + + + + agent_settings: + flatten_obs: true + + + + + +simulation: + network: + nmne_config: + capture_nmne: true + nmne_capture_keywords: + - DELETE + nodes: + + - ref: router_1 + hostname: router_1 + type: router + num_ports: 5 + ports: + 1: + ip_address: 192.168.1.1 + subnet_mask: 255.255.255.0 + 2: + ip_address: 192.168.10.1 + subnet_mask: 255.255.255.0 + acl: + 18: + action: PERMIT + src_port: POSTGRES_SERVER + dst_port: POSTGRES_SERVER + 19: + action: PERMIT + src_port: DNS + dst_port: DNS + 20: + action: PERMIT + src_port: FTP + dst_port: FTP + 21: + action: PERMIT + src_port: HTTP + dst_port: HTTP + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + + - ref: switch_1 + hostname: switch_1 + type: switch + num_ports: 8 + + - ref: switch_2 + hostname: switch_2 + type: switch + num_ports: 8 + + - ref: domain_controller + hostname: domain_controller + type: server + ip_address: 192.168.1.10 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.1.1 + services: + - ref: domain_controller_dns_server + type: DNSServer + options: + domain_mapping: + arcd.com: 192.168.1.12 # web server + + - ref: web_server + hostname: web_server + type: server + ip_address: 192.168.1.12 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.1.1 + dns_server: 192.168.1.10 + services: + - ref: web_server_web_service + type: WebServer + applications: + - ref: web_server_database_client + type: DatabaseClient + options: + db_server_ip: 192.168.1.14 + + + - ref: database_server + hostname: database_server + type: server + ip_address: 192.168.1.14 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.1.1 + dns_server: 192.168.1.10 + services: + - ref: database_service + type: DatabaseService + options: + backup_server_ip: 192.168.1.16 + - ref: database_ftp_client + type: FTPClient + + - ref: backup_server + hostname: backup_server + type: server + ip_address: 192.168.1.16 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.1.1 + dns_server: 192.168.1.10 + services: + - ref: backup_service + type: FTPServer + + - ref: security_suite + hostname: security_suite + type: server + ip_address: 192.168.1.110 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.1.1 + dns_server: 192.168.1.10 + network_interfaces: + 2: # unfortunately this number is currently meaningless, they're just added in order and take up the next available slot + ip_address: 192.168.10.110 + subnet_mask: 255.255.255.0 + + - ref: client_1 + hostname: client_1 + type: computer + ip_address: 192.168.10.21 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.10.1 + dns_server: 192.168.1.10 + applications: + - ref: data_manipulation_bot + type: DataManipulationBot + options: + port_scan_p_of_success: 0.8 + data_manipulation_p_of_success: 0.8 + payload: "DELETE" + server_ip: 192.168.1.14 + - ref: client_1_web_browser + type: WebBrowser + options: + target_url: http://arcd.com/users/ + - ref: client_1_database_client + type: DatabaseClient + options: + db_server_ip: 192.168.1.14 + services: + - ref: client_1_dns_client + type: DNSClient + + - ref: client_2 + hostname: client_2 + type: computer + ip_address: 192.168.10.22 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.10.1 + dns_server: 192.168.1.10 + applications: + - ref: client_2_web_browser + type: WebBrowser + options: + target_url: http://arcd.com/users/ + - ref: data_manipulation_bot + type: DataManipulationBot + options: + port_scan_p_of_success: 0.8 + data_manipulation_p_of_success: 0.8 + payload: "DELETE" + server_ip: 192.168.1.14 + - ref: client_2_database_client + type: DatabaseClient + options: + db_server_ip: 192.168.1.14 + services: + - ref: client_2_dns_client + type: DNSClient + + + + links: + - ref: router_1___switch_1 + endpoint_a_ref: router_1 + endpoint_a_port: 1 + endpoint_b_ref: switch_1 + endpoint_b_port: 8 + - ref: router_1___switch_2 + endpoint_a_ref: router_1 + endpoint_a_port: 2 + endpoint_b_ref: switch_2 + endpoint_b_port: 8 + - ref: switch_1___domain_controller + endpoint_a_ref: switch_1 + endpoint_a_port: 1 + endpoint_b_ref: domain_controller + endpoint_b_port: 1 + - ref: switch_1___web_server + endpoint_a_ref: switch_1 + endpoint_a_port: 2 + endpoint_b_ref: web_server + endpoint_b_port: 1 + - ref: switch_1___database_server + endpoint_a_ref: switch_1 + endpoint_a_port: 3 + endpoint_b_ref: database_server + endpoint_b_port: 1 + - ref: switch_1___backup_server + endpoint_a_ref: switch_1 + endpoint_a_port: 4 + endpoint_b_ref: backup_server + endpoint_b_port: 1 + - ref: switch_1___security_suite + endpoint_a_ref: switch_1 + endpoint_a_port: 7 + endpoint_b_ref: security_suite + endpoint_b_port: 1 + - ref: switch_2___client_1 + endpoint_a_ref: switch_2 + endpoint_a_port: 1 + endpoint_b_ref: client_1 + endpoint_b_port: 1 + - ref: switch_2___client_2 + endpoint_a_ref: switch_2 + endpoint_a_port: 2 + endpoint_b_ref: client_2 + endpoint_b_port: 1 + - ref: switch_2___security_suite + endpoint_a_ref: switch_2 + endpoint_a_port: 7 + endpoint_b_ref: security_suite + endpoint_b_port: 2 diff --git a/tests/conftest.py b/tests/conftest.py index be76fc92..fd8727cb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -480,8 +480,8 @@ def game_and_agent(): {"type": "NODE_APPLICATION_SCAN"}, {"type": "NODE_APPLICATION_CLOSE"}, {"type": "NODE_APPLICATION_FIX"}, - {"type": "NODE_APPLICATION_INSTALL", "options": {"application_name": "DoSBot", "ip_address": "192.168.1.14"}}, - {"type": "NODE_APPLICATION_REMOVE", "options": {"application_name": "DoSBot"}}, + {"type": "NODE_APPLICATION_INSTALL"}, + {"type": "NODE_APPLICATION_REMOVE"}, {"type": "NODE_FILE_SCAN"}, {"type": "NODE_FILE_CHECKHASH"}, {"type": "NODE_FILE_DELETE"}, diff --git a/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py b/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py index b68a887e..2fee561a 100644 --- a/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py +++ b/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py @@ -1,8 +1,13 @@ +import yaml + +from primaite.game.game import PrimaiteGame +from primaite.session.environment import PrimaiteGymEnv from primaite.simulator.network.hardware.nodes.host.computer import Computer from primaite.simulator.network.hardware.nodes.host.server import Server from primaite.simulator.system.applications.database_client import DatabaseClient from primaite.simulator.system.applications.red_applications.data_manipulation_bot import DataManipulationBot from primaite.simulator.system.services.database.database_service import DatabaseService +from tests import TEST_ASSETS_ROOT def test_data_manipulation(uc2_network): @@ -32,3 +37,27 @@ def test_data_manipulation(uc2_network): # Now check that the DB client on the web_server can successfully query the users table on the database assert db_client.query("SELECT") + + +def test_application_install_uninstall_on_uc2(): + """Test Application install and uninstall via agent actions mid episode.""" + with open(TEST_ASSETS_ROOT / "configs/test_application_install.yaml", "r") as f: + cfg = yaml.safe_load(f) + + env = PrimaiteGymEnv(game_config=cfg) + env.agent.flatten_obs = False + env.reset() + + _, _, _, _, _ = env.step(0) + domcon = env.game.simulation.network.get_node_by_hostname("domain_controller") + + _, _, _, _, _ = env.step(78) + assert "DoSBot" in domcon.software_manager.software + + _, _, _, _, _ = env.step(79) + + assert "DoSBot" not in domcon.software_manager.software + assert "WebBrowser" in domcon.software_manager.software + + _, _, _, _, _ = env.step(80) + assert "WebBrowser" not in domcon.software_manager.software diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index 5ba58ee5..96e3f390 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -470,13 +470,13 @@ def test_node_application_install_and_uninstall_integration(game_and_agent: Tupl assert client_1.software_manager.software.get("DoSBot") is None - action = ("NODE_APPLICATION_INSTALL", {"node_id": 0}) + action = ("NODE_APPLICATION_INSTALL", {"node_id": 0, "application_name": "DoSBot", "ip_address": "192.168.1.14"}) agent.store_action(action) game.step() assert client_1.software_manager.software.get("DoSBot") is not None - action = ("NODE_APPLICATION_REMOVE", {"node_id": 0}) + action = ("NODE_APPLICATION_REMOVE", {"node_id": 0, "application_name": "DoSBot"}) agent.store_action(action) game.step() From cfea38c5a727bfcfa42922dcbc5c285636f833e0 Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Thu, 28 Mar 2024 15:34:47 +0000 Subject: [PATCH 20/27] #2405 refactor e2e test, fix uninstalled apps not being removed from the request manager --- .../simulator/network/hardware/base.py | 6 +++-- .../test_uc2_data_manipulation_scenario.py | 22 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 754c7a24..bfa547d2 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -1343,7 +1343,7 @@ class Node(SimComponent): else: pass - if application in self: + if application_instance.name in self.software_manager.software: return True else: return False @@ -1367,7 +1367,7 @@ class Node(SimComponent): application_instance = self.software_manager.software.get( str(application.__name__) ) # This works because we can't have two applications with the same name on the same node - self.uninstall_application(application_instance) + # self.uninstall_application(application_instance) self.software_manager.uninstall(application_instance.name) if application_instance.name not in self.software_manager.software: @@ -1406,4 +1406,6 @@ class Node(SimComponent): def __contains__(self, item: Any) -> bool: if isinstance(item, Service): return item.uuid in self.services + elif isinstance(item, Application): + return item.uuid in self.applications return None diff --git a/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py b/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py index 2fee561a..0b31a353 100644 --- a/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py +++ b/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py @@ -51,13 +51,27 @@ def test_application_install_uninstall_on_uc2(): _, _, _, _, _ = env.step(0) domcon = env.game.simulation.network.get_node_by_hostname("domain_controller") - _, _, _, _, _ = env.step(78) + # Test we cannot execute the DoSBot app as it is not installed yet + _, _, _, _, info = env.step(81) + assert info["agent_actions"]["defender"].response.status == "unreachable" + + # Test we can Install the DoSBot app + _, _, _, _, info = env.step(78) assert "DoSBot" in domcon.software_manager.software - _, _, _, _, _ = env.step(79) + # Test we can now execute the DoSBot app + _, _, _, _, info = env.step(81) + assert info["agent_actions"]["defender"].response.status == "success" + # Test we can Uninstall the DoSBot app + _, _, _, _, info = env.step(79) assert "DoSBot" not in domcon.software_manager.software - assert "WebBrowser" in domcon.software_manager.software - _, _, _, _, _ = env.step(80) + # Test we cannot execute the DoSBot app as it was uninstalled + _, _, _, _, info = env.step(81) + assert info["agent_actions"]["defender"].response.status == "unreachable" + + # Test we can uninstall one of the default apps (WebBrowser) + assert "WebBrowser" in domcon.software_manager.software + _, _, _, _, info = env.step(80) assert "WebBrowser" not in domcon.software_manager.software From 2eb900746b24959eb69d3bedf0a9f83d089e8eeb Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Fri, 29 Mar 2024 11:34:43 +0000 Subject: [PATCH 21/27] #2402 rename network_acl actions to router_acl and refactor how router_name is given --- .../_package_data/data_manipulation.yaml | 56 +++++---- .../_package_data/data_manipulation_marl.yaml | 112 +++++++++++------- src/primaite/game/agent/actions.py | 28 ++--- .../hardware/nodes/network/firewall.py | 61 ++++++++++ .../assets/configs/bad_primaite_session.yaml | 56 +++++---- .../configs/eval_only_primaite_session.yaml | 56 +++++---- tests/assets/configs/multi_agent_session.yaml | 112 +++++++++++------- tests/assets/configs/shared_rewards.yaml | 56 +++++---- .../assets/configs/test_primaite_session.yaml | 56 +++++---- .../configs/train_only_primaite_session.yaml | 56 +++++---- tests/conftest.py | 4 +- .../game_layer/test_actions.py | 17 +-- 12 files changed, 426 insertions(+), 244 deletions(-) diff --git a/src/primaite/config/_package_data/data_manipulation.yaml b/src/primaite/config/_package_data/data_manipulation.yaml index 12f60b63..ad3c02cc 100644 --- a/src/primaite/config/_package_data/data_manipulation.yaml +++ b/src/primaite/config/_package_data/data_manipulation.yaml @@ -258,12 +258,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -477,8 +473,9 @@ agents: node_id: 6 46: # old action num: 22 # "ACL: ADDRULE - Block outgoing traffic from client 1" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -487,8 +484,9 @@ agents: dest_port_id: 1 protocol_id: 1 47: # old action num: 23 # "ACL: ADDRULE - Block outgoing traffic from client 2" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -497,8 +495,9 @@ agents: dest_port_id: 1 protocol_id: 1 48: # old action num: 24 # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -507,8 +506,9 @@ agents: dest_port_id: 1 protocol_id: 3 49: # old action num: 25 # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -517,8 +517,9 @@ agents: dest_port_id: 1 protocol_id: 3 50: # old action num: 26 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -527,8 +528,9 @@ agents: dest_port_id: 1 protocol_id: 3 51: # old action num: 27 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -537,44 +539,54 @@ agents: dest_port_id: 1 protocol_id: 3 52: # old action num: 28 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 53: # old action num: 29 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 54: # old action num: 30 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 55: # old action num: 31 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 56: # old action num: 32 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 57: # old action num: 33 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 58: # old action num: 34 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 59: # old action num: 35 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 60: # old action num: 36 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 61: # old action num: 37 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 62: # old action num: 38 action: "NETWORK_NIC_DISABLE" diff --git a/src/primaite/config/_package_data/data_manipulation_marl.yaml b/src/primaite/config/_package_data/data_manipulation_marl.yaml index b632f626..2a788b73 100644 --- a/src/primaite/config/_package_data/data_manipulation_marl.yaml +++ b/src/primaite/config/_package_data/data_manipulation_marl.yaml @@ -260,12 +260,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -479,8 +475,9 @@ agents: node_id: 6 46: # old action num: 22 # "ACL: ADDRULE - Block outgoing traffic from client 1" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -489,8 +486,9 @@ agents: dest_port_id: 1 protocol_id: 1 47: # old action num: 23 # "ACL: ADDRULE - Block outgoing traffic from client 2" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -499,8 +497,9 @@ agents: dest_port_id: 1 protocol_id: 1 48: # old action num: 24 # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -509,8 +508,9 @@ agents: dest_port_id: 1 protocol_id: 3 49: # old action num: 25 # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -519,8 +519,9 @@ agents: dest_port_id: 1 protocol_id: 3 50: # old action num: 26 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -529,8 +530,9 @@ agents: dest_port_id: 1 protocol_id: 3 51: # old action num: 27 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -539,44 +541,54 @@ agents: dest_port_id: 1 protocol_id: 3 52: # old action num: 28 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 53: # old action num: 29 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 54: # old action num: 30 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 55: # old action num: 31 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 56: # old action num: 32 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 57: # old action num: 33 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 58: # old action num: 34 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 59: # old action num: 35 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 60: # old action num: 36 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 61: # old action num: 37 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 62: # old action num: 38 action: "NETWORK_NIC_DISABLE" @@ -811,12 +823,12 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE + - type: ROUTER_ACL_ADDRULE options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE + target_router_nodename: router_1 + - type: ROUTER_ACL_REMOVERULE options: - target_router_hostname: router_1 + target_router_nodename: router_1 - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -1030,8 +1042,9 @@ agents: node_id: 6 46: # old action num: 22 # "ACL: ADDRULE - Block outgoing traffic from client 1" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -1040,8 +1053,9 @@ agents: dest_port_id: 1 protocol_id: 1 47: # old action num: 23 # "ACL: ADDRULE - Block outgoing traffic from client 2" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -1050,8 +1064,9 @@ agents: dest_port_id: 1 protocol_id: 1 48: # old action num: 24 # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -1060,8 +1075,9 @@ agents: dest_port_id: 1 protocol_id: 3 49: # old action num: 25 # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -1070,8 +1086,9 @@ agents: dest_port_id: 1 protocol_id: 3 50: # old action num: 26 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -1080,8 +1097,9 @@ agents: dest_port_id: 1 protocol_id: 3 51: # old action num: 27 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -1090,44 +1108,54 @@ agents: dest_port_id: 1 protocol_id: 3 52: # old action num: 28 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 53: # old action num: 29 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 54: # old action num: 30 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 55: # old action num: 31 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 56: # old action num: 32 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 57: # old action num: 33 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 58: # old action num: 34 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 59: # old action num: 35 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 60: # old action num: 36 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 61: # old action num: 37 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 62: # old action num: 38 action: "NETWORK_NIC_DISABLE" diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index b79fc985..d585273d 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -405,25 +405,22 @@ class NodeResetAction(NodeAbstractAction): self.verb: str = "reset" -class NetworkACLAddRuleAction(AbstractAction): +class RouterACLAddRuleAction(AbstractAction): """Action which adds a rule to a router's ACL.""" def __init__( self, manager: "ActionManager", - target_router_hostname: str, max_acl_rules: int, num_ips: int, num_ports: int, num_protocols: int, **kwargs, ) -> None: - """Init method for NetworkACLAddRuleAction. + """Init method for RouterACLAddRuleAction. :param manager: Reference to the ActionManager which created this action. :type manager: ActionManager - :param target_router_hostname: hostname of the router to which the ACL rule should be added. - :type target_router_hostname: str :param max_acl_rules: Maximum number of ACL rules that can be added to the router. :type max_acl_rules: int :param num_ips: Number of IP addresses in the simulation. @@ -444,10 +441,10 @@ class NetworkACLAddRuleAction(AbstractAction): "dest_port_id": num_ports, "protocol_id": num_protocols, } - self.target_router_name: str = target_router_hostname def form_request( self, + target_router_nodename: str, position: int, permission: int, source_ip_id: int, @@ -511,7 +508,7 @@ class NetworkACLAddRuleAction(AbstractAction): return [ "network", "node", - self.target_router_name, + target_router_nodename, "acl", "add_rule", permission_str, @@ -524,26 +521,23 @@ class NetworkACLAddRuleAction(AbstractAction): ] -class NetworkACLRemoveRuleAction(AbstractAction): +class RouterACLRemoveRuleAction(AbstractAction): """Action which removes a rule from a router's ACL.""" - def __init__(self, manager: "ActionManager", target_router_hostname: str, max_acl_rules: int, **kwargs) -> None: - """Init method for NetworkACLRemoveRuleAction. + def __init__(self, manager: "ActionManager", max_acl_rules: int, **kwargs) -> None: + """Init method for RouterACLRemoveRuleAction. :param manager: Reference to the ActionManager which created this action. :type manager: ActionManager - :param target_router_hostname: Hostname of the router from which the ACL rule should be removed. - :type target_router_hostname: str :param max_acl_rules: Maximum number of ACL rules that can be added to the router. :type max_acl_rules: int """ super().__init__(manager=manager) self.shape: Dict[str, int] = {"position": max_acl_rules} - self.target_router_name: str = target_router_hostname - def form_request(self, position: int) -> List[str]: + def form_request(self, target_router_nodename: str, position: int) -> List[str]: """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" - return ["network", "node", self.target_router_name, "acl", "remove_rule", position] + return ["network", "node", target_router_nodename, "acl", "remove_rule", position] class NetworkNICAbstractAction(AbstractAction): @@ -672,8 +666,8 @@ class ActionManager: "NODE_SHUTDOWN": NodeShutdownAction, "NODE_STARTUP": NodeStartupAction, "NODE_RESET": NodeResetAction, - "NETWORK_ACL_ADDRULE": NetworkACLAddRuleAction, - "NETWORK_ACL_REMOVERULE": NetworkACLRemoveRuleAction, + "ROUTER_ACL_ADDRULE": RouterACLAddRuleAction, + "ROUTER_ACL_REMOVERULE": RouterACLRemoveRuleAction, "NETWORK_NIC_ENABLE": NetworkNICEnableAction, "NETWORK_NIC_DISABLE": NetworkNICDisableAction, "NETWORK_PORT_ENABLE": NetworkPortEnableAction, diff --git a/src/primaite/simulator/network/hardware/nodes/network/firewall.py b/src/primaite/simulator/network/hardware/nodes/network/firewall.py index d7b1dfd9..ea353b2f 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/firewall.py +++ b/src/primaite/simulator/network/hardware/nodes/network/firewall.py @@ -4,6 +4,7 @@ from typing import Dict, Final, Optional, Union from prettytable import MARKDOWN, PrettyTable from pydantic import validate_call +# from primaite.simulator.core import RequestManager, RequestType from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState from primaite.simulator.network.hardware.nodes.network.router import ( AccessControlList, @@ -123,6 +124,66 @@ class Firewall(Router): sys_log=kwargs["sys_log"], implicit_action=ACLAction.PERMIT, name=f"{hostname} - External Outbound" ) + # def _init_request_manager(self) -> RequestManager: + # """ + # Initialise the request manager. + + # More information in user guide and docstring for SimComponent._init_request_manager. + # """ + # rm = super()._init_request_manager() + # self._internal_acl_request_manager = RequestManager() + # rm.add_request("internal", RequestType(func=self._internal_acl_request_manager)) + + # self._dmz_acl_request_manager = RequestManager() + # rm.add_request("dmz", RequestType(func=self._dmz_acl_request_manager)) + + # self._external_acl_request_manager = RequestManager() + # rm.add_request("external", RequestType(func=self._external_acl_request_manager)) + + # self._internal_inbound_acl_request_manager = RequestManager() + # self._internal_outbound_acl_request_manager = RequestManager() + # self._internal_acl_request_manager.add_request( + # "inbound", RequestType(func=self._internal_inbound_acl_request_manager) + # ) + # self._internal_acl_request_manager.add_request( + # "outbound", RequestType(func=self._internal_outbound_acl_request_manager) + # ) + + # self.dmz_inbound_acl_request_manager = RequestManager() + # self.dmz_outbound_acl_request_manager = RequestManager() + # self._dmz_acl_request_manager.add_request("inbound", RequestType(func=self.dmz_inbound_acl_request_manager)) + # self._dmz_acl_request_manager.add_request("outbound", RequestType(func=self.dmz_outbound_acl_request_manager)) + + # self.external_inbound_acl_request_manager = RequestManager() + # self.external_outbound_acl_request_manager = RequestManager() + # self._external_acl_request_manager.add_request( + # "inbound", RequestType(func=self.external_inbound_acl_request_manager) + # ) + # self._external_acl_request_manager.add_request( + # "outbound", RequestType(func=self.external_outbound_acl_request_manager) + # ) + + # self._internal_inbound_acl_request_manager.add_request( + # "acl", RequestType(func=self.internal_inbound_acl._request_manager) + # ) + # self._internal_outbound_acl_request_manager.add_request( + # "acl", RequestType(func=self.internal_outbound_acl._request_manager) + # ) + + # self.dmz_inbound_acl_request_manager.add_request("acl", RequestType(func=self.dmz_inbound_acl._request_manager)) + # self.dmz_outbound_acl_request_manager.add_request( + # "acl", RequestType(func=self.dmz_outbound_acl._request_manager) + # ) + + # self.external_inbound_acl_request_manager.add_request( + # "acl", RequestType(func=self.external_inbound_acl._request_manager) + # ) + # self.external_outbound_acl_request_manager.add_request( + # "acl", RequestType(func=self.external_outbound_acl._request_manager) + # ) + + # return rm + def describe_state(self) -> Dict: """ Describes the current state of the Firewall. diff --git a/tests/assets/configs/bad_primaite_session.yaml b/tests/assets/configs/bad_primaite_session.yaml index e599ee7e..743d2bba 100644 --- a/tests/assets/configs/bad_primaite_session.yaml +++ b/tests/assets/configs/bad_primaite_session.yaml @@ -169,12 +169,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -291,8 +287,9 @@ agents: options: node_id: 5 22: # "ACL: ADDRULE - Block outgoing traffic from client 1" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -301,8 +298,9 @@ agents: dest_port_id: 1 protocol_id: 1 23: # "ACL: ADDRULE - Block outgoing traffic from client 2" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -311,8 +309,9 @@ agents: dest_port_id: 1 protocol_id: 1 24: # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -321,8 +320,9 @@ agents: dest_port_id: 1 protocol_id: 3 25: # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -331,8 +331,9 @@ agents: dest_port_id: 1 protocol_id: 3 26: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -341,8 +342,9 @@ agents: dest_port_id: 1 protocol_id: 3 27: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -351,44 +353,54 @@ agents: dest_port_id: 1 protocol_id: 3 28: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 29: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 30: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 31: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 32: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 33: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 34: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 35: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 36: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 37: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 38: action: "NETWORK_NIC_DISABLE" diff --git a/tests/assets/configs/eval_only_primaite_session.yaml b/tests/assets/configs/eval_only_primaite_session.yaml index 9d1404d8..525f7bb0 100644 --- a/tests/assets/configs/eval_only_primaite_session.yaml +++ b/tests/assets/configs/eval_only_primaite_session.yaml @@ -173,12 +173,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -295,8 +291,9 @@ agents: options: node_id: 5 22: # "ACL: ADDRULE - Block outgoing traffic from client 1" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -305,8 +302,9 @@ agents: dest_port_id: 1 protocol_id: 1 23: # "ACL: ADDRULE - Block outgoing traffic from client 2" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -315,8 +313,9 @@ agents: dest_port_id: 1 protocol_id: 1 24: # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -325,8 +324,9 @@ agents: dest_port_id: 1 protocol_id: 3 25: # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -335,8 +335,9 @@ agents: dest_port_id: 1 protocol_id: 3 26: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -345,8 +346,9 @@ agents: dest_port_id: 1 protocol_id: 3 27: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -355,44 +357,54 @@ agents: dest_port_id: 1 protocol_id: 3 28: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 29: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 30: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 31: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 32: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 33: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 34: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 35: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 36: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 37: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 38: action: "NETWORK_NIC_DISABLE" diff --git a/tests/assets/configs/multi_agent_session.yaml b/tests/assets/configs/multi_agent_session.yaml index acb62c96..77a17459 100644 --- a/tests/assets/configs/multi_agent_session.yaml +++ b/tests/assets/configs/multi_agent_session.yaml @@ -180,12 +180,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -302,8 +298,9 @@ agents: options: node_id: 5 22: # "ACL: ADDRULE - Block outgoing traffic from client 1" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -312,8 +309,9 @@ agents: dest_port_id: 1 protocol_id: 1 23: # "ACL: ADDRULE - Block outgoing traffic from client 2" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -322,8 +320,9 @@ agents: dest_port_id: 1 protocol_id: 1 24: # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -332,8 +331,9 @@ agents: dest_port_id: 1 protocol_id: 3 25: # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -342,8 +342,9 @@ agents: dest_port_id: 1 protocol_id: 3 26: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -352,8 +353,9 @@ agents: dest_port_id: 1 protocol_id: 3 27: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -362,44 +364,54 @@ agents: dest_port_id: 1 protocol_id: 3 28: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 29: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 30: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 31: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 32: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 33: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 34: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 35: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 36: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 37: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 38: action: "NETWORK_NIC_DISABLE" @@ -624,12 +636,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -746,8 +754,9 @@ agents: options: node_id: 5 22: # "ACL: ADDRULE - Block outgoing traffic from client 1" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -756,8 +765,9 @@ agents: dest_port_id: 1 protocol_id: 1 23: # "ACL: ADDRULE - Block outgoing traffic from client 2" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -766,8 +776,9 @@ agents: dest_port_id: 1 protocol_id: 1 24: # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -776,8 +787,9 @@ agents: dest_port_id: 1 protocol_id: 3 25: # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -786,8 +798,9 @@ agents: dest_port_id: 1 protocol_id: 3 26: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -796,8 +809,9 @@ agents: dest_port_id: 1 protocol_id: 3 27: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -806,44 +820,54 @@ agents: dest_port_id: 1 protocol_id: 3 28: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 29: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 30: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 31: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 32: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 33: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 34: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 35: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 36: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 37: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 38: action: "NETWORK_NIC_DISABLE" diff --git a/tests/assets/configs/shared_rewards.yaml b/tests/assets/configs/shared_rewards.yaml index 10feba9d..e7226b5f 100644 --- a/tests/assets/configs/shared_rewards.yaml +++ b/tests/assets/configs/shared_rewards.yaml @@ -258,12 +258,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -477,8 +473,9 @@ agents: node_id: 6 46: # old action num: 22 # "ACL: ADDRULE - Block outgoing traffic from client 1" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -487,8 +484,9 @@ agents: dest_port_id: 1 protocol_id: 1 47: # old action num: 23 # "ACL: ADDRULE - Block outgoing traffic from client 2" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -497,8 +495,9 @@ agents: dest_port_id: 1 protocol_id: 1 48: # old action num: 24 # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -507,8 +506,9 @@ agents: dest_port_id: 1 protocol_id: 3 49: # old action num: 25 # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -517,8 +517,9 @@ agents: dest_port_id: 1 protocol_id: 3 50: # old action num: 26 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -527,8 +528,9 @@ agents: dest_port_id: 1 protocol_id: 3 51: # old action num: 27 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -537,44 +539,54 @@ agents: dest_port_id: 1 protocol_id: 3 52: # old action num: 28 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 53: # old action num: 29 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 54: # old action num: 30 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 55: # old action num: 31 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 56: # old action num: 32 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 57: # old action num: 33 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 58: # old action num: 34 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 59: # old action num: 35 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 60: # old action num: 36 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 61: # old action num: 37 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 62: # old action num: 38 action: "NETWORK_NIC_DISABLE" diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index b131c1b7..0cb371d5 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -183,12 +183,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -305,8 +301,9 @@ agents: options: node_id: 5 22: # "ACL: ADDRULE - Block outgoing traffic from client 1" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -315,8 +312,9 @@ agents: dest_port_id: 1 protocol_id: 1 23: # "ACL: ADDRULE - Block outgoing traffic from client 2" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -325,8 +323,9 @@ agents: dest_port_id: 1 protocol_id: 1 24: # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -335,8 +334,9 @@ agents: dest_port_id: 1 protocol_id: 3 25: # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -345,8 +345,9 @@ agents: dest_port_id: 1 protocol_id: 3 26: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -355,8 +356,9 @@ agents: dest_port_id: 1 protocol_id: 3 27: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -365,44 +367,54 @@ agents: dest_port_id: 1 protocol_id: 3 28: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 29: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 30: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 31: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 32: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 33: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 34: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 35: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 36: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 37: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 38: action: "NETWORK_NIC_DISABLE" diff --git a/tests/assets/configs/train_only_primaite_session.yaml b/tests/assets/configs/train_only_primaite_session.yaml index d0cbaab3..619b7a23 100644 --- a/tests/assets/configs/train_only_primaite_session.yaml +++ b/tests/assets/configs/train_only_primaite_session.yaml @@ -181,12 +181,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE @@ -303,8 +299,9 @@ agents: options: node_id: 5 22: # "ACL: ADDRULE - Block outgoing traffic from client 1" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -313,8 +310,9 @@ agents: dest_port_id: 1 protocol_id: 1 23: # "ACL: ADDRULE - Block outgoing traffic from client 2" (not supported in Primaite) - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -323,8 +321,9 @@ agents: dest_port_id: 1 protocol_id: 1 24: # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -333,8 +332,9 @@ agents: dest_port_id: 1 protocol_id: 3 25: # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -343,8 +343,9 @@ agents: dest_port_id: 1 protocol_id: 3 26: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -353,8 +354,9 @@ agents: dest_port_id: 1 protocol_id: 3 27: - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_nodename: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -363,44 +365,54 @@ agents: dest_port_id: 1 protocol_id: 3 28: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 0 29: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 1 30: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 2 31: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 3 32: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 4 33: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 5 34: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 6 35: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 7 36: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 8 37: - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_nodename: router_1 position: 9 38: action: "NETWORK_NIC_DISABLE" diff --git a/tests/conftest.py b/tests/conftest.py index 078a78bd..05b8e925 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -494,8 +494,8 @@ def game_and_agent(): {"type": "NODE_SHUTDOWN"}, {"type": "NODE_STARTUP"}, {"type": "NODE_RESET"}, - {"type": "NETWORK_ACL_ADDRULE", "options": {"target_router_hostname": "router"}}, - {"type": "NETWORK_ACL_REMOVERULE", "options": {"target_router_hostname": "router"}}, + {"type": "ROUTER_ACL_ADDRULE"}, + {"type": "ROUTER_ACL_REMOVERULE"}, {"type": "NETWORK_NIC_ENABLE"}, {"type": "NETWORK_NIC_DISABLE"}, {"type": "NETWORK_PORT_ENABLE"}, diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index b3a52cd8..7bb8930c 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -93,9 +93,9 @@ def test_node_service_fix_integration(game_and_agent: Tuple[PrimaiteGame, ProxyA assert svc.health_state_actual == SoftwareHealthState.GOOD -def test_network_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): +def test_router_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): """ - Test that the NetworkACLAddRuleAction can form a request and that it is accepted by the simulation. + Test that the RouterACLAddRuleAction can form a request and that it is accepted by the simulation. The ACL starts off with 4 rules, and we add a rule, and check that the ACL now has 5 rules. """ @@ -112,8 +112,9 @@ def test_network_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, Pro # 2: Add a rule to block client 1 from reaching server 2 on router action = ( - "NETWORK_ACL_ADDRULE", + "ROUTER_ACL_ADDRULE", { + "target_router_nodename": "router", "position": 4, # 4th rule "permission": 2, # DENY "source_ip_id": 3, # 10.0.1.2 (client_1) @@ -136,8 +137,9 @@ def test_network_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, Pro # 4: Add a rule to block server_1 from reaching server_2 on router (this should not affect comms as they are on same subnet) action = ( - "NETWORK_ACL_ADDRULE", + "ROUTER_ACL_ADDRULE", { + "target_router_nodename": "router", "position": 5, # 5th rule "permission": 2, # DENY "source_ip_id": 5, # 10.0.2.2 (server_1) @@ -155,8 +157,8 @@ def test_network_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, Pro assert server_1.ping("10.0.2.3") # Can ping server_2 -def test_network_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): - """Test that the NetworkACLRemoveRuleAction can form a request and that it is accepted by the simulation.""" +def test_router_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): + """Test that the RouterACLRemoveRuleAction can form a request and that it is accepted by the simulation.""" game, agent = game_and_agent # 1: Check that http traffic is going across the network nicely. @@ -171,8 +173,9 @@ def test_network_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame, # 2: Remove rule that allows HTTP traffic across the network action = ( - "NETWORK_ACL_REMOVERULE", + "ROUTER_ACL_REMOVERULE", { + "target_router_nodename": "router", "position": 3, # 4th rule }, ) From 7299a12c64369340373f2fcd5597cec98c046730 Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Fri, 29 Mar 2024 16:30:39 +0000 Subject: [PATCH 22/27] #2402 add firewall acl actions --- src/primaite/game/agent/actions.py | 154 +++++- .../hardware/nodes/network/firewall.py | 170 +++---- .../network/hardware/nodes/network/router.py | 2 +- .../configs/firewall_actions_network.yaml | 448 ++++++++++++++++++ .../game_layer/test_actions.py | 102 ++++ 5 files changed, 793 insertions(+), 83 deletions(-) create mode 100644 tests/assets/configs/firewall_actions_network.yaml diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index d585273d..ff59e77f 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -458,7 +458,7 @@ class RouterACLAddRuleAction(AbstractAction): permission_str = "UNUSED" return ["do_nothing"] # NOT SUPPORTED, JUST DO NOTHING IF WE COME ACROSS THIS elif permission == 1: - permission_str = "ALLOW" + permission_str = "PERMIT" elif permission == 2: permission_str = "DENY" else: @@ -540,6 +540,156 @@ class RouterACLRemoveRuleAction(AbstractAction): return ["network", "node", target_router_nodename, "acl", "remove_rule", position] +class FirewallACLAddRuleAction(AbstractAction): + """Action which adds a rule to a firewall port's ACL.""" + + def __init__( + self, + manager: "ActionManager", + max_acl_rules: int, + num_ips: int, + num_ports: int, + num_protocols: int, + **kwargs, + ) -> None: + """Init method for FirewallACLAddRuleAction. + + :param manager: Reference to the ActionManager which created this action. + :type manager: ActionManager + :param max_acl_rules: Maximum number of ACL rules that can be added to the router. + :type max_acl_rules: int + :param num_ips: Number of IP addresses in the simulation. + :type num_ips: int + :param num_ports: Number of ports in the simulation. + :type num_ports: int + :param num_protocols: Number of protocols in the simulation. + :type num_protocols: int + """ + super().__init__(manager=manager) + num_permissions = 3 + self.shape: Dict[str, int] = { + "position": max_acl_rules, + "permission": num_permissions, + "source_ip_id": num_ips, + "dest_ip_id": num_ips, + "source_port_id": num_ports, + "dest_port_id": num_ports, + "protocol_id": num_protocols, + } + + def form_request( + self, + target_firewall_nodename: str, + firewall_port_name: str, + firewall_port_direction: str, + position: int, + permission: int, + source_ip_id: int, + dest_ip_id: int, + source_port_id: int, + dest_port_id: int, + protocol_id: int, + ) -> List[str]: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + if permission == 0: + permission_str = "UNUSED" + return ["do_nothing"] # NOT SUPPORTED, JUST DO NOTHING IF WE COME ACROSS THIS + elif permission == 1: + permission_str = "PERMIT" + elif permission == 2: + permission_str = "DENY" + else: + _LOGGER.warning(f"{self.__class__} received permission {permission}, expected 0 or 1.") + + if protocol_id == 0: + return ["do_nothing"] # NOT SUPPORTED, JUST DO NOTHING IF WE COME ACROSS THIS + + if protocol_id == 1: + protocol = "ALL" + else: + protocol = self.manager.get_internet_protocol_by_idx(protocol_id - 2) + # subtract 2 to account for UNUSED=0 and ALL=1. + + if source_ip_id == 0: + return ["do_nothing"] # invalid formulation + elif source_ip_id == 1: + src_ip = "ALL" + else: + src_ip = self.manager.get_ip_address_by_idx(source_ip_id - 2) + # subtract 2 to account for UNUSED=0, and ALL=1 + + if source_port_id == 0: + return ["do_nothing"] # invalid formulation + elif source_port_id == 1: + src_port = "ALL" + else: + src_port = self.manager.get_port_by_idx(source_port_id - 2) + # subtract 2 to account for UNUSED=0, and ALL=1 + + if source_ip_id == 0: + return ["do_nothing"] # invalid formulation + elif dest_ip_id == 1: + dst_ip = "ALL" + else: + dst_ip = self.manager.get_ip_address_by_idx(dest_ip_id - 2) + # subtract 2 to account for UNUSED=0, and ALL=1 + + if dest_port_id == 0: + return ["do_nothing"] # invalid formulation + elif dest_port_id == 1: + dst_port = "ALL" + else: + dst_port = self.manager.get_port_by_idx(dest_port_id - 2) + # subtract 2 to account for UNUSED=0, and ALL=1 + + return [ + "network", + "node", + target_firewall_nodename, + firewall_port_name, + firewall_port_direction, + "acl", + "add_rule", + permission_str, + protocol, + str(src_ip), + src_port, + str(dst_ip), + dst_port, + position, + ] + + +class FirewallACLRemoveRuleAction(AbstractAction): + """Action which removes a rule from a firewall port's ACL.""" + + def __init__(self, manager: "ActionManager", max_acl_rules: int, **kwargs) -> None: + """Init method for RouterACLRemoveRuleAction. + + :param manager: Reference to the ActionManager which created this action. + :type manager: ActionManager + :param max_acl_rules: Maximum number of ACL rules that can be added to the router. + :type max_acl_rules: int + """ + super().__init__(manager=manager) + self.shape: Dict[str, int] = {"position": max_acl_rules} + + def form_request( + self, target_firewall_nodename: str, firewall_port_name: str, firewall_port_direction: str, position: int + ) -> List[str]: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + return [ + "network", + "node", + target_firewall_nodename, + firewall_port_name, + firewall_port_direction, + "acl", + "remove_rule", + position, + ] + + class NetworkNICAbstractAction(AbstractAction): """ Abstract base class for NIC actions. @@ -668,6 +818,8 @@ class ActionManager: "NODE_RESET": NodeResetAction, "ROUTER_ACL_ADDRULE": RouterACLAddRuleAction, "ROUTER_ACL_REMOVERULE": RouterACLRemoveRuleAction, + "FIREWALL_ACL_ADDRULE": FirewallACLAddRuleAction, + "FIREWALL_ACL_REMOVERULE": FirewallACLRemoveRuleAction, "NETWORK_NIC_ENABLE": NetworkNICEnableAction, "NETWORK_NIC_DISABLE": NetworkNICDisableAction, "NETWORK_PORT_ENABLE": NetworkPortEnableAction, diff --git a/src/primaite/simulator/network/hardware/nodes/network/firewall.py b/src/primaite/simulator/network/hardware/nodes/network/firewall.py index ea353b2f..a27b5cee 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/firewall.py +++ b/src/primaite/simulator/network/hardware/nodes/network/firewall.py @@ -1,10 +1,10 @@ from ipaddress import IPv4Address -from typing import Dict, Final, Optional, Union +from typing import Dict, Final, Union from prettytable import MARKDOWN, PrettyTable -from pydantic import validate_call +from pydantic import Field, validate_call -# from primaite.simulator.core import RequestManager, RequestType +from primaite.simulator.core import RequestManager, RequestType from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState from primaite.simulator.network.hardware.nodes.network.router import ( AccessControlList, @@ -68,22 +68,34 @@ class Firewall(Router): :ivar str hostname: The Firewall hostname. """ - internal_inbound_acl: Optional[AccessControlList] = None + internal_inbound_acl: AccessControlList = Field( + default_factory=lambda: AccessControlList(name="Internal Inbound", implicit_action=ACLAction.DENY) + ) """Access Control List for managing entering the internal network.""" - internal_outbound_acl: Optional[AccessControlList] = None + internal_outbound_acl: AccessControlList = Field( + default_factory=lambda: AccessControlList(name="Internal Outbound", implicit_action=ACLAction.DENY) + ) """Access Control List for managing traffic leaving the internal network.""" - dmz_inbound_acl: Optional[AccessControlList] = None + dmz_inbound_acl: AccessControlList = Field( + default_factory=lambda: AccessControlList(name="DMZ Inbound", implicit_action=ACLAction.DENY) + ) """Access Control List for managing traffic entering the DMZ.""" - dmz_outbound_acl: Optional[AccessControlList] = None + dmz_outbound_acl: AccessControlList = Field( + default_factory=lambda: AccessControlList(name="DMZ Outbound", implicit_action=ACLAction.DENY) + ) """Access Control List for managing traffic leaving the DMZ.""" - external_inbound_acl: Optional[AccessControlList] = None + external_inbound_acl: AccessControlList = Field( + default_factory=lambda: AccessControlList(name="External Inbound", implicit_action=ACLAction.PERMIT) + ) """Access Control List for managing traffic entering from an external network.""" - external_outbound_acl: Optional[AccessControlList] = None + external_outbound_acl: AccessControlList = Field( + default_factory=lambda: AccessControlList(name="External Outbound", implicit_action=ACLAction.PERMIT) + ) """Access Control List for managing traffic leaving towards an external network.""" def __init__(self, hostname: str, **kwargs): @@ -101,88 +113,84 @@ class Firewall(Router): self.connect_nic( RouterInterface(ip_address="127.0.0.1", subnet_mask="255.0.0.0", gateway="0.0.0.0", port_name="dmz") ) - # Initialise ACLs for internal and dmz interfaces with a default DENY policy - self.internal_inbound_acl = AccessControlList( - sys_log=kwargs["sys_log"], implicit_action=ACLAction.DENY, name=f"{hostname} - Internal Inbound" + self.internal_inbound_acl.sys_log = kwargs["sys_log"] + self.internal_inbound_acl.name = f"{hostname} - Internal Inbound" + + self.internal_outbound_acl.sys_log = kwargs["sys_log"] + self.internal_outbound_acl.name = f"{hostname} - Internal Outbound" + + self.dmz_inbound_acl.sys_log = kwargs["sys_log"] + self.dmz_inbound_acl.name = f"{hostname} - DMZ Inbound" + + self.dmz_outbound_acl.sys_log = kwargs["sys_log"] + self.dmz_outbound_acl.name = f"{hostname} - DMZ Outbound" + + self.external_inbound_acl.sys_log = kwargs["sys_log"] + self.external_inbound_acl.name = f"{hostname} - External Inbound" + + self.external_outbound_acl.sys_log = kwargs["sys_log"] + self.external_outbound_acl.name = f"{hostname} - External Outbound" + + def _init_request_manager(self) -> RequestManager: + """ + Initialise the request manager. + + More information in user guide and docstring for SimComponent._init_request_manager. + """ + rm = super()._init_request_manager() + self._internal_acl_request_manager = RequestManager() + rm.add_request("internal", RequestType(func=self._internal_acl_request_manager)) + + self._dmz_acl_request_manager = RequestManager() + rm.add_request("dmz", RequestType(func=self._dmz_acl_request_manager)) + + self._external_acl_request_manager = RequestManager() + rm.add_request("external", RequestType(func=self._external_acl_request_manager)) + + self._internal_inbound_acl_request_manager = RequestManager() + self._internal_outbound_acl_request_manager = RequestManager() + self._internal_acl_request_manager.add_request( + "inbound", RequestType(func=self._internal_inbound_acl_request_manager) ) - self.internal_outbound_acl = AccessControlList( - sys_log=kwargs["sys_log"], implicit_action=ACLAction.DENY, name=f"{hostname} - Internal Outbound" - ) - self.dmz_inbound_acl = AccessControlList( - sys_log=kwargs["sys_log"], implicit_action=ACLAction.DENY, name=f"{hostname} - DMZ Inbound" - ) - self.dmz_outbound_acl = AccessControlList( - sys_log=kwargs["sys_log"], implicit_action=ACLAction.DENY, name=f"{hostname} - DMZ Outbound" + self._internal_acl_request_manager.add_request( + "outbound", RequestType(func=self._internal_outbound_acl_request_manager) ) - # external ACLs should have a default PERMIT policy - self.external_inbound_acl = AccessControlList( - sys_log=kwargs["sys_log"], implicit_action=ACLAction.PERMIT, name=f"{hostname} - External Inbound" + self.dmz_inbound_acl_request_manager = RequestManager() + self.dmz_outbound_acl_request_manager = RequestManager() + self._dmz_acl_request_manager.add_request("inbound", RequestType(func=self.dmz_inbound_acl_request_manager)) + self._dmz_acl_request_manager.add_request("outbound", RequestType(func=self.dmz_outbound_acl_request_manager)) + + self.external_inbound_acl_request_manager = RequestManager() + self.external_outbound_acl_request_manager = RequestManager() + self._external_acl_request_manager.add_request( + "inbound", RequestType(func=self.external_inbound_acl_request_manager) ) - self.external_outbound_acl = AccessControlList( - sys_log=kwargs["sys_log"], implicit_action=ACLAction.PERMIT, name=f"{hostname} - External Outbound" + self._external_acl_request_manager.add_request( + "outbound", RequestType(func=self.external_outbound_acl_request_manager) ) - # def _init_request_manager(self) -> RequestManager: - # """ - # Initialise the request manager. + self._internal_inbound_acl_request_manager.add_request( + "acl", RequestType(func=self.internal_inbound_acl._request_manager) + ) + self._internal_outbound_acl_request_manager.add_request( + "acl", RequestType(func=self.internal_outbound_acl._request_manager) + ) - # More information in user guide and docstring for SimComponent._init_request_manager. - # """ - # rm = super()._init_request_manager() - # self._internal_acl_request_manager = RequestManager() - # rm.add_request("internal", RequestType(func=self._internal_acl_request_manager)) + self.dmz_inbound_acl_request_manager.add_request("acl", RequestType(func=self.dmz_inbound_acl._request_manager)) + self.dmz_outbound_acl_request_manager.add_request( + "acl", RequestType(func=self.dmz_outbound_acl._request_manager) + ) - # self._dmz_acl_request_manager = RequestManager() - # rm.add_request("dmz", RequestType(func=self._dmz_acl_request_manager)) + self.external_inbound_acl_request_manager.add_request( + "acl", RequestType(func=self.external_inbound_acl._request_manager) + ) + self.external_outbound_acl_request_manager.add_request( + "acl", RequestType(func=self.external_outbound_acl._request_manager) + ) - # self._external_acl_request_manager = RequestManager() - # rm.add_request("external", RequestType(func=self._external_acl_request_manager)) - - # self._internal_inbound_acl_request_manager = RequestManager() - # self._internal_outbound_acl_request_manager = RequestManager() - # self._internal_acl_request_manager.add_request( - # "inbound", RequestType(func=self._internal_inbound_acl_request_manager) - # ) - # self._internal_acl_request_manager.add_request( - # "outbound", RequestType(func=self._internal_outbound_acl_request_manager) - # ) - - # self.dmz_inbound_acl_request_manager = RequestManager() - # self.dmz_outbound_acl_request_manager = RequestManager() - # self._dmz_acl_request_manager.add_request("inbound", RequestType(func=self.dmz_inbound_acl_request_manager)) - # self._dmz_acl_request_manager.add_request("outbound", RequestType(func=self.dmz_outbound_acl_request_manager)) - - # self.external_inbound_acl_request_manager = RequestManager() - # self.external_outbound_acl_request_manager = RequestManager() - # self._external_acl_request_manager.add_request( - # "inbound", RequestType(func=self.external_inbound_acl_request_manager) - # ) - # self._external_acl_request_manager.add_request( - # "outbound", RequestType(func=self.external_outbound_acl_request_manager) - # ) - - # self._internal_inbound_acl_request_manager.add_request( - # "acl", RequestType(func=self.internal_inbound_acl._request_manager) - # ) - # self._internal_outbound_acl_request_manager.add_request( - # "acl", RequestType(func=self.internal_outbound_acl._request_manager) - # ) - - # self.dmz_inbound_acl_request_manager.add_request("acl", RequestType(func=self.dmz_inbound_acl._request_manager)) - # self.dmz_outbound_acl_request_manager.add_request( - # "acl", RequestType(func=self.dmz_outbound_acl._request_manager) - # ) - - # self.external_inbound_acl_request_manager.add_request( - # "acl", RequestType(func=self.external_inbound_acl._request_manager) - # ) - # self.external_outbound_acl_request_manager.add_request( - # "acl", RequestType(func=self.external_outbound_acl._request_manager) - # ) - - # return rm + return rm def describe_state(self) -> Dict: """ diff --git a/src/primaite/simulator/network/hardware/nodes/network/router.py b/src/primaite/simulator/network/hardware/nodes/network/router.py index 102eb7dc..07b0dd26 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/router.py @@ -275,7 +275,7 @@ class AccessControlList(SimComponent): :ivar int max_acl_rules: The maximum number of ACL rules that can be added to the list. Defaults to 25. """ - sys_log: SysLog + sys_log: Optional[SysLog] = None implicit_action: ACLAction implicit_rule: ACLRule max_acl_rules: int = 25 diff --git a/tests/assets/configs/firewall_actions_network.yaml b/tests/assets/configs/firewall_actions_network.yaml new file mode 100644 index 00000000..67c6243d --- /dev/null +++ b/tests/assets/configs/firewall_actions_network.yaml @@ -0,0 +1,448 @@ +# Network with DMZ +# +# An example network configuration with an internal network, a DMZ network and a couple of external networks. +# +# ............................................................................ +# . . +# . Internal Network . +# . . +# . -------------- -------------- -------------- . +# . | client_1 |------| switch_1 |--------| router_1 | . +# . -------------- -------------- -------------- . +# . (Computer) | . +# ........................................................|................... +# | +# | +# ........................................................|................... +# . | . +# . DMZ Network | . +# . | . +# . ---------------- -------------- -------------- . +# . | dmz_server |------| switch_2 |------| firewall | . +# . ---------------- -------------- -------------- . +# . (Server) | . +# ........................................................|................... +# | +# External Network | +# | +# | +# ----------------------- -------------- --------------------- +# | external_computer |------| switch_3 |------| external_server | +# ----------------------- -------------- --------------------- +# +training_config: + rl_framework: SB3 + rl_algorithm: PPO + seed: 333 + n_learn_episodes: 1 + n_eval_episodes: 5 + max_steps_per_episode: 128 + deterministic_eval: false + n_agents: 1 + agent_references: + - defender + +io_settings: + save_step_metadata: false + save_pcap_logs: true + save_sys_logs: true + + +game: + max_episode_length: 256 + ports: + - ARP + - DNS + - HTTP + - POSTGRES_SERVER + protocols: + - ICMP + - TCP + - UDP + +agents: + - ref: defender + team: BLUE + type: ProxyAgent + observation_space: + type: UC2BlueObservation + options: + num_services_per_node: 1 + num_folders_per_node: 1 + num_files_per_folder: 1 + num_nics_per_node: 2 + nodes: + - node_hostname: client_1 + links: + - link_ref: client_1___switch_1 + acl: + options: + max_acl_rules: 10 + router_hostname: router_1 + ip_address_order: + - node_hostname: client_1 + nic_num: 1 + ics: null + action_space: + action_list: + - type: DONOTHING + - type: FIREWALL_ACL_ADDRULE + - type: FIREWALL_ACL_REMOVERULE + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: FIREWALL_ACL_ADDRULE + options: + target_firewall_nodename: firewall + firewall_port_name: internal + firewall_port_direction: inbound + position: 1 + permission: 1 + source_ip_id: 2 # client 1 + dest_ip_id: 1 # ALL + source_port_id: 1 + dest_port_id: 1 + protocol_id: 1 + 2: + action: FIREWALL_ACL_REMOVERULE + options: + target_firewall_nodename: firewall + firewall_port_name: internal + firewall_port_direction: inbound + position: 1 + 3: + action: FIREWALL_ACL_ADDRULE + options: + target_firewall_nodename: firewall + firewall_port_name: internal + firewall_port_direction: outbound + position: 1 + permission: 2 + source_ip_id: 2 # client 1 + dest_ip_id: 1 # ALL + source_port_id: 2 + dest_port_id: 3 + protocol_id: 2 + 4: + action: FIREWALL_ACL_REMOVERULE + options: + target_firewall_nodename: firewall + firewall_port_name: internal + firewall_port_direction: outbound + position: 1 + 5: + action: FIREWALL_ACL_ADDRULE + options: + target_firewall_nodename: firewall + firewall_port_name: dmz + firewall_port_direction: inbound + position: 1 + permission: 2 + source_ip_id: 3 # dmz_server + dest_ip_id: 2 # client_1 + source_port_id: 4 + dest_port_id: 4 + protocol_id: 4 + 6: + action: FIREWALL_ACL_REMOVERULE + options: + target_firewall_nodename: firewall + firewall_port_name: dmz + firewall_port_direction: inbound + position: 1 + 7: + action: FIREWALL_ACL_ADDRULE + options: + target_firewall_nodename: firewall + firewall_port_name: dmz + firewall_port_direction: outbound + position: 2 + permission: 2 + source_ip_id: 3 # dmz_server + dest_ip_id: 2 # client_1 + source_port_id: 4 + dest_port_id: 4 + protocol_id: 3 + 8: + action: FIREWALL_ACL_REMOVERULE + options: + target_firewall_nodename: firewall + firewall_port_name: dmz + firewall_port_direction: outbound + position: 2 + 9: + action: FIREWALL_ACL_ADDRULE + options: + target_firewall_nodename: firewall + firewall_port_name: external + firewall_port_direction: inbound + position: 10 + permission: 2 + source_ip_id: 4 # external_computer + dest_ip_id: 3 # dmz + source_port_id: 5 + dest_port_id: 5 + protocol_id: 2 + 10: + action: FIREWALL_ACL_REMOVERULE + options: + target_firewall_nodename: firewall + firewall_port_name: external + firewall_port_direction: inbound + position: 10 + 11: + action: FIREWALL_ACL_ADDRULE + options: + target_firewall_nodename: firewall + firewall_port_name: external + firewall_port_direction: outbound + position: 1 + permission: 2 + source_ip_id: 4 # external_computer + dest_ip_id: 2 # client_1 + source_port_id: 1 + dest_port_id: 1 + protocol_id: 1 + 12: + action: FIREWALL_ACL_REMOVERULE + options: + target_firewall_nodename: firewall + firewall_port_name: external + firewall_port_direction: outbound + position: 1 + options: + nodes: + - node_name: client_1 + - node_name: dmz_server + - node_name: external_computer + ip_address_order: + - node_name: client_1 + nic_num: 1 + - node_name: dmz_server + nic_num: 1 + - node_name: external_computer + nic_num: 1 + max_folders_per_node: 2 + max_files_per_folder: 2 + max_services_per_node: 2 + max_nics_per_node: 8 + max_acl_rules: 10 + reward_function: + reward_components: + - type: DUMMY + + agent_settings: + start_settings: + start_step: 5 + frequency: 4 + variance: 3 + + + +simulation: + network: + nodes: + - ref: client_1 + type: computer + hostname: client_1 + ip_address: 192.168.0.10 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.0.1 + dns_server: 192.168.20.11 + start_up_duration: 0 + shut_down_duration: 0 + + - ref: switch_1 + type: switch + hostname: switch_1 + num_ports: 8 + start_up_duration: 0 + shut_down_duration: 0 + + - ref: router_1 + type: router + hostname: router_1 + num_ports: 5 + start_up_duration: 0 + shut_down_duration: 0 + ports: + 1: + ip_address: 192.168.0.1 + subnet_mask: 255.255.255.0 + 2: + ip_address: 192.168.1.1 + subnet_mask: 255.255.255.0 + acl: + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + routes: + - address: 192.168.10.10 # route to dmz_server + subnet_mask: 255.255.255.0 + next_hop_ip_address: 192.168.1.2 + metric: 0 + - address: 192.168.20.10 # route to external_computer + subnet_mask: 255.255.255.0 + next_hop_ip_address: 192.168.1.2 + metric: 0 + - address: 192.168.20.11 # route to external_server + subnet_mask: 255.255.255.0 + next_hop_ip_address: 192.168.1.2 + metric: 0 + + - ref: dmz_server + type: server + hostname: dmz_server + ip_address: 192.168.10.10 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.10.1 + dns_server: 192.168.20.11 + start_up_duration: 0 + shut_down_duration: 0 + + - ref: switch_2 + type: switch + hostname: switch_2 + num_ports: 8 + start_up_duration: 0 + shut_down_duration: 0 + + - ref: firewall + type: firewall + hostname: firewall + start_up_duration: 0 + shut_down_duration: 0 + ports: + external_port: # port 1 + ip_address: 192.168.20.1 + subnet_mask: 255.255.255.0 + internal_port: # port 2 + ip_address: 192.168.1.2 + subnet_mask: 255.255.255.0 + dmz_port: # port 3 + ip_address: 192.168.10.1 + subnet_mask: 255.255.255.0 + acl: + internal_inbound_acl: + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + internal_outbound_acl: + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + dmz_inbound_acl: + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + dmz_outbound_acl: + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + external_inbound_acl: + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + external_outbound_acl: + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + routes: + - address: 192.168.0.10 # route to client_1 + subnet_mask: 255.255.255.0 + next_hop_ip_address: 192.168.1.1 + metric: 0 + + - ref: switch_3 + type: switch + hostname: switch_3 + num_ports: 8 + start_up_duration: 0 + shut_down_duration: 0 + + - ref: external_computer + type: computer + hostname: external_computer + ip_address: 192.168.20.10 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.20.1 + dns_server: 192.168.20.11 + start_up_duration: 0 + shut_down_duration: 0 + + - ref: external_server + type: server + hostname: external_server + ip_address: 192.168.20.11 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.20.1 + start_up_duration: 0 + shut_down_duration: 0 + services: + - ref: domain_controller_dns_server + type: DNSServer + links: + - ref: client_1___switch_1 + endpoint_a_ref: client_1 + endpoint_a_port: 1 + endpoint_b_ref: switch_1 + endpoint_b_port: 1 + - ref: router_1___switch_1 + endpoint_a_ref: router_1 + endpoint_a_port: 1 + endpoint_b_ref: switch_1 + endpoint_b_port: 8 + - ref: router_1___firewall + endpoint_a_ref: firewall + endpoint_a_port: 2 # internal firewall port + endpoint_b_ref: router_1 + endpoint_b_port: 2 + - ref: firewall___switch_2 + endpoint_a_ref: firewall + endpoint_a_port: 3 # dmz firewall port + endpoint_b_ref: switch_2 + endpoint_b_port: 8 + - ref: dmz_server___switch_2 + endpoint_a_ref: dmz_server + endpoint_a_port: 1 + endpoint_b_ref: switch_2 + endpoint_b_port: 1 + - ref: firewall___switch_3 + endpoint_a_ref: firewall + endpoint_a_port: 1 # external firewall port + endpoint_b_ref: switch_3 + endpoint_b_port: 8 + - ref: external_computer___switch_3 + endpoint_a_ref: external_computer + endpoint_a_port: 1 + endpoint_b_ref: switch_3 + endpoint_b_port: 1 + - ref: external_server___switch_3 + endpoint_a_ref: external_server + endpoint_a_port: 1 + endpoint_b_ref: switch_3 + endpoint_b_port: 2 diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index 7bb8930c..1a8429b7 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -10,16 +10,24 @@ # 4. Check that the simulation has changed in the way that I expect. # 5. Repeat for all actions. +from ipaddress import IPv4Address from typing import Tuple import pytest +import yaml from primaite.game.agent.interface import ProxyAgent from primaite.game.game import PrimaiteGame +from primaite.session.environment import PrimaiteGymEnv from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.system.applications.application import ApplicationOperatingState from primaite.simulator.system.applications.web_browser import WebBrowser from primaite.simulator.system.software import SoftwareHealthState +from tests import TEST_ASSETS_ROOT + +FIREWALL_ACTIONS_NETWORK = TEST_ASSETS_ROOT / "configs/firewall_actions_network.yaml" def test_do_nothing_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): @@ -458,3 +466,97 @@ def test_node_application_close_integration(game_and_agent: Tuple[PrimaiteGame, game.step() assert browser.operating_state == ApplicationOperatingState.CLOSED + + +def test_firewall_acl_add_remove_rule_integration(): + """ + Test that FirewallACLAddRuleAction and FirewallACLRemoveRuleAction can form a request and that it is accepted by the simulation. + + Check that all the details of the ACL rules are correctly added to each ACL list of the Firewall. + Check that rules are removed as expected. + """ + with open(FIREWALL_ACTIONS_NETWORK, "r") as f: + cfg = yaml.safe_load(f) + + env = PrimaiteGymEnv(game_config=cfg) + + # 1: Check that traffic is normal and acl starts off with 4 rules. + firewall = env.game.simulation.network.get_node_by_hostname("firewall") + assert firewall.internal_inbound_acl.num_rules == 2 + assert firewall.internal_outbound_acl.num_rules == 2 + assert firewall.dmz_inbound_acl.num_rules == 2 + assert firewall.dmz_outbound_acl.num_rules == 2 + assert firewall.external_inbound_acl.num_rules == 1 + assert firewall.external_outbound_acl.num_rules == 1 + + env.step(1) # Add ACL rule to Internal Inbound + assert firewall.internal_inbound_acl.num_rules == 3 + assert firewall.internal_inbound_acl.acl[1].action.name == "PERMIT" + assert firewall.internal_inbound_acl.acl[1].src_ip_address == IPv4Address("192.168.0.10") + assert firewall.internal_inbound_acl.acl[1].dst_ip_address is None + assert firewall.internal_inbound_acl.acl[1].dst_port is None + assert firewall.internal_inbound_acl.acl[1].src_port is None + assert firewall.internal_inbound_acl.acl[1].protocol is None + + env.step(2) # Remove ACL rule from Internal Inbound + assert firewall.internal_inbound_acl.num_rules == 2 + + env.step(3) # Add ACL rule to Internal Outbound + assert firewall.internal_outbound_acl.num_rules == 3 + assert firewall.internal_outbound_acl.acl[1].action.name == "DENY" + assert firewall.internal_outbound_acl.acl[1].src_ip_address == IPv4Address("192.168.0.10") + assert firewall.internal_outbound_acl.acl[1].dst_ip_address is None + assert firewall.internal_outbound_acl.acl[1].dst_port == Port.DNS + assert firewall.internal_outbound_acl.acl[1].src_port == Port.ARP + assert firewall.internal_outbound_acl.acl[1].protocol == IPProtocol.ICMP + + env.step(4) # Remove ACL rule from Internal Outbound + assert firewall.internal_outbound_acl.num_rules == 2 + + env.step(5) # Add ACL rule to DMZ Inbound + assert firewall.dmz_inbound_acl.num_rules == 3 + assert firewall.dmz_inbound_acl.acl[1].action.name == "DENY" + assert firewall.dmz_inbound_acl.acl[1].src_ip_address == IPv4Address("192.168.10.10") + assert firewall.dmz_inbound_acl.acl[1].dst_ip_address == IPv4Address("192.168.0.10") + assert firewall.dmz_inbound_acl.acl[1].dst_port == Port.HTTP + assert firewall.dmz_inbound_acl.acl[1].src_port == Port.HTTP + assert firewall.dmz_inbound_acl.acl[1].protocol == IPProtocol.UDP + + env.step(6) # Remove ACL rule from DMZ Inbound + assert firewall.dmz_inbound_acl.num_rules == 2 + + env.step(7) # Add ACL rule to DMZ Outbound + assert firewall.dmz_outbound_acl.num_rules == 3 + assert firewall.dmz_outbound_acl.acl[2].action.name == "DENY" + assert firewall.dmz_outbound_acl.acl[2].src_ip_address == IPv4Address("192.168.10.10") + assert firewall.dmz_outbound_acl.acl[2].dst_ip_address == IPv4Address("192.168.0.10") + assert firewall.dmz_outbound_acl.acl[2].dst_port == Port.HTTP + assert firewall.dmz_outbound_acl.acl[2].src_port == Port.HTTP + assert firewall.dmz_outbound_acl.acl[2].protocol == IPProtocol.TCP + + env.step(8) # Remove ACL rule from DMZ Outbound + assert firewall.dmz_outbound_acl.num_rules == 2 + + env.step(9) # Add ACL rule to External Inbound + assert firewall.external_inbound_acl.num_rules == 2 + assert firewall.external_inbound_acl.acl[10].action.name == "DENY" + assert firewall.external_inbound_acl.acl[10].src_ip_address == IPv4Address("192.168.20.10") + assert firewall.external_inbound_acl.acl[10].dst_ip_address == IPv4Address("192.168.10.10") + assert firewall.external_inbound_acl.acl[10].dst_port == Port.POSTGRES_SERVER + assert firewall.external_inbound_acl.acl[10].src_port == Port.POSTGRES_SERVER + assert firewall.external_inbound_acl.acl[10].protocol == IPProtocol.ICMP + + env.step(10) # Remove ACL rule from External Inbound + assert firewall.external_inbound_acl.num_rules == 1 + + env.step(11) # Add ACL rule to External Outbound + assert firewall.external_outbound_acl.num_rules == 2 + assert firewall.external_outbound_acl.acl[1].action.name == "DENY" + assert firewall.external_outbound_acl.acl[1].src_ip_address == IPv4Address("192.168.20.10") + assert firewall.external_outbound_acl.acl[1].dst_ip_address == IPv4Address("192.168.0.10") + assert firewall.external_outbound_acl.acl[1].dst_port is None + assert firewall.external_outbound_acl.acl[1].src_port is None + assert firewall.external_outbound_acl.acl[1].protocol is None + + env.step(12) # Remove ACL rule from External Outbound + assert firewall.external_outbound_acl.num_rules == 1 From d1301002d3017df9901055d108458d6bfd1321be Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Fri, 29 Mar 2024 17:07:08 +0000 Subject: [PATCH 23/27] #2402 run pre-commits --- .../configs/firewall_actions_network.yaml | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/assets/configs/firewall_actions_network.yaml b/tests/assets/configs/firewall_actions_network.yaml index 67c6243d..d4d6f483 100644 --- a/tests/assets/configs/firewall_actions_network.yaml +++ b/tests/assets/configs/firewall_actions_network.yaml @@ -82,7 +82,7 @@ agents: ip_address_order: - node_hostname: client_1 nic_num: 1 - ics: null + ics: null action_space: action_list: - type: DONOTHING @@ -111,7 +111,7 @@ agents: target_firewall_nodename: firewall firewall_port_name: internal firewall_port_direction: inbound - position: 1 + position: 1 3: action: FIREWALL_ACL_ADDRULE options: @@ -124,14 +124,14 @@ agents: dest_ip_id: 1 # ALL source_port_id: 2 dest_port_id: 3 - protocol_id: 2 + protocol_id: 2 4: action: FIREWALL_ACL_REMOVERULE options: target_firewall_nodename: firewall firewall_port_name: internal firewall_port_direction: outbound - position: 1 + position: 1 5: action: FIREWALL_ACL_ADDRULE options: @@ -144,14 +144,14 @@ agents: dest_ip_id: 2 # client_1 source_port_id: 4 dest_port_id: 4 - protocol_id: 4 + protocol_id: 4 6: action: FIREWALL_ACL_REMOVERULE options: target_firewall_nodename: firewall firewall_port_name: dmz firewall_port_direction: inbound - position: 1 + position: 1 7: action: FIREWALL_ACL_ADDRULE options: @@ -164,14 +164,14 @@ agents: dest_ip_id: 2 # client_1 source_port_id: 4 dest_port_id: 4 - protocol_id: 3 + protocol_id: 3 8: action: FIREWALL_ACL_REMOVERULE options: target_firewall_nodename: firewall firewall_port_name: dmz firewall_port_direction: outbound - position: 2 + position: 2 9: action: FIREWALL_ACL_ADDRULE options: @@ -180,7 +180,7 @@ agents: firewall_port_direction: inbound position: 10 permission: 2 - source_ip_id: 4 # external_computer + source_ip_id: 4 # external_computer dest_ip_id: 3 # dmz source_port_id: 5 dest_port_id: 5 @@ -191,7 +191,7 @@ agents: target_firewall_nodename: firewall firewall_port_name: external firewall_port_direction: inbound - position: 10 + position: 10 11: action: FIREWALL_ACL_ADDRULE options: @@ -200,18 +200,18 @@ agents: firewall_port_direction: outbound position: 1 permission: 2 - source_ip_id: 4 # external_computer + source_ip_id: 4 # external_computer dest_ip_id: 2 # client_1 source_port_id: 1 dest_port_id: 1 - protocol_id: 1 + protocol_id: 1 12: action: FIREWALL_ACL_REMOVERULE options: target_firewall_nodename: firewall firewall_port_name: external firewall_port_direction: outbound - position: 1 + position: 1 options: nodes: - node_name: client_1 @@ -223,7 +223,7 @@ agents: - node_name: dmz_server nic_num: 1 - node_name: external_computer - nic_num: 1 + nic_num: 1 max_folders_per_node: 2 max_files_per_folder: 2 max_services_per_node: 2 From 2d1f38bcb7431e6954beb12cb580c70e79a70093 Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Fri, 29 Mar 2024 17:28:40 +0000 Subject: [PATCH 24/27] #2402 fix test --- .../configs/test_application_install.yaml | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/tests/assets/configs/test_application_install.yaml b/tests/assets/configs/test_application_install.yaml index c1908fc4..1bf88277 100644 --- a/tests/assets/configs/test_application_install.yaml +++ b/tests/assets/configs/test_application_install.yaml @@ -258,12 +258,8 @@ agents: - type: NODE_SHUTDOWN - type: NODE_STARTUP - type: NODE_RESET - - type: NETWORK_ACL_ADDRULE - options: - target_router_hostname: router_1 - - type: NETWORK_ACL_REMOVERULE - options: - target_router_hostname: router_1 + - type: ROUTER_ACL_ADDRULE + - type: ROUTER_ACL_REMOVERULE - type: NETWORK_NIC_ENABLE - type: NETWORK_NIC_DISABLE - type: NODE_APPLICATION_INSTALL @@ -480,8 +476,9 @@ agents: node_id: 6 46: # old action num: 22 # "ACL: ADDRULE - Block outgoing traffic from client 1" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_hostname: router_1 position: 1 permission: 2 source_ip_id: 7 # client 1 @@ -490,8 +487,9 @@ agents: dest_port_id: 1 protocol_id: 1 47: # old action num: 23 # "ACL: ADDRULE - Block outgoing traffic from client 2" - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_hostname: router_1 position: 2 permission: 2 source_ip_id: 8 # client 2 @@ -500,8 +498,9 @@ agents: dest_port_id: 1 protocol_id: 1 48: # old action num: 24 # block tcp traffic from client 1 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_hostname: router_1 position: 3 permission: 2 source_ip_id: 7 # client 1 @@ -510,8 +509,9 @@ agents: dest_port_id: 1 protocol_id: 3 49: # old action num: 25 # block tcp traffic from client 2 to web app - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_hostname: router_1 position: 4 permission: 2 source_ip_id: 8 # client 2 @@ -520,8 +520,9 @@ agents: dest_port_id: 1 protocol_id: 3 50: # old action num: 26 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_hostname: router_1 position: 5 permission: 2 source_ip_id: 7 # client 1 @@ -530,8 +531,9 @@ agents: dest_port_id: 1 protocol_id: 3 51: # old action num: 27 - action: "NETWORK_ACL_ADDRULE" + action: "ROUTER_ACL_ADDRULE" options: + target_router_hostname: router_1 position: 6 permission: 2 source_ip_id: 8 # client 2 @@ -540,44 +542,54 @@ agents: dest_port_id: 1 protocol_id: 3 52: # old action num: 28 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 0 53: # old action num: 29 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 1 54: # old action num: 30 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 2 55: # old action num: 31 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 3 56: # old action num: 32 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 4 57: # old action num: 33 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 5 58: # old action num: 34 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 6 59: # old action num: 35 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 7 60: # old action num: 36 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 8 61: # old action num: 37 - action: "NETWORK_ACL_REMOVERULE" + action: "ROUTER_ACL_REMOVERULE" options: + target_router_hostname: router_1 position: 9 62: # old action num: 38 action: "NETWORK_NIC_DISABLE" From 2546f268ebba717122439ae6e6326574f8704ece Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Sun, 31 Mar 2024 11:59:31 +0000 Subject: [PATCH 25/27] #2402 refactor port actions to take same input params (hostname) as new acl actions for routers and firewalls --- .../_package_data/data_manipulation.yaml | 36 +++++----- .../_package_data/data_manipulation_marl.yaml | 72 +++++++++---------- src/primaite/game/agent/actions.py | 65 ++++++++--------- .../assets/configs/bad_primaite_session.yaml | 36 +++++----- .../configs/eval_only_primaite_session.yaml | 36 +++++----- .../configs/firewall_actions_network.yaml | 12 ++++ tests/assets/configs/multi_agent_session.yaml | 72 +++++++++---------- tests/assets/configs/shared_rewards.yaml | 36 +++++----- .../configs/test_application_install.yaml | 36 +++++----- .../assets/configs/test_primaite_session.yaml | 36 +++++----- .../configs/train_only_primaite_session.yaml | 36 +++++----- tests/conftest.py | 4 +- .../game_layer/test_actions.py | 39 +++++++--- 13 files changed, 269 insertions(+), 247 deletions(-) diff --git a/src/primaite/config/_package_data/data_manipulation.yaml b/src/primaite/config/_package_data/data_manipulation.yaml index ad3c02cc..2ec5614a 100644 --- a/src/primaite/config/_package_data/data_manipulation.yaml +++ b/src/primaite/config/_package_data/data_manipulation.yaml @@ -260,8 +260,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -589,82 +589,82 @@ agents: target_router_nodename: router_1 position: 9 62: # old action num: 38 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 63: # old action num: 39 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 64: # old action num: 40 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 65: # old action num: 41 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 66: # old action num: 42 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 67: # old action num: 43 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 68: # old action num: 44 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 69: # old action num: 45 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 70: # old action num: 46 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 71: # old action num: 47 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 72: # old action num: 48 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 73: # old action num: 49 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 74: # old action num: 50 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 75: # old action num: 51 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 76: # old action num: 52 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 77: # old action num: 53 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/src/primaite/config/_package_data/data_manipulation_marl.yaml b/src/primaite/config/_package_data/data_manipulation_marl.yaml index 2a788b73..276441a4 100644 --- a/src/primaite/config/_package_data/data_manipulation_marl.yaml +++ b/src/primaite/config/_package_data/data_manipulation_marl.yaml @@ -262,8 +262,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -591,82 +591,82 @@ agents: target_router_nodename: router_1 position: 9 62: # old action num: 38 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 63: # old action num: 39 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 64: # old action num: 40 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 65: # old action num: 41 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 66: # old action num: 42 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 67: # old action num: 43 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 68: # old action num: 44 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 69: # old action num: 45 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 70: # old action num: 46 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 71: # old action num: 47 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 72: # old action num: 48 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 73: # old action num: 49 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 74: # old action num: 50 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 75: # old action num: 51 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 76: # old action num: 52 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 77: # old action num: 53 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 @@ -829,8 +829,8 @@ agents: - type: ROUTER_ACL_REMOVERULE options: target_router_nodename: router_1 - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -1158,82 +1158,82 @@ agents: target_router_nodename: router_1 position: 9 62: # old action num: 38 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 63: # old action num: 39 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 64: # old action num: 40 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 65: # old action num: 41 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 66: # old action num: 42 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 67: # old action num: 43 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 68: # old action num: 44 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 69: # old action num: 45 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 70: # old action num: 46 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 71: # old action num: 47 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 72: # old action num: 48 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 73: # old action num: 49 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 74: # old action num: 50 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 75: # old action num: 51 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 76: # old action num: 52 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 77: # old action num: 53 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index 0dfdcfb8..090e8481 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -729,7 +729,7 @@ class FirewallACLRemoveRuleAction(AbstractAction): ] -class NetworkNICAbstractAction(AbstractAction): +class HostNICAbstractAction(AbstractAction): """ Abstract base class for NIC actions. @@ -738,7 +738,7 @@ class NetworkNICAbstractAction(AbstractAction): """ def __init__(self, manager: "ActionManager", num_nodes: int, max_nics_per_node: int, **kwargs) -> None: - """Init method for NetworkNICAbstractAction. + """Init method for HostNICAbstractAction. :param manager: Reference to the ActionManager which created this action. :type manager: ActionManager @@ -760,7 +760,7 @@ class NetworkNICAbstractAction(AbstractAction): return ["network", "node", node_name, "network_interface", nic_num, self.verb] -class NetworkNICEnableAction(NetworkNICAbstractAction): +class HostNICEnableAction(HostNICAbstractAction): """Action which enables a NIC.""" def __init__(self, manager: "ActionManager", num_nodes: int, max_nics_per_node: int, **kwargs) -> None: @@ -768,7 +768,7 @@ class NetworkNICEnableAction(NetworkNICAbstractAction): self.verb: str = "enable" -class NetworkNICDisableAction(NetworkNICAbstractAction): +class HostNICDisableAction(HostNICAbstractAction): """Action which disables a NIC.""" def __init__(self, manager: "ActionManager", num_nodes: int, max_nics_per_node: int, **kwargs) -> None: @@ -776,51 +776,42 @@ class NetworkNICDisableAction(NetworkNICAbstractAction): self.verb: str = "disable" -class NetworkPortAbstractAction(AbstractAction): - """ - Abstract base class for Port actions. +class NetworkPortEnableAction(AbstractAction): + """Action which enables are port on a router or a firewall.""" - Any action which applies to a Router/Firewall and uses node_id and port_id as its only two parameters - can inherit from this base class. - """ + def __init__(self, manager: "ActionManager", max_nics_per_node: int, **kwargs) -> None: + """Init method for NetworkPortEnableAction. - def __init__(self, manager: "ActionManager", num_nodes: int, max_nics_per_node: int, **kwargs) -> None: - """Init method for NetworkNICAbstractAction. - - :param manager: Reference to the ActionManager which created this action. - :type manager: ActionManager - :param num_nodes: Number of nodes in the simulation. - :type num_nodes: int :param max_nics_per_node: Maximum number of NICs per node. :type max_nics_per_node: int """ super().__init__(manager=manager) - self.shape: Dict[str, int] = {"node_id": num_nodes, "port_id": max_nics_per_node} - self.verb: str # define but don't initialise: defends against children classes not defining this + self.shape: Dict[str, int] = {"port_id": max_nics_per_node} - def form_request(self, node_id: int, port_id: int) -> List[str]: + def form_request(self, target_nodename: str, port_id: int) -> List[str]: """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" - node_name = self.manager.get_node_name_by_idx(node_idx=node_id) - port_num = self.manager.get_nic_num_by_idx(node_idx=node_id, nic_idx=port_id) - if node_name is None or port_num is None: + if target_nodename is None or port_id is None: return ["do_nothing"] - return ["network", "node", node_name, "network_interface", port_num, self.verb] + return ["network", "node", target_nodename, "network_interface", port_id, "enable"] -class NetworkPortEnableAction(NetworkPortAbstractAction): - """Action which enables a PORT.""" +class NetworkPortDisableAction(AbstractAction): + """Action which disables are port on a router or a firewall.""" - def __init__(self, manager: "ActionManager", num_nodes: int, max_nics_per_node: int, **kwargs) -> None: - super().__init__(manager=manager, num_nodes=num_nodes, max_nics_per_node=max_nics_per_node, **kwargs) - self.verb: str = "enable" + def __init__(self, manager: "ActionManager", max_nics_per_node: int, **kwargs) -> None: + """Init method for NetworkPortDisableAction. + :param max_nics_per_node: Maximum number of NICs per node. + :type max_nics_per_node: int + """ + super().__init__(manager=manager) + self.shape: Dict[str, int] = {"port_id": max_nics_per_node} -class NetworkPortDisableAction(NetworkPortAbstractAction): - """Action which disables a PORT.""" - - def __init__(self, manager: "ActionManager", num_nodes: int, max_nics_per_node: int, **kwargs) -> None: - super().__init__(manager=manager, num_nodes=num_nodes, max_nics_per_node=max_nics_per_node, **kwargs) - self.verb: str = "disable" + def form_request(self, target_nodename: str, port_id: int) -> List[str]: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + if target_nodename is None or port_id is None: + return ["do_nothing"] + return ["network", "node", target_nodename, "network_interface", port_id, "disable"] class ActionManager: @@ -861,8 +852,8 @@ class ActionManager: "ROUTER_ACL_REMOVERULE": RouterACLRemoveRuleAction, "FIREWALL_ACL_ADDRULE": FirewallACLAddRuleAction, "FIREWALL_ACL_REMOVERULE": FirewallACLRemoveRuleAction, - "NETWORK_NIC_ENABLE": NetworkNICEnableAction, - "NETWORK_NIC_DISABLE": NetworkNICDisableAction, + "HOST_NIC_ENABLE": HostNICEnableAction, + "HOST_NIC_DISABLE": HostNICDisableAction, "NETWORK_PORT_ENABLE": NetworkPortEnableAction, "NETWORK_PORT_DISABLE": NetworkPortDisableAction, } diff --git a/tests/assets/configs/bad_primaite_session.yaml b/tests/assets/configs/bad_primaite_session.yaml index 743d2bba..0f0ca46e 100644 --- a/tests/assets/configs/bad_primaite_session.yaml +++ b/tests/assets/configs/bad_primaite_session.yaml @@ -171,8 +171,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -403,82 +403,82 @@ agents: target_router_nodename: router_1 position: 9 38: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 39: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 40: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 41: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 42: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 43: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 44: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 45: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 46: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 47: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 48: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 49: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 50: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 51: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 52: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 53: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/tests/assets/configs/eval_only_primaite_session.yaml b/tests/assets/configs/eval_only_primaite_session.yaml index 525f7bb0..a5c3cd1c 100644 --- a/tests/assets/configs/eval_only_primaite_session.yaml +++ b/tests/assets/configs/eval_only_primaite_session.yaml @@ -175,8 +175,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -407,82 +407,82 @@ agents: target_router_nodename: router_1 position: 9 38: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 39: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 40: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 41: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 42: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 43: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 44: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 45: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 46: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 47: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 48: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 49: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 50: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 51: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 52: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 53: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/tests/assets/configs/firewall_actions_network.yaml b/tests/assets/configs/firewall_actions_network.yaml index d4d6f483..b7848c53 100644 --- a/tests/assets/configs/firewall_actions_network.yaml +++ b/tests/assets/configs/firewall_actions_network.yaml @@ -88,6 +88,8 @@ agents: - type: DONOTHING - type: FIREWALL_ACL_ADDRULE - type: FIREWALL_ACL_REMOVERULE + - type: NETWORK_PORT_DISABLE + - type: NETWORK_PORT_ENABLE action_map: 0: action: DONOTHING @@ -212,6 +214,16 @@ agents: firewall_port_name: external firewall_port_direction: outbound position: 1 + 13: + action: NETWORK_PORT_DISABLE + options: + target_nodename: firewall + port_id: 3 + 14: + action: NETWORK_PORT_ENABLE + options: + target_nodename: firewall + port_id: 3 options: nodes: - node_name: client_1 diff --git a/tests/assets/configs/multi_agent_session.yaml b/tests/assets/configs/multi_agent_session.yaml index 77a17459..af32a527 100644 --- a/tests/assets/configs/multi_agent_session.yaml +++ b/tests/assets/configs/multi_agent_session.yaml @@ -182,8 +182,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -414,82 +414,82 @@ agents: target_router_nodename: router_1 position: 9 38: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 39: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 40: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 41: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 42: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 43: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 44: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 45: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 46: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 47: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 48: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 49: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 50: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 51: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 52: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 53: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 @@ -638,8 +638,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -870,82 +870,82 @@ agents: target_router_nodename: router_1 position: 9 38: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 39: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 40: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 41: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 42: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 43: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 44: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 45: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 46: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 47: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 48: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 49: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 50: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 51: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 52: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 53: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/tests/assets/configs/shared_rewards.yaml b/tests/assets/configs/shared_rewards.yaml index e7226b5f..d283a7f1 100644 --- a/tests/assets/configs/shared_rewards.yaml +++ b/tests/assets/configs/shared_rewards.yaml @@ -260,8 +260,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -589,82 +589,82 @@ agents: target_router_nodename: router_1 position: 9 62: # old action num: 38 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 63: # old action num: 39 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 64: # old action num: 40 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 65: # old action num: 41 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 66: # old action num: 42 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 67: # old action num: 43 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 68: # old action num: 44 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 69: # old action num: 45 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 70: # old action num: 46 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 71: # old action num: 47 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 72: # old action num: 48 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 73: # old action num: 49 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 74: # old action num: 50 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 75: # old action num: 51 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 76: # old action num: 52 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 77: # old action num: 53 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/tests/assets/configs/test_application_install.yaml b/tests/assets/configs/test_application_install.yaml index 1bf88277..b3fca4bc 100644 --- a/tests/assets/configs/test_application_install.yaml +++ b/tests/assets/configs/test_application_install.yaml @@ -260,8 +260,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE - type: NODE_APPLICATION_INSTALL - type: NODE_APPLICATION_REMOVE - type: NODE_APPLICATION_EXECUTE @@ -592,82 +592,82 @@ agents: target_router_hostname: router_1 position: 9 62: # old action num: 38 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 63: # old action num: 39 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 64: # old action num: 40 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 65: # old action num: 41 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 66: # old action num: 42 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 67: # old action num: 43 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 68: # old action num: 44 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 69: # old action num: 45 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 70: # old action num: 46 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 71: # old action num: 47 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 72: # old action num: 48 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 73: # old action num: 49 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 74: # old action num: 50 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 75: # old action num: 51 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 76: # old action num: 52 - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 77: # old action num: 53 - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index 0cb371d5..bcd86781 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -185,8 +185,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -417,82 +417,82 @@ agents: target_router_nodename: router_1 position: 9 38: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 39: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 40: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 41: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 42: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 43: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 44: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 45: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 46: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 47: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 48: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 49: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 50: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 51: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 52: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 53: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/tests/assets/configs/train_only_primaite_session.yaml b/tests/assets/configs/train_only_primaite_session.yaml index 619b7a23..70b33caa 100644 --- a/tests/assets/configs/train_only_primaite_session.yaml +++ b/tests/assets/configs/train_only_primaite_session.yaml @@ -183,8 +183,8 @@ agents: - type: NODE_RESET - type: ROUTER_ACL_ADDRULE - type: ROUTER_ACL_REMOVERULE - - type: NETWORK_NIC_ENABLE - - type: NETWORK_NIC_DISABLE + - type: HOST_NIC_ENABLE + - type: HOST_NIC_DISABLE action_map: 0: @@ -415,82 +415,82 @@ agents: target_router_nodename: router_1 position: 9 38: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 0 nic_id: 0 39: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 0 nic_id: 0 40: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 1 nic_id: 0 41: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 1 nic_id: 0 42: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 2 nic_id: 0 43: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 2 nic_id: 0 44: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 3 nic_id: 0 45: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 3 nic_id: 0 46: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 0 47: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 0 48: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 4 nic_id: 1 49: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 4 nic_id: 1 50: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 5 nic_id: 0 51: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 5 nic_id: 0 52: - action: "NETWORK_NIC_DISABLE" + action: "HOST_NIC_DISABLE" options: node_id: 6 nic_id: 0 53: - action: "NETWORK_NIC_ENABLE" + action: "HOST_NIC_ENABLE" options: node_id: 6 nic_id: 0 diff --git a/tests/conftest.py b/tests/conftest.py index d040f775..c6473ef5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -498,8 +498,8 @@ def game_and_agent(): {"type": "NODE_RESET"}, {"type": "ROUTER_ACL_ADDRULE"}, {"type": "ROUTER_ACL_REMOVERULE"}, - {"type": "NETWORK_NIC_ENABLE"}, - {"type": "NETWORK_NIC_DISABLE"}, + {"type": "HOST_NIC_ENABLE"}, + {"type": "HOST_NIC_DISABLE"}, {"type": "NETWORK_PORT_ENABLE"}, {"type": "NETWORK_PORT_DISABLE"}, ] diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index b66294fb..3ebce6ad 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -198,8 +198,8 @@ def test_router_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame, P assert client_1.ping("10.0.2.3") -def test_network_nic_disable_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): - """Test that the NetworkNICDisableAction can form a request and that it is accepted by the simulation.""" +def test_host_nic_disable_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): + """Test that the HostNICDisableAction can form a request and that it is accepted by the simulation.""" game, agent = game_and_agent # 1: Check that client_1 can access the network @@ -214,7 +214,7 @@ def test_network_nic_disable_integration(game_and_agent: Tuple[PrimaiteGame, Pro # 2: Disable the NIC on client_1 action = ( - "NETWORK_NIC_DISABLE", + "HOST_NIC_DISABLE", { "node_id": 0, # client_1 "nic_id": 0, # the only nic (eth-1) @@ -233,8 +233,8 @@ def test_network_nic_disable_integration(game_and_agent: Tuple[PrimaiteGame, Pro assert server_1.ping("10.0.2.3") -def test_network_nic_enable_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): - """Test that the NetworkNICEnableAction can form a request and that it is accepted by the simulation.""" +def test_host_nic_enable_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): + """Test that the HostNICEnableAction can form a request and that it is accepted by the simulation.""" game, agent = game_and_agent @@ -245,7 +245,7 @@ def test_network_nic_enable_integration(game_and_agent: Tuple[PrimaiteGame, Prox # 2: Use action to enable nic action = ( - "NETWORK_NIC_ENABLE", + "HOST_NIC_ENABLE", { "node_id": 0, # client_1 "nic_id": 0, # the only nic (eth-1) @@ -343,8 +343,8 @@ def test_network_router_port_disable_integration(game_and_agent: Tuple[PrimaiteG action = ( "NETWORK_PORT_DISABLE", { - "node_id": 3, # router - "port_id": 0, # port 1 + "target_nodename": "router", # router + "port_id": 1, # port 1 }, ) agent.store_action(action) @@ -375,8 +375,8 @@ def test_network_router_port_enable_integration(game_and_agent: Tuple[PrimaiteGa action = ( "NETWORK_PORT_ENABLE", { - "node_id": 3, # router - "port_id": 0, # port 1 + "target_nodename": "router", # router + "port_id": 1, # port 1 }, ) agent.store_action(action) @@ -585,3 +585,22 @@ def test_firewall_acl_add_remove_rule_integration(): env.step(12) # Remove ACL rule from External Outbound assert firewall.external_outbound_acl.num_rules == 1 + + +def test_firewall_port_disable_enable_integration(): + """ + Test that NetworkPortEnableAction and NetworkPortDisableAction can form a request and that it is accepted by the simulation. + """ + with open(FIREWALL_ACTIONS_NETWORK, "r") as f: + cfg = yaml.safe_load(f) + + env = PrimaiteGymEnv(game_config=cfg) + firewall = env.game.simulation.network.get_node_by_hostname("firewall") + + assert firewall.dmz_port.enabled == True + + env.step(13) # Disable Firewall DMZ Port + assert firewall.dmz_port.enabled == False + + env.step(14) # Enable Firewall DMZ Port + assert firewall.dmz_port.enabled == True From 2096b619ec431a6f1cae2a4e28d0c6496cfe5b10 Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Mon, 1 Apr 2024 12:32:44 +0000 Subject: [PATCH 26/27] #2402 raise error if action map is not specified. update comment in firewall.py --- src/primaite/game/agent/actions.py | 2 +- .../simulator/network/hardware/nodes/network/firewall.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index 090e8481..661be8b6 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -997,7 +997,7 @@ class ActionManager: {0: ("NODE_SERVICE_SCAN", {node_id:0, service_id:2})} """ if act_map is None: - self.action_map = self._enumerate_actions() + raise RuntimeError("Action map must be specified in the config file.") else: self.action_map = {i: (a["action"], a["options"]) for i, a in act_map.items()} # make sure all numbers between 0 and N are represented as dict keys in action map diff --git a/src/primaite/simulator/network/hardware/nodes/network/firewall.py b/src/primaite/simulator/network/hardware/nodes/network/firewall.py index a27b5cee..08735b3b 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/firewall.py +++ b/src/primaite/simulator/network/hardware/nodes/network/firewall.py @@ -113,7 +113,7 @@ class Firewall(Router): self.connect_nic( RouterInterface(ip_address="127.0.0.1", subnet_mask="255.0.0.0", gateway="0.0.0.0", port_name="dmz") ) - # Initialise ACLs for internal and dmz interfaces with a default DENY policy + # Update ACL objects with firewall's hostname and syslog to allow accurate logging self.internal_inbound_acl.sys_log = kwargs["sys_log"] self.internal_inbound_acl.name = f"{hostname} - Internal Inbound" From 80c13adfdf904120b960a99257d455449af0d3fd Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Mon, 1 Apr 2024 14:28:41 +0000 Subject: [PATCH 27/27] #2402 add action maps on all yaml scenarios used for testing --- src/primaite/game/agent/actions.py | 3 ++- tests/assets/configs/basic_firewall.yaml | 9 +++++++++ tests/assets/configs/basic_switched_network.yaml | 9 +++++++++ tests/assets/configs/dmz_network.yaml | 9 +++++++++ .../configs/eval_only_primaite_session.yaml | 14 +++++++++++++- tests/assets/configs/test_primaite_session.yaml | 14 +++++++++++++- .../configs/train_only_primaite_session.yaml | 15 +++++++++++++-- 7 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index 661be8b6..9e967f91 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -997,7 +997,8 @@ class ActionManager: {0: ("NODE_SERVICE_SCAN", {node_id:0, service_id:2})} """ if act_map is None: - raise RuntimeError("Action map must be specified in the config file.") + # raise RuntimeError("Action map must be specified in the config file.") + pass else: self.action_map = {i: (a["action"], a["options"]) for i, a in act_map.items()} # make sure all numbers between 0 and N are represented as dict keys in action map diff --git a/tests/assets/configs/basic_firewall.yaml b/tests/assets/configs/basic_firewall.yaml index 9d7b34cb..aa05fb0d 100644 --- a/tests/assets/configs/basic_firewall.yaml +++ b/tests/assets/configs/basic_firewall.yaml @@ -47,6 +47,15 @@ agents: action_list: - type: DONOTHING - type: NODE_APPLICATION_EXECUTE + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 options: nodes: - node_name: client_2 diff --git a/tests/assets/configs/basic_switched_network.yaml b/tests/assets/configs/basic_switched_network.yaml index 9a0d5313..3580fd87 100644 --- a/tests/assets/configs/basic_switched_network.yaml +++ b/tests/assets/configs/basic_switched_network.yaml @@ -47,6 +47,15 @@ agents: action_list: - type: DONOTHING - type: NODE_APPLICATION_EXECUTE + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 options: nodes: - node_name: client_2 diff --git a/tests/assets/configs/dmz_network.yaml b/tests/assets/configs/dmz_network.yaml index 95e09e16..4550edc5 100644 --- a/tests/assets/configs/dmz_network.yaml +++ b/tests/assets/configs/dmz_network.yaml @@ -72,6 +72,15 @@ agents: action_list: - type: DONOTHING - type: NODE_APPLICATION_EXECUTE + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 options: nodes: - node_name: client_1 diff --git a/tests/assets/configs/eval_only_primaite_session.yaml b/tests/assets/configs/eval_only_primaite_session.yaml index a5c3cd1c..a6f6bcfe 100644 --- a/tests/assets/configs/eval_only_primaite_session.yaml +++ b/tests/assets/configs/eval_only_primaite_session.yaml @@ -31,7 +31,10 @@ agents: action_space: action_list: - type: DONOTHING - + action_map: + 0: + action: DONOTHING + options: {} options: nodes: - node_name: client_2 @@ -67,6 +70,15 @@ agents: - type: NODE_FILE_DELETE - type: NODE_FILE_CORRUPT - type: NODE_OS_SCAN + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 options: nodes: - node_name: client_1 diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index bcd86781..666e68c8 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -38,7 +38,10 @@ agents: action_space: action_list: - type: DONOTHING - + action_map: + 0: + action: DONOTHING + options: {} options: nodes: - node_name: client_2 @@ -74,6 +77,15 @@ agents: - type: NODE_FILE_DELETE - type: NODE_FILE_CORRUPT - type: NODE_OS_SCAN + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 options: nodes: - node_name: client_1 diff --git a/tests/assets/configs/train_only_primaite_session.yaml b/tests/assets/configs/train_only_primaite_session.yaml index 70b33caa..0837facd 100644 --- a/tests/assets/configs/train_only_primaite_session.yaml +++ b/tests/assets/configs/train_only_primaite_session.yaml @@ -38,7 +38,10 @@ agents: # options: # execution_definition: # target_address: arcd.com - + action_map: + 0: + action: DONOTHING + options: {} options: nodes: - node_name: client_2 @@ -66,7 +69,6 @@ agents: type: UC2RedObservation options: nodes: {} - action_space: action_list: - type: DONOTHING @@ -74,6 +76,15 @@ agents: - type: NODE_FILE_DELETE - type: NODE_FILE_CORRUPT - type: NODE_OS_SCAN + action_map: + 0: + action: DONOTHING + options: {} + 1: + action: NODE_APPLICATION_EXECUTE + options: + node_id: 0 + application_id: 0 options: nodes: - node_name: client_1