Merge remote-tracking branch 'origin/dev' into feature/2257-router-routes-cannot-be-represented-in-config-file
This commit is contained in:
@@ -153,22 +153,18 @@ class SimComponent(BaseModel):
|
||||
uuid: str = Field(default_factory=lambda: str(uuid4()))
|
||||
"""The component UUID."""
|
||||
|
||||
_original_state: Dict = {}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._request_manager: RequestManager = self._init_request_manager()
|
||||
self._parent: Optional["SimComponent"] = None
|
||||
|
||||
# @abstractmethod
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
pass
|
||||
def setup_for_episode(self, episode: int):
|
||||
"""
|
||||
Perform any additional setup on this component that can't happen during __init__.
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
for key, value in self._original_state.items():
|
||||
self.__setattr__(key, value)
|
||||
For instance, some components may require for the entire network to exist before some configuration can be set.
|
||||
"""
|
||||
pass
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
"""
|
||||
|
||||
@@ -42,19 +42,6 @@ class Account(SimComponent):
|
||||
"Account Type, currently this can be service account (used by apps) or user account."
|
||||
enabled: bool = True
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_include = {
|
||||
"num_logons",
|
||||
"num_logoffs",
|
||||
"num_group_changes",
|
||||
"username",
|
||||
"password",
|
||||
"account_type",
|
||||
"enabled",
|
||||
}
|
||||
self._original_state = self.model_dump(include=vals_to_include)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Produce a dictionary describing the current state of this object.
|
||||
|
||||
@@ -73,20 +73,6 @@ class File(FileSystemItemABC):
|
||||
|
||||
self.sys_log.info(f"Created file /{self.path} (id: {self.uuid})")
|
||||
|
||||
self.set_original_state()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting File ({self.path}) original state on node {self.sys_log.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {"folder_id", "folder_name", "file_type", "sim_size", "real", "sim_path", "sim_root"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting File ({self.path}) state on node {self.sys_log.hostname}")
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
@property
|
||||
def path(self) -> str:
|
||||
"""
|
||||
|
||||
@@ -34,43 +34,6 @@ class FileSystem(SimComponent):
|
||||
if not self.folders:
|
||||
self.create_folder("root")
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting FileSystem original state on node {self.sys_log.hostname}")
|
||||
for folder in self.folders.values():
|
||||
folder.set_original_state()
|
||||
# Capture a list of all 'original' file uuids
|
||||
original_keys = list(self.folders.keys())
|
||||
vals_to_include = {"sim_root"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
self._original_state["original_folder_uuids"] = original_keys
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting FileSystem state on node {self.sys_log.hostname}")
|
||||
# Move any 'original' folder that have been deleted back to folders
|
||||
original_folder_uuids = self._original_state["original_folder_uuids"]
|
||||
for uuid in original_folder_uuids:
|
||||
if uuid in self.deleted_folders:
|
||||
folder = self.deleted_folders[uuid]
|
||||
self.deleted_folders.pop(uuid)
|
||||
self.folders[uuid] = folder
|
||||
|
||||
# Clear any other deleted folders that aren't original (have been created by agent)
|
||||
self.deleted_folders.clear()
|
||||
|
||||
# Now clear all non-original folders created by agent
|
||||
current_folder_uuids = list(self.folders.keys())
|
||||
for uuid in current_folder_uuids:
|
||||
if uuid not in original_folder_uuids:
|
||||
folder = self.folders[uuid]
|
||||
self.folders.pop(uuid)
|
||||
|
||||
# Now reset all remaining folders
|
||||
for folder in self.folders.values():
|
||||
folder.reset_component_for_episode(episode)
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
|
||||
|
||||
@@ -85,11 +85,6 @@ class FileSystemItemABC(SimComponent):
|
||||
deleted: bool = False
|
||||
"If true, the FileSystemItem was deleted."
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_keep = {"name", "health_status", "visible_health_status", "previous_hash", "revealed_to_red", "deleted"}
|
||||
self._original_state = self.model_dump(include=vals_to_keep)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Produce a dictionary describing the current state of this object.
|
||||
|
||||
@@ -49,49 +49,6 @@ class Folder(FileSystemItemABC):
|
||||
|
||||
self.sys_log.info(f"Created file /{self.name} (id: {self.uuid})")
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting Folder ({self.name}) original state on node {self.sys_log.hostname}")
|
||||
for file in self.files.values():
|
||||
file.set_original_state()
|
||||
super().set_original_state()
|
||||
vals_to_include = {
|
||||
"scan_duration",
|
||||
"scan_countdown",
|
||||
"red_scan_duration",
|
||||
"red_scan_countdown",
|
||||
"restore_duration",
|
||||
"restore_countdown",
|
||||
}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
self._original_state["original_file_uuids"] = list(self.files.keys())
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting Folder ({self.name}) state on node {self.sys_log.hostname}")
|
||||
# Move any 'original' file that have been deleted back to files
|
||||
original_file_uuids = self._original_state["original_file_uuids"]
|
||||
for uuid in original_file_uuids:
|
||||
if uuid in self.deleted_files:
|
||||
file = self.deleted_files[uuid]
|
||||
self.deleted_files.pop(uuid)
|
||||
self.files[uuid] = file
|
||||
|
||||
# Clear any other deleted files that aren't original (have been created by agent)
|
||||
self.deleted_files.clear()
|
||||
|
||||
# Now clear all non-original files created by agent
|
||||
current_file_uuids = list(self.files.keys())
|
||||
for uuid in current_file_uuids:
|
||||
if uuid not in original_file_uuids:
|
||||
file = self.files[uuid]
|
||||
self.files.pop(uuid)
|
||||
|
||||
# Now reset all remaining files
|
||||
for file in self.files.values():
|
||||
file.reset_component_for_episode(episode)
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
rm.add_request(
|
||||
|
||||
@@ -273,11 +273,6 @@ class IPWirelessNetworkInterface(WirelessNetworkInterface, Layer3Interface, ABC)
|
||||
|
||||
return state
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_include = {"ip_address", "subnet_mask", "mac_address", "speed", "mtu", "wake_on_lan", "enabled"}
|
||||
self._original_state = self.model_dump(include=vals_to_include)
|
||||
|
||||
def enable(self):
|
||||
"""
|
||||
Enables this wired network interface and attempts to send a "hello" message to the default gateway.
|
||||
|
||||
@@ -45,19 +45,12 @@ class Network(SimComponent):
|
||||
|
||||
self._nx_graph = MultiGraph()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
for node in self.nodes.values():
|
||||
node.set_original_state()
|
||||
for link in self.links.values():
|
||||
link.set_original_state()
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
def setup_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
for node in self.nodes.values():
|
||||
node.reset_component_for_episode(episode)
|
||||
node.setup_for_episode(episode=episode)
|
||||
for link in self.links.values():
|
||||
link.reset_component_for_episode(episode)
|
||||
link.setup_for_episode(episode=episode)
|
||||
|
||||
for node in self.nodes.values():
|
||||
node.power_on()
|
||||
@@ -179,7 +172,7 @@ class Network(SimComponent):
|
||||
def clear_links(self):
|
||||
"""Clear all the links in the network by resetting their component state for the episode."""
|
||||
for link in self.links.values():
|
||||
link.reset_component_for_episode()
|
||||
link.setup_for_episode(episode=0) # TODO: shouldn't be using this method here.
|
||||
|
||||
def draw(self, seed: int = 123):
|
||||
"""
|
||||
|
||||
@@ -100,6 +100,15 @@ class NetworkInterface(SimComponent, ABC):
|
||||
nmne: Dict = Field(default_factory=lambda: {})
|
||||
"A dict containing details of the number of malicious network events captured."
|
||||
|
||||
def setup_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
super().setup_for_episode(episode=episode)
|
||||
self.nmne = {}
|
||||
if episode and self.pcap:
|
||||
self.pcap.current_episode = episode
|
||||
self.pcap.setup_logger()
|
||||
self.enable()
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
|
||||
@@ -127,15 +136,6 @@ class NetworkInterface(SimComponent, ABC):
|
||||
state.update({"nmne": self.nmne})
|
||||
return state
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
super().reset_component_for_episode(episode)
|
||||
self.nmne = {}
|
||||
if episode and self.pcap:
|
||||
self.pcap.current_episode = episode
|
||||
self.pcap.setup_logger()
|
||||
self.enable()
|
||||
|
||||
@abstractmethod
|
||||
def enable(self):
|
||||
"""Enable the interface."""
|
||||
@@ -547,14 +547,6 @@ class Link(SimComponent):
|
||||
self.endpoint_b.connect_link(self)
|
||||
self.endpoint_up()
|
||||
|
||||
self.set_original_state()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_include = {"bandwidth", "current_load"}
|
||||
self._original_state = self.model_dump(include=vals_to_include)
|
||||
super().set_original_state()
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Produce a dictionary describing the current state of this object.
|
||||
@@ -740,50 +732,20 @@ class Node(SimComponent):
|
||||
self.session_manager.node = self
|
||||
self.session_manager.software_manager = self.software_manager
|
||||
self._install_system_software()
|
||||
self.set_original_state()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
for software in self.software_manager.software.values():
|
||||
software.set_original_state()
|
||||
|
||||
self.file_system.set_original_state()
|
||||
|
||||
for network_interface in self.network_interfaces.values():
|
||||
network_interface.set_original_state()
|
||||
|
||||
vals_to_include = {
|
||||
"hostname",
|
||||
"default_gateway",
|
||||
"operating_state",
|
||||
"revealed_to_red",
|
||||
"start_up_duration",
|
||||
"start_up_countdown",
|
||||
"shut_down_duration",
|
||||
"shut_down_countdown",
|
||||
"is_resetting",
|
||||
"node_scan_duration",
|
||||
"node_scan_countdown",
|
||||
"red_scan_countdown",
|
||||
}
|
||||
self._original_state = self.model_dump(include=vals_to_include)
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
def setup_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
# Reset Session Manager
|
||||
self.session_manager.clear()
|
||||
super().setup_for_episode(episode=episode)
|
||||
|
||||
# Reset File System
|
||||
self.file_system.reset_component_for_episode(episode)
|
||||
self.file_system.setup_for_episode(episode=episode)
|
||||
|
||||
# Reset all Nics
|
||||
for network_interface in self.network_interfaces.values():
|
||||
network_interface.reset_component_for_episode(episode)
|
||||
network_interface.setup_for_episode(episode=episode)
|
||||
|
||||
for software in self.software_manager.software.values():
|
||||
software.reset_component_for_episode(episode)
|
||||
software.setup_for_episode(episode=episode)
|
||||
|
||||
if episode and self.sys_log:
|
||||
self.sys_log.current_episode = episode
|
||||
|
||||
@@ -209,11 +209,6 @@ class NIC(IPWiredNetworkInterface):
|
||||
|
||||
return state
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_include = {"ip_address", "subnet_mask", "mac_address", "speed", "mtu", "wake_on_lan", "enabled"}
|
||||
self._original_state = self.model_dump(include=vals_to_include)
|
||||
|
||||
def receive_frame(self, frame: Frame) -> bool:
|
||||
"""
|
||||
Attempt to receive and process a network frame from the connected Link.
|
||||
|
||||
@@ -111,24 +111,6 @@ class Firewall(Router):
|
||||
sys_log=kwargs["sys_log"], implicit_action=ACLAction.PERMIT, name=f"{hostname} - External Outbound"
|
||||
)
|
||||
|
||||
self.set_original_state()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Set the original state for the Firewall."""
|
||||
super().set_original_state()
|
||||
vals_to_include = {
|
||||
"internal_port",
|
||||
"external_port",
|
||||
"dmz_port",
|
||||
"internal_inbound_acl",
|
||||
"internal_outbound_acl",
|
||||
"dmz_inbound_acl",
|
||||
"dmz_outbound_acl",
|
||||
"external_inbound_acl",
|
||||
"external_outbound_acl",
|
||||
}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Describes the current state of the Firewall.
|
||||
|
||||
@@ -136,11 +136,6 @@ class ACLRule(SimComponent):
|
||||
rule_strings.append(f"{key}={value}")
|
||||
return ", ".join(rule_strings)
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_keep = {"action", "protocol", "src_ip_address", "src_port", "dst_ip_address", "dst_port"}
|
||||
self._original_state = self.model_dump(include=vals_to_keep, exclude_none=True)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Describes the current state of the ACLRule.
|
||||
@@ -296,48 +291,6 @@ class AccessControlList(SimComponent):
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self._acl = [None] * (self.max_acl_rules - 1)
|
||||
self.set_original_state()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
self.implicit_rule.set_original_state()
|
||||
vals_to_keep = {"implicit_action", "max_acl_rules", "acl"}
|
||||
self._original_state = self.model_dump(include=vals_to_keep, exclude_none=True)
|
||||
|
||||
for i, rule in enumerate(self._acl):
|
||||
if not rule:
|
||||
continue
|
||||
self._default_config[i] = {"action": rule.action.name}
|
||||
if rule.src_ip_address:
|
||||
self._default_config[i]["src_ip"] = str(rule.src_ip_address)
|
||||
if rule.dst_ip_address:
|
||||
self._default_config[i]["dst_ip"] = str(rule.dst_ip_address)
|
||||
if rule.src_port:
|
||||
self._default_config[i]["src_port"] = rule.src_port.name
|
||||
if rule.dst_port:
|
||||
self._default_config[i]["dst_port"] = rule.dst_port.name
|
||||
if rule.protocol:
|
||||
self._default_config[i]["protocol"] = rule.protocol.name
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
self.implicit_rule.reset_component_for_episode(episode)
|
||||
super().reset_component_for_episode(episode)
|
||||
self._reset_rules_to_default()
|
||||
|
||||
def _reset_rules_to_default(self) -> None:
|
||||
"""Clear all ACL rules and set them to the default rules config."""
|
||||
self._acl = [None] * (self.max_acl_rules - 1)
|
||||
for r_num, r_cfg in self._default_config.items():
|
||||
self.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,
|
||||
)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
# TODO: Add src and dst wildcard masks as positional args in this request.
|
||||
@@ -616,11 +569,6 @@ class RouteEntry(SimComponent):
|
||||
metric: float = 0.0
|
||||
"The cost metric for this route. Default is 0.0."
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_include = {"address", "subnet_mask", "next_hop_ip_address", "metric"}
|
||||
self._original_values = self.model_dump(include=vals_to_include)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Describes the current state of the RouteEntry.
|
||||
@@ -653,17 +601,6 @@ class RouteTable(SimComponent):
|
||||
default_route: Optional[RouteEntry] = None
|
||||
sys_log: SysLog
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
super().set_original_state()
|
||||
self._original_state["routes_orig"] = self.routes
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
self.routes.clear()
|
||||
self.routes = self._original_state["routes_orig"]
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Describes the current state of the RouteTable.
|
||||
@@ -1104,8 +1041,6 @@ class Router(NetworkNode):
|
||||
|
||||
self._set_default_acl()
|
||||
|
||||
self.set_original_state()
|
||||
|
||||
def _install_system_software(self):
|
||||
"""
|
||||
Installs essential system software and network services on the router.
|
||||
@@ -1131,20 +1066,7 @@ class Router(NetworkNode):
|
||||
self.acl.add_rule(action=ACLAction.PERMIT, src_port=Port.ARP, dst_port=Port.ARP, position=22)
|
||||
self.acl.add_rule(action=ACLAction.PERMIT, protocol=IPProtocol.ICMP, position=23)
|
||||
|
||||
def set_original_state(self):
|
||||
"""
|
||||
Sets or resets the router to its original configuration state.
|
||||
|
||||
This method is called to initialize the router's state or to revert it to a known good configuration during
|
||||
network simulations or after configuration changes.
|
||||
"""
|
||||
self.acl.set_original_state()
|
||||
self.route_table.set_original_state()
|
||||
super().set_original_state()
|
||||
vals_to_include = {"num_ports"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
def setup_for_episode(self, episode: int):
|
||||
"""
|
||||
Resets the router's components for a new network simulation episode.
|
||||
|
||||
@@ -1154,13 +1076,10 @@ class Router(NetworkNode):
|
||||
:param episode: The episode number for which the router is being reset.
|
||||
"""
|
||||
self.software_manager.arp.clear()
|
||||
self.acl.reset_component_for_episode(episode)
|
||||
self.route_table.reset_component_for_episode(episode)
|
||||
for i, network_interface in self.network_interface.items():
|
||||
network_interface.reset_component_for_episode(episode)
|
||||
for i, _ in self.network_interface.items():
|
||||
self.enable_port(i)
|
||||
|
||||
super().reset_component_for_episode(episode)
|
||||
super().setup_for_episode(episode=episode)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
@@ -1391,7 +1310,6 @@ class Router(NetworkNode):
|
||||
network_interface.ip_address = ip_address
|
||||
network_interface.subnet_mask = subnet_mask
|
||||
self.sys_log.info(f"Configured Network Interface {network_interface}")
|
||||
self.set_original_state()
|
||||
|
||||
def enable_port(self, port: int):
|
||||
"""
|
||||
@@ -1493,8 +1411,16 @@ class Router(NetworkNode):
|
||||
subnet_mask=IPv4Address(port_cfg.get("subnet_mask", "255.255.255.0")),
|
||||
)
|
||||
if "acl" in cfg:
|
||||
router.acl._default_config = cfg["acl"] # save the config to allow resetting
|
||||
router.acl._reset_rules_to_default() # read the config and apply rules
|
||||
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(
|
||||
|
||||
@@ -32,12 +32,6 @@ class SwitchPort(WiredNetworkInterface):
|
||||
_connected_node: Optional[Switch] = None
|
||||
"The Switch to which the SwitchPort is connected."
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_include = {"port_num", "mac_address", "speed", "mtu", "enabled"}
|
||||
self._original_state = self.model_dump(include=vals_to_include)
|
||||
super().set_original_state()
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Produce a dictionary describing the current state of this object.
|
||||
|
||||
@@ -122,8 +122,6 @@ class WirelessRouter(Router):
|
||||
|
||||
self.connect_nic(RouterInterface(ip_address="127.0.0.1", subnet_mask="255.0.0.0", gateway="0.0.0.0"))
|
||||
|
||||
self.set_original_state()
|
||||
|
||||
@property
|
||||
def wireless_access_point(self) -> WirelessAccessPoint:
|
||||
"""
|
||||
@@ -166,7 +164,6 @@ class WirelessRouter(Router):
|
||||
network_interface.ip_address = ip_address
|
||||
network_interface.subnet_mask = subnet_mask
|
||||
self.sys_log.info(f"Configured WAP {network_interface}")
|
||||
self.set_original_state()
|
||||
self.wireless_access_point.frequency = frequency # Set operating frequency
|
||||
self.wireless_access_point.enable() # Re-enable the WAP with new settings
|
||||
|
||||
|
||||
@@ -21,13 +21,9 @@ class Simulation(SimComponent):
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
self.network.set_original_state()
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
def setup_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
self.network.reset_component_for_episode(episode)
|
||||
self.network.setup_for_episode(episode=episode)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
|
||||
@@ -38,12 +38,6 @@ class Application(IOSoftware):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
super().set_original_state()
|
||||
vals_to_include = {"operating_state", "execution_control_status", "num_executions", "groups"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
@abstractmethod
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
|
||||
@@ -31,20 +31,6 @@ class DatabaseClient(Application):
|
||||
kwargs["port"] = Port.POSTGRES_SERVER
|
||||
kwargs["protocol"] = IPProtocol.TCP
|
||||
super().__init__(**kwargs)
|
||||
self.set_original_state()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting DatabaseClient WebServer original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {"server_ip_address", "server_password", "connected", "_query_success_tracker"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting DataBaseClient state on node {self.software_manager.node.hostname}")
|
||||
super().reset_component_for_episode(episode)
|
||||
self._query_success_tracker.clear()
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
|
||||
@@ -49,26 +49,6 @@ class DataManipulationBot(DatabaseClient):
|
||||
super().__init__(**kwargs)
|
||||
self.name = "DataManipulationBot"
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting DataManipulationBot original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {
|
||||
"server_ip_address",
|
||||
"payload",
|
||||
"server_password",
|
||||
"port_scan_p_of_success",
|
||||
"data_manipulation_p_of_success",
|
||||
"attack_stage",
|
||||
"repeat",
|
||||
}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting DataManipulationBot state on node {self.software_manager.node.hostname}")
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
|
||||
|
||||
@@ -57,27 +57,6 @@ class DoSBot(DatabaseClient, Application):
|
||||
self.name = "DoSBot"
|
||||
self.max_sessions = 1000 # override normal max sessions
|
||||
|
||||
def set_original_state(self):
|
||||
"""Set the original state of the Denial of Service Bot."""
|
||||
_LOGGER.debug(f"Setting {self.name} original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {
|
||||
"target_ip_address",
|
||||
"target_port",
|
||||
"payload",
|
||||
"repeat",
|
||||
"attack_stage",
|
||||
"max_sessions",
|
||||
"port_scan_p_of_success",
|
||||
"dos_intensity",
|
||||
}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting {self.name} state on node {self.software_manager.node.hostname}")
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
|
||||
|
||||
@@ -47,21 +47,8 @@ class WebBrowser(Application):
|
||||
kwargs["port"] = Port.HTTP
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self.set_original_state()
|
||||
self.run()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting WebBrowser original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {"target_url", "domain_name_ip_address", "latest_response"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting WebBrowser state on node {self.software_manager.node.hostname}")
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
rm.add_request(
|
||||
@@ -80,9 +67,6 @@ class WebBrowser(Application):
|
||||
state["history"] = [hist_item.state() for hist_item in self.history]
|
||||
return state
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
|
||||
def get_webpage(self, url: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Retrieve the webpage.
|
||||
|
||||
@@ -24,12 +24,6 @@ class Process(Software):
|
||||
operating_state: ProcessOperatingState
|
||||
"The current operating state of the Process."
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
super().set_original_state()
|
||||
vals_to_include = {"operating_state"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
@abstractmethod
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
|
||||
@@ -41,25 +41,6 @@ class DatabaseService(Service):
|
||||
super().__init__(**kwargs)
|
||||
self._create_db_file()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting DatabaseService original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {
|
||||
"password",
|
||||
"connections",
|
||||
"backup_server_ip",
|
||||
"latest_backup_directory",
|
||||
"latest_backup_file_name",
|
||||
}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug("Resetting DatabaseService original state on node {self.software_manager.node.hostname}")
|
||||
self.clear_connections()
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def configure_backup(self, backup_server: IPv4Address):
|
||||
"""
|
||||
Set up the database backup.
|
||||
|
||||
@@ -29,18 +29,6 @@ class DNSClient(Service):
|
||||
super().__init__(**kwargs)
|
||||
self.start()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting DNSClient original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {"dns_server"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
self.dns_cache.clear()
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Describes the current state of the software.
|
||||
|
||||
@@ -28,20 +28,6 @@ class DNSServer(Service):
|
||||
super().__init__(**kwargs)
|
||||
self.start()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting DNSServer original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {"dns_table"}
|
||||
self._original_state["dns_table_orig"] = self.model_dump(include=vals_to_include)["dns_table"]
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
self.dns_table.clear()
|
||||
for key, value in self._original_state["dns_table_orig"].items():
|
||||
self.dns_table[key] = value
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Describes the current state of the software.
|
||||
|
||||
@@ -27,18 +27,6 @@ class FTPClient(FTPServiceABC):
|
||||
super().__init__(**kwargs)
|
||||
self.start()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting FTPClient original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {"connected"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting FTPClient state on node {self.software_manager.node.hostname}")
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def _process_ftp_command(self, payload: FTPPacket, session_id: Optional[str] = None, **kwargs) -> FTPPacket:
|
||||
"""
|
||||
Process the command in the FTP Packet.
|
||||
|
||||
@@ -27,19 +27,6 @@ class FTPServer(FTPServiceABC):
|
||||
super().__init__(**kwargs)
|
||||
self.start()
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting FTPServer original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {"server_password"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting FTPServer state on node {self.software_manager.node.hostname}")
|
||||
self.clear_connections()
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def _process_ftp_command(self, payload: FTPPacket, session_id: Optional[str] = None, **kwargs) -> FTPPacket:
|
||||
"""
|
||||
Process the command in the FTP Packet.
|
||||
|
||||
@@ -49,21 +49,12 @@ class NTPClient(Service):
|
||||
state = super().describe_state()
|
||||
return state
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""
|
||||
Resets the Service component for a new episode.
|
||||
|
||||
This method ensures the Service is ready for a new episode, including resetting any
|
||||
stateful properties or statistics, and clearing any message queues.
|
||||
"""
|
||||
pass
|
||||
|
||||
def send(
|
||||
self,
|
||||
payload: NTPPacket,
|
||||
session_id: Optional[str] = None,
|
||||
dest_ip_address: IPv4Address = None,
|
||||
dest_port: [Port] = Port.NTP,
|
||||
dest_port: Port = Port.NTP,
|
||||
**kwargs,
|
||||
) -> bool:
|
||||
"""Requests NTP data from NTP server.
|
||||
|
||||
@@ -34,16 +34,6 @@ class NTPServer(Service):
|
||||
state = super().describe_state()
|
||||
return state
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""
|
||||
Resets the Service component for a new episode.
|
||||
|
||||
This method ensures the Service is ready for a new episode, including
|
||||
resetting any stateful properties or statistics, and clearing any message
|
||||
queues.
|
||||
"""
|
||||
pass
|
||||
|
||||
def receive(
|
||||
self,
|
||||
payload: NTPPacket,
|
||||
|
||||
@@ -78,12 +78,6 @@ class Service(IOSoftware):
|
||||
"""
|
||||
return super().receive(payload=payload, session_id=session_id, **kwargs)
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
super().set_original_state()
|
||||
vals_to_include = {"operating_state", "restart_duration", "restart_countdown"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
rm.add_request("scan", RequestType(func=lambda request, context: self.scan()))
|
||||
|
||||
@@ -23,18 +23,6 @@ class WebServer(Service):
|
||||
|
||||
last_response_status_code: Optional[HttpStatusCode] = None
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
_LOGGER.debug(f"Setting WebServer original state on node {self.software_manager.node.hostname}")
|
||||
super().set_original_state()
|
||||
vals_to_include = {"last_response_status_code"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
def reset_component_for_episode(self, episode: int):
|
||||
"""Reset the original state of the SimComponent."""
|
||||
_LOGGER.debug(f"Resetting WebServer state on node {self.software_manager.node.hostname}")
|
||||
super().reset_component_for_episode(episode)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Produce a dictionary describing the current state of this object.
|
||||
@@ -130,7 +118,7 @@ class WebServer(Service):
|
||||
self.set_health_state(SoftwareHealthState.COMPROMISED)
|
||||
|
||||
return response
|
||||
except Exception:
|
||||
except Exception: # TODO: refactor this. Likely to cause silent bugs. (ADO ticket #2345 )
|
||||
# something went wrong on the server
|
||||
response.status_code = HttpStatusCode.INTERNAL_SERVER_ERROR
|
||||
return response
|
||||
|
||||
@@ -3,7 +3,7 @@ from abc import abstractmethod
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from ipaddress import IPv4Address, IPv4Network
|
||||
from typing import Any, Dict, Optional, Union
|
||||
from typing import Any, Dict, Optional, TYPE_CHECKING, Union
|
||||
|
||||
from primaite.simulator.core import _LOGGER, RequestManager, RequestType, SimComponent
|
||||
from primaite.simulator.file_system.file_system import FileSystem, Folder
|
||||
@@ -13,6 +13,9 @@ from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.core.session_manager import Session
|
||||
from primaite.simulator.system.core.sys_log import SysLog
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from primaite.simulator.system.core.software_manager import SoftwareManager
|
||||
|
||||
|
||||
class SoftwareType(Enum):
|
||||
"""
|
||||
@@ -84,7 +87,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: Any = None
|
||||
software_manager: "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."
|
||||
@@ -97,19 +100,6 @@ class Software(SimComponent):
|
||||
_patching_countdown: Optional[int] = None
|
||||
"Current number of ticks left to patch the software."
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
vals_to_include = {
|
||||
"name",
|
||||
"health_state_actual",
|
||||
"health_state_visible",
|
||||
"criticality",
|
||||
"patching_count",
|
||||
"scanning_count",
|
||||
"revealed_to_red",
|
||||
}
|
||||
self._original_state = self.model_dump(include=vals_to_include)
|
||||
|
||||
def _init_request_manager(self) -> RequestManager:
|
||||
rm = super()._init_request_manager()
|
||||
rm.add_request(
|
||||
@@ -248,12 +238,6 @@ class IOSoftware(Software):
|
||||
_connections: Dict[str, Dict] = {}
|
||||
"Active connections."
|
||||
|
||||
def set_original_state(self):
|
||||
"""Sets the original state."""
|
||||
super().set_original_state()
|
||||
vals_to_include = {"installing_count", "max_sessions", "tcp", "udp", "port"}
|
||||
self._original_state.update(self.model_dump(include=vals_to_include))
|
||||
|
||||
@abstractmethod
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user