diff --git a/src/primaite/VERSION b/src/primaite/VERSION index dcc86c22..0fd919fd 100644 --- a/src/primaite/VERSION +++ b/src/primaite/VERSION @@ -1 +1 @@ -3.0.0b2dev +3.0.0b4dev diff --git a/src/primaite/config/_package_data/example_config.yaml b/src/primaite/config/_package_data/example_config.yaml index 83a6de73..3c4ac62b 100644 --- a/src/primaite/config/_package_data/example_config.yaml +++ b/src/primaite/config/_package_data/example_config.yaml @@ -167,6 +167,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE + - type: NODE_SERVICE_PATCH - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -197,111 +198,110 @@ agents: 1: action: NODE_SERVICE_SCAN options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 # stop webapp service 2: action: NODE_SERVICE_STOP options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 # start webapp service 3: action: "NODE_SERVICE_START" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 4: action: "NODE_SERVICE_PAUSE" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 5: action: "NODE_SERVICE_RESUME" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 6: action: "NODE_SERVICE_RESTART" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 7: action: "NODE_SERVICE_DISABLE" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 8: action: "NODE_SERVICE_ENABLE" options: - node_id: 2 - service_id: 1 - 9: + node_id: 1 + service_id: 0 + 9: # check database.db file action: "NODE_FILE_SCAN" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 10: action: "NODE_FILE_CHECKHASH" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 11: action: "NODE_FILE_DELETE" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 12: action: "NODE_FILE_REPAIR" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 13: - action: "NODE_FILE_RESTORE" + action: "NODE_SERVICE_PATCH" options: - node_id: 3 - folder_id: 1 - file_id: 1 + node_id: 2 + service_id: 0 14: action: "NODE_FOLDER_SCAN" options: - node_id: 3 + node_id: 2 folder_id: 1 15: action: "NODE_FOLDER_CHECKHASH" options: - node_id: 3 + node_id: 2 folder_id: 1 16: action: "NODE_FOLDER_REPAIR" options: - node_id: 3 + node_id: 2 folder_id: 1 17: action: "NODE_FOLDER_RESTORE" options: - node_id: 3 + node_id: 2 folder_id: 1 18: action: "NODE_OS_SCAN" options: - node_id: 3 - 19: + node_id: 2 + 19: # shutdown client 1 action: "NODE_SHUTDOWN" options: - node_id: 6 + node_id: 5 20: action: "NODE_STARTUP" options: - node_id: 6 + node_id: 5 21: action: "NODE_RESET" options: - node_id: 6 + node_id: 5 22: action: "NETWORK_ACL_ADDRULE" options: @@ -486,9 +486,6 @@ agents: options: nodes: - # - node_name: router_1 - # - node_name: switch_1 - # - node_name: switch_2 - node_name: domain_controller - node_name: web_server applications: @@ -504,6 +501,7 @@ agents: - 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 diff --git a/src/primaite/config/_package_data/example_config_2_rl_agents.yaml b/src/primaite/config/_package_data/example_config_2_rl_agents.yaml index 9c2acaae..c1e2ea81 100644 --- a/src/primaite/config/_package_data/example_config_2_rl_agents.yaml +++ b/src/primaite/config/_package_data/example_config_2_rl_agents.yaml @@ -165,6 +165,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE + - type: NODE_SERVICE_PATCH - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -195,111 +196,110 @@ agents: 1: action: NODE_SERVICE_SCAN options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 # stop webapp service 2: action: NODE_SERVICE_STOP options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 # start webapp service 3: action: "NODE_SERVICE_START" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 4: action: "NODE_SERVICE_PAUSE" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 5: action: "NODE_SERVICE_RESUME" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 6: action: "NODE_SERVICE_RESTART" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 7: action: "NODE_SERVICE_DISABLE" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 8: action: "NODE_SERVICE_ENABLE" options: - node_id: 2 - service_id: 1 - 9: + node_id: 1 + service_id: 0 + 9: # check database.db file action: "NODE_FILE_SCAN" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 10: action: "NODE_FILE_CHECKHASH" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 11: action: "NODE_FILE_DELETE" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 12: action: "NODE_FILE_REPAIR" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 13: - action: "NODE_FILE_RESTORE" + action: "NODE_SERVICE_PATCH" options: - node_id: 3 - folder_id: 1 - file_id: 1 + node_id: 2 + service_id: 0 14: action: "NODE_FOLDER_SCAN" options: - node_id: 3 + node_id: 2 folder_id: 1 15: action: "NODE_FOLDER_CHECKHASH" options: - node_id: 3 + node_id: 2 folder_id: 1 16: action: "NODE_FOLDER_REPAIR" options: - node_id: 3 + node_id: 2 folder_id: 1 17: action: "NODE_FOLDER_RESTORE" options: - node_id: 3 + node_id: 2 folder_id: 1 18: action: "NODE_OS_SCAN" options: - node_id: 3 - 19: + node_id: 2 + 19: # shutdown client 1 action: "NODE_SHUTDOWN" options: - node_id: 6 + node_id: 5 20: action: "NODE_STARTUP" options: - node_id: 6 + node_id: 5 21: action: "NODE_RESET" options: - node_id: 6 + node_id: 5 22: action: "NETWORK_ACL_ADDRULE" options: @@ -403,93 +403,94 @@ agents: 38: action: "NETWORK_NIC_DISABLE" options: - node_id: 1 + node_id: 0 nic_id: 1 39: action: "NETWORK_NIC_ENABLE" options: - node_id: 1 + node_id: 0 nic_id: 1 40: action: "NETWORK_NIC_DISABLE" options: - node_id: 2 + node_id: 1 nic_id: 1 41: action: "NETWORK_NIC_ENABLE" options: - node_id: 2 + node_id: 1 nic_id: 1 42: action: "NETWORK_NIC_DISABLE" options: - node_id: 3 + node_id: 2 nic_id: 1 43: action: "NETWORK_NIC_ENABLE" options: - node_id: 3 + node_id: 2 nic_id: 1 44: action: "NETWORK_NIC_DISABLE" options: - node_id: 4 + node_id: 3 nic_id: 1 45: action: "NETWORK_NIC_ENABLE" options: - node_id: 4 + node_id: 3 nic_id: 1 46: action: "NETWORK_NIC_DISABLE" options: - node_id: 5 + node_id: 4 nic_id: 1 47: action: "NETWORK_NIC_ENABLE" options: - node_id: 5 + node_id: 4 nic_id: 1 48: action: "NETWORK_NIC_DISABLE" options: - node_id: 5 + node_id: 4 nic_id: 2 49: action: "NETWORK_NIC_ENABLE" options: - node_id: 5 + node_id: 4 nic_id: 2 50: action: "NETWORK_NIC_DISABLE" options: - node_id: 6 + node_id: 5 nic_id: 1 51: action: "NETWORK_NIC_ENABLE" options: - node_id: 6 + node_id: 5 nic_id: 1 52: action: "NETWORK_NIC_DISABLE" options: - node_id: 7 + node_id: 6 nic_id: 1 53: action: "NETWORK_NIC_ENABLE" options: - node_id: 7 + node_id: 6 nic_id: 1 options: nodes: - - node_ref: router_1 - - node_ref: switch_1 - - node_ref: switch_2 - node_ref: domain_controller - node_ref: web_server + services: + - service_ref: web_server_web_service - node_ref: database_server + services: + - service_ref: database_service - node_ref: backup_server - node_ref: security_suite - node_ref: client_1 @@ -597,6 +598,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE + - type: NODE_SERVICE_PATCH - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -627,111 +629,110 @@ agents: 1: action: NODE_SERVICE_SCAN options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 # stop webapp service 2: action: NODE_SERVICE_STOP options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 # start webapp service 3: action: "NODE_SERVICE_START" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 4: action: "NODE_SERVICE_PAUSE" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 5: action: "NODE_SERVICE_RESUME" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 6: action: "NODE_SERVICE_RESTART" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 7: action: "NODE_SERVICE_DISABLE" options: - node_id: 2 - service_id: 1 + node_id: 1 + service_id: 0 8: action: "NODE_SERVICE_ENABLE" options: - node_id: 2 - service_id: 1 - 9: + node_id: 1 + service_id: 0 + 9: # check database.db file action: "NODE_FILE_SCAN" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 10: action: "NODE_FILE_CHECKHASH" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 11: action: "NODE_FILE_DELETE" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 12: action: "NODE_FILE_REPAIR" options: - node_id: 3 + node_id: 2 folder_id: 1 - file_id: 1 + file_id: 0 13: - action: "NODE_FILE_RESTORE" + action: "NODE_SERVICE_PATCH" options: - node_id: 3 - folder_id: 1 - file_id: 1 + node_id: 2 + service_id: 0 14: action: "NODE_FOLDER_SCAN" options: - node_id: 3 + node_id: 2 folder_id: 1 15: action: "NODE_FOLDER_CHECKHASH" options: - node_id: 3 + node_id: 2 folder_id: 1 16: action: "NODE_FOLDER_REPAIR" options: - node_id: 3 + node_id: 2 folder_id: 1 17: action: "NODE_FOLDER_RESTORE" options: - node_id: 3 + node_id: 2 folder_id: 1 18: action: "NODE_OS_SCAN" options: - node_id: 3 - 19: + node_id: 2 + 19: # shutdown client 1 action: "NODE_SHUTDOWN" options: - node_id: 6 + node_id: 5 20: action: "NODE_STARTUP" options: - node_id: 6 + node_id: 5 21: action: "NODE_RESET" options: - node_id: 6 + node_id: 5 22: action: "NETWORK_ACL_ADDRULE" options: @@ -835,93 +836,94 @@ agents: 38: action: "NETWORK_NIC_DISABLE" options: - node_id: 1 + node_id: 0 nic_id: 1 39: action: "NETWORK_NIC_ENABLE" options: - node_id: 1 + node_id: 0 nic_id: 1 40: action: "NETWORK_NIC_DISABLE" options: - node_id: 2 + node_id: 1 nic_id: 1 41: action: "NETWORK_NIC_ENABLE" options: - node_id: 2 + node_id: 1 nic_id: 1 42: action: "NETWORK_NIC_DISABLE" options: - node_id: 3 + node_id: 2 nic_id: 1 43: action: "NETWORK_NIC_ENABLE" options: - node_id: 3 + node_id: 2 nic_id: 1 44: action: "NETWORK_NIC_DISABLE" options: - node_id: 4 + node_id: 3 nic_id: 1 45: action: "NETWORK_NIC_ENABLE" options: - node_id: 4 + node_id: 3 nic_id: 1 46: action: "NETWORK_NIC_DISABLE" options: - node_id: 5 + node_id: 4 nic_id: 1 47: action: "NETWORK_NIC_ENABLE" options: - node_id: 5 + node_id: 4 nic_id: 1 48: action: "NETWORK_NIC_DISABLE" options: - node_id: 5 + node_id: 4 nic_id: 2 49: action: "NETWORK_NIC_ENABLE" options: - node_id: 5 + node_id: 4 nic_id: 2 50: action: "NETWORK_NIC_DISABLE" options: - node_id: 6 + node_id: 5 nic_id: 1 51: action: "NETWORK_NIC_ENABLE" options: - node_id: 6 + node_id: 5 nic_id: 1 52: action: "NETWORK_NIC_DISABLE" options: - node_id: 7 + node_id: 6 nic_id: 1 53: action: "NETWORK_NIC_ENABLE" options: - node_id: 7 + node_id: 6 nic_id: 1 options: nodes: - - node_ref: router_1 - - node_ref: switch_1 - - node_ref: switch_2 - node_ref: domain_controller - node_ref: web_server + services: + - service_ref: web_server_web_service - node_ref: database_server + services: + - service_ref: database_service - node_ref: backup_server - node_ref: security_suite - node_ref: client_1 diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index f6ba71d5..47cd2394 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -156,6 +156,14 @@ class NodeServiceEnableAction(NodeServiceAbstractAction): self.verb: str = "enable" +class NodeServicePatchAction(NodeServiceAbstractAction): + """Action which patches a service.""" + + def __init__(self, manager: "ActionManager", num_nodes: int, num_services: int, **kwargs) -> None: + super().__init__(manager=manager, num_nodes=num_nodes, num_services=num_services) + self.verb: str = "patch" + + class NodeApplicationAbstractAction(AbstractAction): """ Base class for application actions. @@ -559,6 +567,7 @@ class ActionManager: "NODE_SERVICE_RESTART": NodeServiceRestartAction, "NODE_SERVICE_DISABLE": NodeServiceDisableAction, "NODE_SERVICE_ENABLE": NodeServiceEnableAction, + "NODE_SERVICE_PATCH": NodeServicePatchAction, "NODE_APPLICATION_EXECUTE": NodeApplicationExecuteAction, "NODE_FILE_SCAN": NodeFileScanAction, "NODE_FILE_CHECKHASH": NodeFileCheckhashAction, diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index fd18e154..d4e72f63 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -102,6 +102,11 @@ class Folder(FileSystemItemABC): name="delete", request_type=RequestType(func=lambda request, context: self.remove_file_by_id(file_uuid=request[0])), ) + self._file_request_manager = RequestManager() + rm.add_request( + name="file", + request_type=RequestType(func=lambda request, context: self._file_request_manager), + ) return rm def describe_state(self) -> Dict: @@ -254,6 +259,7 @@ class Folder(FileSystemItemABC): # add to list self.files[file.uuid] = file self._files_by_name[file.name] = file + self._file_request_manager.add_request(file.uuid, RequestType(func=file._request_manager)) file.folder = self def remove_file(self, file: Optional[File]): @@ -273,6 +279,7 @@ class Folder(FileSystemItemABC): self.deleted_files[file.uuid] = file file.delete() self.sys_log.info(f"Removed file {file.name} (id: {file.uuid})") + self._file_request_manager.remove_request(file.uuid) else: _LOGGER.debug(f"File with UUID {file.uuid} was not found.") diff --git a/src/primaite/simulator/sim_container.py b/src/primaite/simulator/sim_container.py index c529ed04..896861e6 100644 --- a/src/primaite/simulator/sim_container.py +++ b/src/primaite/simulator/sim_container.py @@ -55,3 +55,8 @@ class Simulation(SimComponent): } ) return state + + def apply_timestep(self, timestep: int) -> None: + """Apply a timestep to the simulation.""" + super().apply_timestep(timestep) + self.network.apply_timestep(timestep) diff --git a/src/primaite/simulator/system/software.py b/src/primaite/simulator/system/software.py index c3db48fc..27d5b3b3 100644 --- a/src/primaite/simulator/system/software.py +++ b/src/primaite/simulator/system/software.py @@ -91,6 +91,10 @@ class Software(SimComponent): "The FileSystem of the Node the Software is installed on." folder: Optional[Folder] = None "The folder on the file system the Software uses." + patching_duration: int = 2 + "The number of ticks it takes to patch the software." + _patching_countdown: Optional[int] = None + "Current number of ticks left to patch the software." def set_original_state(self): """Sets the original state.""" @@ -113,6 +117,12 @@ class Software(SimComponent): func=lambda request, context: self.set_health_state(SoftwareHealthState.COMPROMISED), ), ) + rm.add_request( + "patch", + RequestType( + func=lambda request, context: self.patch(), + ), + ) rm.add_request("scan", RequestType(func=lambda request, context: self.scan())) return rm @@ -183,10 +193,33 @@ class Software(SimComponent): """Update the observed health status to match the actual health status.""" self.health_state_visible = self.health_state_actual + def patch(self) -> None: + """Perform a patch on the software.""" + self._patching_countdown = self.patching_duration + self.set_health_state(SoftwareHealthState.PATCHING) + + def _update_patch_status(self) -> None: + """Update the patch status of the software.""" + self._patching_countdown -= 1 + if self._patching_countdown <= 0: + self.set_health_state(SoftwareHealthState.GOOD) + self._patching_countdown = None + self.patching_count += 1 + def reveal_to_red(self) -> None: """Reveals the software to the red agent.""" self.revealed_to_red = True + def apply_timestep(self, timestep: int) -> None: + """ + Apply a single timestep to the software. + + :param timestep: The current timestep of the simulation. + """ + super().apply_timestep(timestep) + if self.health_state_actual == SoftwareHealthState.PATCHING: + self._update_patch_status() + class IOSoftware(Software): """ diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py index 6b2ee0a7..714644e4 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py @@ -78,3 +78,16 @@ def test_service_enable(service): service.apply_request(["enable"]) assert service.operating_state == ServiceOperatingState.STOPPED + + +def test_service_patch(service): + """Test that a service can be patched and that it takes two timesteps to complete.""" + service.start() + assert service.health_state_actual == SoftwareHealthState.GOOD + + service.apply_request(["patch"]) + assert service.health_state_actual == SoftwareHealthState.PATCHING + service.apply_timestep(1) + assert service.health_state_actual == SoftwareHealthState.PATCHING + service.apply_timestep(2) + assert service.health_state_actual == SoftwareHealthState.GOOD