From f21ee857a7b416f66e1ffdc84c9e8f032db7b9dd Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Wed, 7 Feb 2024 18:09:54 +0000 Subject: [PATCH 1/8] #2258: setting up test that verifies game config parsing --- src/primaite/game/game.py | 41 ++++--- .../configs/basic_switched_network.yaml | 114 ++++++++++++++++++ tests/integration_tests/game_configuration.py | 77 ++++++++++++ 3 files changed, 213 insertions(+), 19 deletions(-) create mode 100644 tests/assets/configs/basic_switched_network.yaml create mode 100644 tests/integration_tests/game_configuration.py diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index 368d899a..e0ad0384 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -31,6 +31,23 @@ from primaite.simulator.system.services.web_server.web_server import WebServer _LOGGER = getLogger(__name__) +APPLICATION_TYPES_MAPPING = { + "WebBrowser": WebBrowser, + "DataManipulationBot": DataManipulationBot, +} + +SERVICE_TYPES_MAPPING = { + "DNSClient": DNSClient, + "DNSServer": DNSServer, + "DatabaseClient": DatabaseClient, + "DatabaseService": DatabaseService, + "WebServer": WebServer, + "FTPClient": FTPClient, + "FTPServer": FTPServer, + "NTPClient": NTPClient, + "NTPServer": NTPServer, +} + class PrimaiteGameOptions(BaseModel): """ @@ -238,20 +255,9 @@ class PrimaiteGame: new_service = None service_ref = service_cfg["ref"] service_type = service_cfg["type"] - service_types_mapping = { - "DNSClient": DNSClient, # key is equal to the 'name' attr of the service class itself. - "DNSServer": DNSServer, - "DatabaseClient": DatabaseClient, - "DatabaseService": DatabaseService, - "WebServer": WebServer, - "FTPClient": FTPClient, - "FTPServer": FTPServer, - "NTPClient": NTPClient, - "NTPServer": NTPServer, - } - if service_type in service_types_mapping: + if service_type in SERVICE_TYPES_MAPPING: _LOGGER.debug(f"installing {service_type} on node {new_node.hostname}") - new_node.software_manager.install(service_types_mapping[service_type]) + new_node.software_manager.install(SERVICE_TYPES_MAPPING[service_type]) new_service = new_node.software_manager.software[service_type] game.ref_map_services[service_ref] = new_service.uuid else: @@ -280,12 +286,9 @@ class PrimaiteGame: new_application = None application_ref = application_cfg["ref"] application_type = application_cfg["type"] - application_types_mapping = { - "WebBrowser": WebBrowser, - "DataManipulationBot": DataManipulationBot, - } - if application_type in application_types_mapping: - new_node.software_manager.install(application_types_mapping[application_type]) + + if application_type in APPLICATION_TYPES_MAPPING: + new_node.software_manager.install(APPLICATION_TYPES_MAPPING[application_type]) new_application = new_node.software_manager.software[application_type] game.ref_map_applications[application_ref] = new_application.uuid else: diff --git a/tests/assets/configs/basic_switched_network.yaml b/tests/assets/configs/basic_switched_network.yaml new file mode 100644 index 00000000..f20fedce --- /dev/null +++ b/tests/assets/configs/basic_switched_network.yaml @@ -0,0 +1,114 @@ +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_checkpoints: true + checkpoint_interval: 5 + 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: client_2_green_user + team: GREEN + type: GreenWebBrowsingAgent + observation_space: + type: UC2GreenObservation + action_space: + action_list: + - type: DONOTHING + - type: NODE_APPLICATION_EXECUTE + options: + nodes: + - node_name: client_2 + applications: + - application_name: WebBrowser + max_folders_per_node: 1 + max_files_per_folder: 1 + max_services_per_node: 1 + max_applications_per_node: 1 + + reward_function: + reward_components: + - type: DUMMY + + agent_settings: + start_settings: + start_step: 5 + frequency: 4 + variance: 3 + +simulation: + network: + nodes: + + - ref: switch_1 + type: switch + hostname: switch_1 + num_ports: 8 + + - ref: client_1 + type: computer + hostname: client_1 + 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: client_1_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 + services: + - ref: client_1_dns_client + type: DNSClient + + - ref: client_2 + type: computer + hostname: client_2 + ip_address: 192.168.10.22 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.10.1 + dns_server: 192.168.1.10 + # pre installed services and applications + + links: + - ref: switch_1___client_1 + endpoint_a_ref: switch_1 + endpoint_a_port: 1 + endpoint_b_ref: client_1 + endpoint_b_port: 1 + - ref: switch_1___client_2 + endpoint_a_ref: switch_1 + endpoint_a_port: 2 + endpoint_b_ref: client_2 + endpoint_b_port: 1 diff --git a/tests/integration_tests/game_configuration.py b/tests/integration_tests/game_configuration.py new file mode 100644 index 00000000..00c94d9e --- /dev/null +++ b/tests/integration_tests/game_configuration.py @@ -0,0 +1,77 @@ +from pathlib import Path +from typing import Union + +import yaml + +from primaite.config.load import example_config_path +from primaite.game.agent.data_manipulation_bot import DataManipulationAgent +from primaite.game.agent.interface import ProxyAgent, RandomAgent +from primaite.game.game import APPLICATION_TYPES_MAPPING, PrimaiteGame, SERVICE_TYPES_MAPPING +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from tests import TEST_ASSETS_ROOT + +BASIC_CONFIG = TEST_ASSETS_ROOT / "configs/basic_switched_network.yaml" + + +def load_config(config_path: Union[str, Path]) -> PrimaiteGame: + """Returns a PrimaiteGame object which loads the contents of a given yaml path.""" + with open(config_path, "r") as f: + cfg = yaml.safe_load(f) + + return PrimaiteGame.from_config(cfg) + + +def test_example_config(): + """Test that the example config can be parsed properly.""" + game = load_config(example_config_path()) + + assert len(game.agents) == 3 # red, blue and green agent + + # green agent + assert game.agents[0].agent_name == "client_2_green_user" + assert isinstance(game.agents[0], RandomAgent) + + # red agent + assert game.agents[1].agent_name == "client_1_data_manipulation_red_bot" + assert isinstance(game.agents[1], DataManipulationAgent) + + # blue agent + assert game.agents[2].agent_name == "defender" + assert isinstance(game.agents[2], ProxyAgent) + + network: Network = game.simulation.network + + assert len(network.nodes) == 10 # 10 nodes in example network + assert len(network.routers) == 1 # 1 router in network + assert len(network.switches) == 2 # 2 switches in network + assert len(network.servers) == 5 # 5 servers in network + + +def test_node_software_install(): + """Test that software can be installed on a node.""" + game = load_config(BASIC_CONFIG) + + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + client_2: Computer = game.simulation.network.get_node_by_hostname("client_2") + + system_software = {DNSClient, FTPClient, WebBrowser} + + # check that system software is installed on client 1 + for software in system_software: + assert client_1.software_manager.software.get(software.__name__) is not None + + # check that system software is installed on client 2 + for software in system_software: + assert client_2.software_manager.software.get(software.__name__) is not None + + # check that applications have been installed on client 1 + for applications in APPLICATION_TYPES_MAPPING: + assert client_1.software_manager.software.get(applications) is not None + + # check that services have been installed on client 1 + # for service in SERVICE_TYPES_MAPPING: + # assert client_1.software_manager.software.get(service) is not None From a4b787860442cfa17e611edd8baac726eaab306d Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Thu, 8 Feb 2024 10:36:07 +0000 Subject: [PATCH 2/8] #2258: added NTPClient to system software + testing all installable software on client1 in config --- .../network/hardware/nodes/computer.py | 4 ++++ .../configs/basic_switched_network.yaml | 22 ++++++++++++++++--- tests/integration_tests/game_configuration.py | 7 +++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/primaite/simulator/network/hardware/nodes/computer.py b/src/primaite/simulator/network/hardware/nodes/computer.py index 0480aca9..9b076647 100644 --- a/src/primaite/simulator/network/hardware/nodes/computer.py +++ b/src/primaite/simulator/network/hardware/nodes/computer.py @@ -2,6 +2,7 @@ from primaite.simulator.network.hardware.base import NIC, Node from primaite.simulator.system.applications.web_browser import WebBrowser from primaite.simulator.system.services.dns.dns_client import DNSClient from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ntp.ntp_client import NTPClient class Computer(Node): @@ -49,6 +50,9 @@ class Computer(Node): # FTP self.software_manager.install(FTPClient) + # NTP + self.software_manager.install(NTPClient) + # Web Browser self.software_manager.install(WebBrowser) diff --git a/tests/assets/configs/basic_switched_network.yaml b/tests/assets/configs/basic_switched_network.yaml index f20fedce..774c4aa2 100644 --- a/tests/assets/configs/basic_switched_network.yaml +++ b/tests/assets/configs/basic_switched_network.yaml @@ -89,9 +89,25 @@ simulation: payload: "DELETE" server_ip: 192.168.1.14 services: - - ref: client_1_dns_client - type: DNSClient - + - ref: client_1_dns_server + type: DNSServer + options: + domain_mapping: + arcd.com: 192.168.1.12 # web server + - ref: client_1_database_client + type: DatabaseClient + options: + db_server_ip: 192.168.10.21 + - ref: client_1_database_service + type: DatabaseService + options: + backup_server_ip: 192.168.10.21 + - ref: client_1_web_service + type: WebServer + - ref: client_1_ftp_server + type: FTPServer + - ref: client_1_ntp_server + type: NTPServer - ref: client_2 type: computer hostname: client_2 diff --git a/tests/integration_tests/game_configuration.py b/tests/integration_tests/game_configuration.py index 00c94d9e..ff977082 100644 --- a/tests/integration_tests/game_configuration.py +++ b/tests/integration_tests/game_configuration.py @@ -12,6 +12,7 @@ from primaite.simulator.network.hardware.nodes.computer import Computer from primaite.simulator.system.applications.web_browser import WebBrowser from primaite.simulator.system.services.dns.dns_client import DNSClient from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ntp.ntp_client import NTPClient from tests import TEST_ASSETS_ROOT BASIC_CONFIG = TEST_ASSETS_ROOT / "configs/basic_switched_network.yaml" @@ -58,7 +59,7 @@ def test_node_software_install(): client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") client_2: Computer = game.simulation.network.get_node_by_hostname("client_2") - system_software = {DNSClient, FTPClient, WebBrowser} + system_software = {DNSClient, FTPClient, NTPClient, WebBrowser} # check that system software is installed on client 1 for software in system_software: @@ -73,5 +74,5 @@ def test_node_software_install(): assert client_1.software_manager.software.get(applications) is not None # check that services have been installed on client 1 - # for service in SERVICE_TYPES_MAPPING: - # assert client_1.software_manager.software.get(service) is not None + for service in SERVICE_TYPES_MAPPING: + assert client_1.software_manager.software.get(service) is not None From 1dcb9214afe0dabe9d4a7a5c173b18da87a3f670 Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Thu, 8 Feb 2024 12:04:49 +0000 Subject: [PATCH 3/8] #2258: Added DoSBot to list of applications --- src/primaite/game/game.py | 11 +++++++---- tests/assets/configs/basic_switched_network.yaml | 8 ++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index e0ad0384..b03828f1 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -19,6 +19,7 @@ from primaite.simulator.network.hardware.nodes.switch import Switch from primaite.simulator.sim_container import Simulation 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.applications.red_applications.dos_bot import DoSBot from primaite.simulator.system.applications.web_browser import WebBrowser from primaite.simulator.system.services.database.database_service import DatabaseService from primaite.simulator.system.services.dns.dns_client import DNSClient @@ -31,10 +32,7 @@ from primaite.simulator.system.services.web_server.web_server import WebServer _LOGGER = getLogger(__name__) -APPLICATION_TYPES_MAPPING = { - "WebBrowser": WebBrowser, - "DataManipulationBot": DataManipulationBot, -} +APPLICATION_TYPES_MAPPING = {"WebBrowser": WebBrowser, "DataManipulationBot": DataManipulationBot, "DoSBot": DoSBot} SERVICE_TYPES_MAPPING = { "DNSClient": DNSClient, @@ -308,6 +306,11 @@ class PrimaiteGame: if "options" in application_cfg: opt = application_cfg["options"] new_application.target_url = opt.get("target_url") + + elif application_type == "DoSBot": + if "options" in application_cfg: + opt = application_cfg["options"] + new_application.target_ip_address = opt.get("target_ip_address") if "nics" in node_cfg: for nic_num, nic_cfg in node_cfg["nics"].items(): new_node.connect_nic(NIC(ip_address=nic_cfg["ip_address"], subnet_mask=nic_cfg["subnet_mask"])) diff --git a/tests/assets/configs/basic_switched_network.yaml b/tests/assets/configs/basic_switched_network.yaml index 774c4aa2..0687478d 100644 --- a/tests/assets/configs/basic_switched_network.yaml +++ b/tests/assets/configs/basic_switched_network.yaml @@ -88,6 +88,10 @@ simulation: data_manipulation_p_of_success: 0.8 payload: "DELETE" server_ip: 192.168.1.14 + - ref: dos_bot + type: DoSBot + options: + target_ip_address: 192.168.10.21 services: - ref: client_1_dns_server type: DNSServer @@ -98,6 +102,10 @@ simulation: type: DatabaseClient options: db_server_ip: 192.168.10.21 + - ref: client_1_dosbot + type: DoSBot + options: + db_server_ip: 192.168.10.21 - ref: client_1_database_service type: DatabaseService options: From b31a9943d7bfe214391e83931fd252c1dab80f99 Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Thu, 8 Feb 2024 16:02:37 +0000 Subject: [PATCH 4/8] #2258: testing individual application install --- src/primaite/game/game.py | 11 ++++- .../configs/basic_switched_network.yaml | 4 +- tests/integration_tests/game_configuration.py | 41 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index b03828f1..e16f4991 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -16,6 +16,7 @@ from primaite.simulator.network.hardware.nodes.computer import Computer from primaite.simulator.network.hardware.nodes.router import Router from primaite.simulator.network.hardware.nodes.server import Server from primaite.simulator.network.hardware.nodes.switch import Switch +from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.sim_container import Simulation from primaite.simulator.system.applications.database_client import DatabaseClient from primaite.simulator.system.applications.red_applications.data_manipulation_bot import DataManipulationBot @@ -310,7 +311,15 @@ class PrimaiteGame: elif application_type == "DoSBot": if "options" in application_cfg: opt = application_cfg["options"] - new_application.target_ip_address = opt.get("target_ip_address") + new_application.configure( + target_ip_address=IPv4Address(opt.get("target_ip_address")), + target_port=Port(opt.get("target_port", Port.POSTGRES_SERVER.value)), + payload=opt.get("payload"), + repeat=bool(opt.get("repeat")), + port_scan_p_of_success=float(opt.get("port_scan_p_of_success", "0.1")), + dos_intensity=float(opt.get("dos_intensity", "1.0")), + max_sessions=int(opt.get("max_sessions", "1000")), + ) if "nics" in node_cfg: for nic_num, nic_cfg in node_cfg["nics"].items(): new_node.connect_nic(NIC(ip_address=nic_cfg["ip_address"], subnet_mask=nic_cfg["subnet_mask"])) diff --git a/tests/assets/configs/basic_switched_network.yaml b/tests/assets/configs/basic_switched_network.yaml index 0687478d..d86af779 100644 --- a/tests/assets/configs/basic_switched_network.yaml +++ b/tests/assets/configs/basic_switched_network.yaml @@ -87,11 +87,13 @@ simulation: port_scan_p_of_success: 0.8 data_manipulation_p_of_success: 0.8 payload: "DELETE" - server_ip: 192.168.1.14 + server_ip: 192.168.1.21 - ref: dos_bot type: DoSBot options: target_ip_address: 192.168.10.21 + payload: SPOOF DATA + port_scan_p_of_success: 0.8 services: - ref: client_1_dns_server type: DNSServer diff --git a/tests/integration_tests/game_configuration.py b/tests/integration_tests/game_configuration.py index ff977082..274e8bd6 100644 --- a/tests/integration_tests/game_configuration.py +++ b/tests/integration_tests/game_configuration.py @@ -1,3 +1,4 @@ +from ipaddress import IPv4Address from pathlib import Path from typing import Union @@ -9,6 +10,8 @@ from primaite.game.agent.interface import ProxyAgent, RandomAgent from primaite.game.game import APPLICATION_TYPES_MAPPING, PrimaiteGame, SERVICE_TYPES_MAPPING from primaite.simulator.network.container import Network from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.system.applications.red_applications.data_manipulation_bot import DataManipulationBot +from primaite.simulator.system.applications.red_applications.dos_bot import DoSBot from primaite.simulator.system.applications.web_browser import WebBrowser from primaite.simulator.system.services.dns.dns_client import DNSClient from primaite.simulator.system.services.ftp.ftp_client import FTPClient @@ -76,3 +79,41 @@ def test_node_software_install(): # check that services have been installed on client 1 for service in SERVICE_TYPES_MAPPING: assert client_1.software_manager.software.get(service) is not None + + +def test_web_browser_install(): + """Test that the web browser can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + web_browser: WebBrowser = client_1.software_manager.software.get("WebBrowser") + + assert web_browser.target_url == "http://arcd.com/users/" + + +def test_data_manipulation_bot_install(): + """Test that the data manipulation bot can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + data_manipulation_bot: DataManipulationBot = client_1.software_manager.software.get("DataManipulationBot") + + assert data_manipulation_bot.server_ip_address == IPv4Address("192.168.1.21") + assert data_manipulation_bot.payload == "DELETE" + assert data_manipulation_bot.data_manipulation_p_of_success == 0.8 + assert data_manipulation_bot.port_scan_p_of_success == 0.8 + + +def test_dos_bot_install(): + """Test that the denial of service bot can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + dos_bot: DoSBot = client_1.software_manager.software.get("DoSBot") + + assert dos_bot.target_ip_address == IPv4Address("192.168.10.21") + assert dos_bot.payload == "SPOOF DATA" + assert dos_bot.port_scan_p_of_success == 0.8 + assert dos_bot.dos_intensity == 1.0 # default + assert dos_bot.max_sessions == 1000 # default + assert dos_bot.repeat is False # default From 0590f956e3443d916bb9273067b013c36130feeb Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Thu, 8 Feb 2024 16:21:08 +0000 Subject: [PATCH 5/8] #2258: ntp client should not request if ntp server is not set --- src/primaite/simulator/system/services/ntp/ntp_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/primaite/simulator/system/services/ntp/ntp_client.py b/src/primaite/simulator/system/services/ntp/ntp_client.py index e8c3d0cb..ccb2cbe7 100644 --- a/src/primaite/simulator/system/services/ntp/ntp_client.py +++ b/src/primaite/simulator/system/services/ntp/ntp_client.py @@ -127,6 +127,7 @@ class NTPClient(Service): super().apply_timestep(timestep) if self.operating_state == ServiceOperatingState.RUNNING: # request time from server - self.request_time() + if self.ntp_server is not None: + self.request_time() else: self.sys_log.debug(f"{self.name} ntp client not running") From d1c3f891bf0ef19fcbccb5e22cb1b4c71aa47514 Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Fri, 9 Feb 2024 11:41:06 +0000 Subject: [PATCH 6/8] #2258: moving applications to application types - more tests --- src/primaite/game/game.py | 26 +++-- .../configs/basic_switched_network.yaml | 22 +++-- tests/integration_tests/game_configuration.py | 99 +++++++++++++++++-- 3 files changed, 123 insertions(+), 24 deletions(-) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index b2b35f26..431db5fb 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -33,12 +33,16 @@ from primaite.simulator.system.services.web_server.web_server import WebServer _LOGGER = getLogger(__name__) -APPLICATION_TYPES_MAPPING = {"WebBrowser": WebBrowser, "DataManipulationBot": DataManipulationBot, "DoSBot": DoSBot} +APPLICATION_TYPES_MAPPING = { + "WebBrowser": WebBrowser, + "DatabaseClient": DatabaseClient, + "DataManipulationBot": DataManipulationBot, + "DoSBot": DoSBot, +} SERVICE_TYPES_MAPPING = { "DNSClient": DNSClient, "DNSServer": DNSServer, - "DatabaseClient": DatabaseClient, "DatabaseService": DatabaseService, "WebServer": WebServer, "FTPClient": FTPClient, @@ -262,22 +266,21 @@ class PrimaiteGame: else: _LOGGER.warning(f"service type not found {service_type}") # service-dependent options - if service_type == "DatabaseClient": + if service_type == "DNSClient": if "options" in service_cfg: opt = service_cfg["options"] - if "db_server_ip" in opt: - new_service.configure(server_ip_address=IPv4Address(opt["db_server_ip"])) + if "dns_server" in opt: + new_service.dns_server = IPv4Address(opt["dns_server"]) if service_type == "DNSServer": if "options" in service_cfg: opt = service_cfg["options"] if "domain_mapping" in opt: for domain, ip in opt["domain_mapping"].items(): - new_service.dns_register(domain, ip) + new_service.dns_register(domain, IPv4Address(ip)) if service_type == "DatabaseService": if "options" in service_cfg: opt = service_cfg["options"] - if "backup_server_ip" in opt: - new_service.configure_backup(backup_server=IPv4Address(opt["backup_server_ip"])) + new_service.configure_backup(backup_server=IPv4Address(opt.get("backup_server_ip"))) new_service.start() if "applications" in node_cfg: @@ -303,6 +306,13 @@ class PrimaiteGame: port_scan_p_of_success=float(opt.get("port_scan_p_of_success", "0.1")), data_manipulation_p_of_success=float(opt.get("data_manipulation_p_of_success", "0.1")), ) + elif application_type == "DatabaseClient": + if "options" in application_cfg: + opt = application_cfg["options"] + new_application.configure( + server_ip_address=IPv4Address(opt.get("db_server_ip")), + server_password=opt.get("server_password"), + ) elif application_type == "WebBrowser": if "options" in application_cfg: opt = application_cfg["options"] diff --git a/tests/assets/configs/basic_switched_network.yaml b/tests/assets/configs/basic_switched_network.yaml index d86af779..0050a0cb 100644 --- a/tests/assets/configs/basic_switched_network.yaml +++ b/tests/assets/configs/basic_switched_network.yaml @@ -81,6 +81,10 @@ simulation: type: WebBrowser options: target_url: http://arcd.com/users/ + - ref: client_1_database_client + type: DatabaseClient + options: + db_server_ip: 192.168.1.10 - ref: data_manipulation_bot type: DataManipulationBot options: @@ -95,27 +99,25 @@ simulation: payload: SPOOF DATA port_scan_p_of_success: 0.8 services: + - ref: client_1_dns_client + type: DNSClient + options: + dns_server: 192.168.1.10 - ref: client_1_dns_server type: DNSServer options: domain_mapping: - arcd.com: 192.168.1.12 # web server - - ref: client_1_database_client - type: DatabaseClient - options: - db_server_ip: 192.168.10.21 - - ref: client_1_dosbot - type: DoSBot - options: - db_server_ip: 192.168.10.21 + arcd.com: 192.168.1.10 - ref: client_1_database_service type: DatabaseService options: - backup_server_ip: 192.168.10.21 + backup_server_ip: 192.168.1.10 - ref: client_1_web_service type: WebServer - ref: client_1_ftp_server type: FTPServer + - ref: client_1_ntp_client + type: NTPClient - ref: client_1_ntp_server type: NTPServer - ref: client_2 diff --git a/tests/integration_tests/game_configuration.py b/tests/integration_tests/game_configuration.py index 274e8bd6..9db894c5 100644 --- a/tests/integration_tests/game_configuration.py +++ b/tests/integration_tests/game_configuration.py @@ -10,12 +10,18 @@ from primaite.game.agent.interface import ProxyAgent, RandomAgent from primaite.game.game import APPLICATION_TYPES_MAPPING, PrimaiteGame, SERVICE_TYPES_MAPPING from primaite.simulator.network.container import Network from primaite.simulator.network.hardware.nodes.computer import Computer +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.applications.red_applications.dos_bot import DoSBot from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.database.database_service import DatabaseService from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.dns.dns_server import DNSServer from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ftp.ftp_server import FTPServer from primaite.simulator.system.services.ntp.ntp_client import NTPClient +from primaite.simulator.system.services.ntp.ntp_server import NTPServer +from primaite.simulator.system.services.web_server.web_server import WebServer from tests import TEST_ASSETS_ROOT BASIC_CONFIG = TEST_ASSETS_ROOT / "configs/basic_switched_network.yaml" @@ -33,19 +39,23 @@ def test_example_config(): """Test that the example config can be parsed properly.""" game = load_config(example_config_path()) - assert len(game.agents) == 3 # red, blue and green agent + assert len(game.agents) == 4 # red, blue and 2 green agents - # green agent + # green agent 1 assert game.agents[0].agent_name == "client_2_green_user" assert isinstance(game.agents[0], RandomAgent) + # green agent 2 + assert game.agents[1].agent_name == "client_1_green_user" + assert isinstance(game.agents[1], RandomAgent) + # red agent - assert game.agents[1].agent_name == "client_1_data_manipulation_red_bot" - assert isinstance(game.agents[1], DataManipulationAgent) + assert game.agents[2].agent_name == "client_1_data_manipulation_red_bot" + assert isinstance(game.agents[2], DataManipulationAgent) # blue agent - assert game.agents[2].agent_name == "defender" - assert isinstance(game.agents[2], ProxyAgent) + assert game.agents[3].agent_name == "defender" + assert isinstance(game.agents[3], ProxyAgent) network: Network = game.simulation.network @@ -91,6 +101,16 @@ def test_web_browser_install(): assert web_browser.target_url == "http://arcd.com/users/" +def test_database_client_install(): + """Test that the Database Client service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + database_client: DatabaseClient = client_1.software_manager.software.get("DatabaseClient") + + assert database_client.server_ip_address == IPv4Address("192.168.1.10") + + def test_data_manipulation_bot_install(): """Test that the data manipulation bot can be configured via config.""" game = load_config(BASIC_CONFIG) @@ -117,3 +137,70 @@ def test_dos_bot_install(): assert dos_bot.dos_intensity == 1.0 # default assert dos_bot.max_sessions == 1000 # default assert dos_bot.repeat is False # default + + +def test_dns_client_install(): + """Test that the DNS Client service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + dns_client: DNSClient = client_1.software_manager.software.get("DNSClient") + + assert dns_client.dns_server == IPv4Address("192.168.1.10") + + +def test_database_service_install(): + """Test that the Database Service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + database_service: DatabaseService = client_1.software_manager.software.get("DatabaseService") + + assert database_service.backup_server_ip == IPv4Address("192.168.1.10") + + +def test_web_server_install(): + """Test that the Web Server Service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + web_server_service: WebServer = client_1.software_manager.software.get("WebServer") + + # config should have also installed database client - web server service should be able to retrieve this + assert web_server_service.software_manager.software.get("DatabaseClient") is not None + + +def test_ftp_client_install(): + """Test that the FTP Client Service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + ftp_client_service: FTPClient = client_1.software_manager.software.get("FTPClient") + assert ftp_client_service is not None + + +def test_ftp_server_install(): + """Test that the FTP Server Service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + ftp_server_service: FTPServer = client_1.software_manager.software.get("FTPServer") + assert ftp_server_service is not None + + +def test_ntp_client_install(): + """Test that the NTP Client Service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + ntp_client_service: NTPClient = client_1.software_manager.software.get("NTPClient") + assert ntp_client_service is not None + + +def test_ntp_server_install(): + """Test that the NTP Server Service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + ntp_server_service: NTPServer = client_1.software_manager.software.get("NTPServer") + assert ntp_server_service is not None From da92d742366c574074e14070ae313897325e8888 Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Mon, 12 Feb 2024 09:01:30 +0000 Subject: [PATCH 7/8] #2258: remove unnecessary ntp server check --- src/primaite/simulator/system/services/ntp/ntp_client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/primaite/simulator/system/services/ntp/ntp_client.py b/src/primaite/simulator/system/services/ntp/ntp_client.py index c9935a16..ddd794ae 100644 --- a/src/primaite/simulator/system/services/ntp/ntp_client.py +++ b/src/primaite/simulator/system/services/ntp/ntp_client.py @@ -132,7 +132,6 @@ class NTPClient(Service): super().apply_timestep(timestep) if self.operating_state == ServiceOperatingState.RUNNING: # request time from server - if self.ntp_server is not None: - self.request_time() + self.request_time() else: self.sys_log.debug(f"{self.name} ntp client not running") From 7beacfd95fb6c48f8f3581bd58be1addd42a1422 Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Mon, 12 Feb 2024 11:41:55 +0000 Subject: [PATCH 8/8] #2258: missing some configuration items + added more tests --- src/primaite/game/game.py | 11 ++++++++++- tests/assets/configs/basic_switched_network.yaml | 6 ++++++ tests/integration_tests/game_configuration.py | 16 +++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index 6ccd2f59..c03bca36 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -283,7 +283,16 @@ class PrimaiteGame: opt = service_cfg["options"] new_service.configure_backup(backup_server=IPv4Address(opt.get("backup_server_ip"))) new_service.start() - + if service_type == "FTPServer": + if "options" in service_cfg: + opt = service_cfg["options"] + new_service.server_password = opt.get("server_password") + new_service.start() + if service_type == "NTPClient": + if "options" in service_cfg: + opt = service_cfg["options"] + new_service.ntp_server = IPv4Address(opt.get("ntp_server_ip")) + new_service.start() if "applications" in node_cfg: for application_cfg in node_cfg["applications"]: new_application = None diff --git a/tests/assets/configs/basic_switched_network.yaml b/tests/assets/configs/basic_switched_network.yaml index 0050a0cb..d1cec079 100644 --- a/tests/assets/configs/basic_switched_network.yaml +++ b/tests/assets/configs/basic_switched_network.yaml @@ -85,6 +85,7 @@ simulation: type: DatabaseClient options: db_server_ip: 192.168.1.10 + server_password: arcd - ref: data_manipulation_bot type: DataManipulationBot options: @@ -92,6 +93,7 @@ simulation: data_manipulation_p_of_success: 0.8 payload: "DELETE" server_ip: 192.168.1.21 + server_password: arcd - ref: dos_bot type: DoSBot options: @@ -116,8 +118,12 @@ simulation: type: WebServer - ref: client_1_ftp_server type: FTPServer + options: + server_password: arcd - ref: client_1_ntp_client type: NTPClient + options: + ntp_server_ip: 192.168.1.10 - ref: client_1_ntp_server type: NTPServer - ref: client_2 diff --git a/tests/integration_tests/game_configuration.py b/tests/integration_tests/game_configuration.py index 9db894c5..3bd870e3 100644 --- a/tests/integration_tests/game_configuration.py +++ b/tests/integration_tests/game_configuration.py @@ -9,7 +9,7 @@ from primaite.game.agent.data_manipulation_bot import DataManipulationAgent from primaite.game.agent.interface import ProxyAgent, RandomAgent from primaite.game.game import APPLICATION_TYPES_MAPPING, PrimaiteGame, SERVICE_TYPES_MAPPING from primaite.simulator.network.container import Network -from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.host.computer import Computer 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.applications.red_applications.dos_bot import DoSBot @@ -109,6 +109,7 @@ def test_database_client_install(): database_client: DatabaseClient = client_1.software_manager.software.get("DatabaseClient") assert database_client.server_ip_address == IPv4Address("192.168.1.10") + assert database_client.server_password == "arcd" def test_data_manipulation_bot_install(): @@ -122,6 +123,7 @@ def test_data_manipulation_bot_install(): assert data_manipulation_bot.payload == "DELETE" assert data_manipulation_bot.data_manipulation_p_of_success == 0.8 assert data_manipulation_bot.port_scan_p_of_success == 0.8 + assert data_manipulation_bot.server_password == "arcd" def test_dos_bot_install(): @@ -149,6 +151,16 @@ def test_dns_client_install(): assert dns_client.dns_server == IPv4Address("192.168.1.10") +def test_dns_server_install(): + """Test that the DNS Client service can be configured via config.""" + game = load_config(BASIC_CONFIG) + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + + dns_server: DNSServer = client_1.software_manager.software.get("DNSServer") + + assert dns_server.dns_lookup("arcd.com") == IPv4Address("192.168.1.10") + + def test_database_service_install(): """Test that the Database Service can be configured via config.""" game = load_config(BASIC_CONFIG) @@ -186,6 +198,7 @@ def test_ftp_server_install(): ftp_server_service: FTPServer = client_1.software_manager.software.get("FTPServer") assert ftp_server_service is not None + assert ftp_server_service.server_password == "arcd" def test_ntp_client_install(): @@ -195,6 +208,7 @@ def test_ntp_client_install(): ntp_client_service: NTPClient = client_1.software_manager.software.get("NTPClient") assert ntp_client_service is not None + assert ntp_client_service.ntp_server == IPv4Address("192.168.1.10") def test_ntp_server_install():