Merge branch 'dev' into feature/2871-Add_show_methods_agents
This commit is contained in:
@@ -1 +1 @@
|
||||
3.3.0-dev0
|
||||
3.3.0
|
||||
|
||||
@@ -52,6 +52,14 @@ class HostObservation(AbstractObservation, identifier="HOST"):
|
||||
"""
|
||||
If True, files and folders must be scanned to update the health state. If False, true state is always shown.
|
||||
"""
|
||||
services_requires_scan: Optional[bool] = None
|
||||
"""
|
||||
If True, services must be scanned to update the health state. If False, true state is always shown.
|
||||
"""
|
||||
applications_requires_scan: Optional[bool] = None
|
||||
"""
|
||||
If True, applications must be scanned to update the health state. If False, true state is always shown.
|
||||
"""
|
||||
include_users: Optional[bool] = True
|
||||
"""If True, report user session information."""
|
||||
|
||||
@@ -71,6 +79,8 @@ class HostObservation(AbstractObservation, identifier="HOST"):
|
||||
monitored_traffic: Optional[Dict],
|
||||
include_num_access: bool,
|
||||
file_system_requires_scan: bool,
|
||||
services_requires_scan: bool,
|
||||
applications_requires_scan: bool,
|
||||
include_users: bool,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -106,6 +116,12 @@ class HostObservation(AbstractObservation, identifier="HOST"):
|
||||
:param file_system_requires_scan: If True, the files and folders must be scanned to update the health state.
|
||||
If False, the true state is always shown.
|
||||
:type file_system_requires_scan: bool
|
||||
:param services_requires_scan: If True, services must be scanned to update the health state.
|
||||
If False, the true state is always shown.
|
||||
:type services_requires_scan: bool
|
||||
:param applications_requires_scan: If True, applications must be scanned to update the health state.
|
||||
If False, the true state is always shown.
|
||||
:type applications_requires_scan: bool
|
||||
:param include_users: If True, report user session information.
|
||||
:type include_users: bool
|
||||
"""
|
||||
@@ -119,7 +135,7 @@ class HostObservation(AbstractObservation, identifier="HOST"):
|
||||
# Ensure lists have lengths equal to specified counts by truncating or padding
|
||||
self.services: List[ServiceObservation] = services
|
||||
while len(self.services) < num_services:
|
||||
self.services.append(ServiceObservation(where=None))
|
||||
self.services.append(ServiceObservation(where=None, services_requires_scan=services_requires_scan))
|
||||
while len(self.services) > num_services:
|
||||
truncated_service = self.services.pop()
|
||||
msg = f"Too many services in Node observation space for node. Truncating service {truncated_service.where}"
|
||||
@@ -127,7 +143,9 @@ class HostObservation(AbstractObservation, identifier="HOST"):
|
||||
|
||||
self.applications: List[ApplicationObservation] = applications
|
||||
while len(self.applications) < num_applications:
|
||||
self.applications.append(ApplicationObservation(where=None))
|
||||
self.applications.append(
|
||||
ApplicationObservation(where=None, applications_requires_scan=applications_requires_scan)
|
||||
)
|
||||
while len(self.applications) > num_applications:
|
||||
truncated_application = self.applications.pop()
|
||||
msg = f"Too many applications in Node observation space for node. Truncating {truncated_application.where}"
|
||||
@@ -263,6 +281,10 @@ class HostObservation(AbstractObservation, identifier="HOST"):
|
||||
folder_config.file_system_requires_scan = config.file_system_requires_scan
|
||||
for nic_config in config.network_interfaces:
|
||||
nic_config.include_nmne = config.include_nmne
|
||||
for service_config in config.services:
|
||||
service_config.services_requires_scan = config.services_requires_scan
|
||||
for application_config in config.applications:
|
||||
application_config.applications_requires_scan = config.applications_requires_scan
|
||||
|
||||
services = [ServiceObservation.from_config(config=c, parent_where=where) for c in config.services]
|
||||
applications = [ApplicationObservation.from_config(config=c, parent_where=where) for c in config.applications]
|
||||
@@ -293,5 +315,7 @@ class HostObservation(AbstractObservation, identifier="HOST"):
|
||||
monitored_traffic=config.monitored_traffic,
|
||||
include_num_access=config.include_num_access,
|
||||
file_system_requires_scan=config.file_system_requires_scan,
|
||||
services_requires_scan=config.services_requires_scan,
|
||||
applications_requires_scan=config.applications_requires_scan,
|
||||
include_users=config.include_users,
|
||||
)
|
||||
|
||||
@@ -45,7 +45,13 @@ class NodesObservation(AbstractObservation, identifier="NODES"):
|
||||
include_num_access: Optional[bool] = None
|
||||
"""Flag to include the number of accesses."""
|
||||
file_system_requires_scan: bool = True
|
||||
"""If True, the folder must be scanned to update the health state. Tf False, the true state is always shown."""
|
||||
"""If True, the folder must be scanned to update the health state. If False, the true state is always shown."""
|
||||
services_requires_scan: bool = True
|
||||
"""If True, the services must be scanned to update the health state.
|
||||
If False, the true state is always shown."""
|
||||
applications_requires_scan: bool = True
|
||||
"""If True, the applications must be scanned to update the health state.
|
||||
If False, the true state is always shown."""
|
||||
include_users: Optional[bool] = True
|
||||
"""If True, report user session information."""
|
||||
num_ports: Optional[int] = None
|
||||
@@ -193,6 +199,10 @@ class NodesObservation(AbstractObservation, identifier="NODES"):
|
||||
host_config.include_num_access = config.include_num_access
|
||||
if host_config.file_system_requires_scan is None:
|
||||
host_config.file_system_requires_scan = config.file_system_requires_scan
|
||||
if host_config.services_requires_scan is None:
|
||||
host_config.services_requires_scan = config.services_requires_scan
|
||||
if host_config.applications_requires_scan is None:
|
||||
host_config.applications_requires_scan = config.applications_requires_scan
|
||||
if host_config.include_users is None:
|
||||
host_config.include_users = config.include_users
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict
|
||||
from typing import Dict, Optional
|
||||
|
||||
from gymnasium import spaces
|
||||
from gymnasium.core import ObsType
|
||||
@@ -19,7 +19,10 @@ class ServiceObservation(AbstractObservation, identifier="SERVICE"):
|
||||
service_name: str
|
||||
"""Name of the service, used for querying simulation state dictionary"""
|
||||
|
||||
def __init__(self, where: WhereType) -> None:
|
||||
services_requires_scan: Optional[bool] = None
|
||||
"""If True, services must be scanned to update the health state. If False, true state is always shown."""
|
||||
|
||||
def __init__(self, where: WhereType, services_requires_scan: bool) -> None:
|
||||
"""
|
||||
Initialise a service observation instance.
|
||||
|
||||
@@ -28,6 +31,7 @@ class ServiceObservation(AbstractObservation, identifier="SERVICE"):
|
||||
:type where: WhereType
|
||||
"""
|
||||
self.where = where
|
||||
self.services_requires_scan = services_requires_scan
|
||||
self.default_observation = {"operating_status": 0, "health_status": 0}
|
||||
|
||||
def observe(self, state: Dict) -> ObsType:
|
||||
@@ -44,7 +48,9 @@ class ServiceObservation(AbstractObservation, identifier="SERVICE"):
|
||||
return self.default_observation
|
||||
return {
|
||||
"operating_status": service_state["operating_state"],
|
||||
"health_status": service_state["health_state_visible"],
|
||||
"health_status": service_state["health_state_visible"]
|
||||
if self.services_requires_scan
|
||||
else service_state["health_state_actual"],
|
||||
}
|
||||
|
||||
@property
|
||||
@@ -70,7 +76,9 @@ class ServiceObservation(AbstractObservation, identifier="SERVICE"):
|
||||
:return: Constructed service observation instance.
|
||||
:rtype: ServiceObservation
|
||||
"""
|
||||
return cls(where=parent_where + ["services", config.service_name])
|
||||
return cls(
|
||||
where=parent_where + ["services", config.service_name], services_requires_scan=config.services_requires_scan
|
||||
)
|
||||
|
||||
|
||||
class ApplicationObservation(AbstractObservation, identifier="APPLICATION"):
|
||||
@@ -82,7 +90,12 @@ class ApplicationObservation(AbstractObservation, identifier="APPLICATION"):
|
||||
application_name: str
|
||||
"""Name of the application, used for querying simulation state dictionary"""
|
||||
|
||||
def __init__(self, where: WhereType) -> None:
|
||||
applications_requires_scan: Optional[bool] = None
|
||||
"""
|
||||
If True, applications must be scanned to update the health state. If False, true state is always shown.
|
||||
"""
|
||||
|
||||
def __init__(self, where: WhereType, applications_requires_scan: bool) -> None:
|
||||
"""
|
||||
Initialise an application observation instance.
|
||||
|
||||
@@ -92,6 +105,7 @@ class ApplicationObservation(AbstractObservation, identifier="APPLICATION"):
|
||||
:type where: WhereType
|
||||
"""
|
||||
self.where = where
|
||||
self.applications_requires_scan = applications_requires_scan
|
||||
self.default_observation = {"operating_status": 0, "health_status": 0, "num_executions": 0}
|
||||
|
||||
# TODO: allow these to be configured in yaml
|
||||
@@ -128,7 +142,9 @@ class ApplicationObservation(AbstractObservation, identifier="APPLICATION"):
|
||||
return self.default_observation
|
||||
return {
|
||||
"operating_status": application_state["operating_state"],
|
||||
"health_status": application_state["health_state_visible"],
|
||||
"health_status": application_state["health_state_visible"]
|
||||
if self.applications_requires_scan
|
||||
else application_state["health_state_actual"],
|
||||
"num_executions": self._categorise_num_executions(application_state["num_executions"]),
|
||||
}
|
||||
|
||||
@@ -161,4 +177,7 @@ class ApplicationObservation(AbstractObservation, identifier="APPLICATION"):
|
||||
:return: Constructed application observation instance.
|
||||
:rtype: ApplicationObservation
|
||||
"""
|
||||
return cls(where=parent_where + ["applications", config.application_name])
|
||||
return cls(
|
||||
where=parent_where + ["applications", config.application_name],
|
||||
applications_requires_scan=config.applications_requires_scan,
|
||||
)
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
"source": [
|
||||
"## **Notebook Setup** | Network Prerequisites\n",
|
||||
"\n",
|
||||
"Before the Red Agent is able to perform any C2 specific actions, the C2 Server needs to be installed and run before the Red Agent can perform any C2 specific action.\n",
|
||||
"Before the Red Agent is able to perform any C2 specific actions, the C2 Server needs to be installed and run.\n",
|
||||
"This is because in higher fidelity environments (and the real-world) a C2 server would not be accessible by a private network blue agent and the C2 Server would already be in place before the an adversary (Red Agent) starts.\n",
|
||||
"\n",
|
||||
"The cells below install and run the C2 Server on client_1 directly via the simulation API."
|
||||
@@ -1164,7 +1164,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now we are unable to do so as the C2 Server is unable has lost it's connection to the C2 Beacon:"
|
||||
"Now we are unable to do so as the C2 Server has lost its connection to the C2 Beacon:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1276,7 +1276,7 @@
|
||||
"source": [
|
||||
"#### Blocking C2 Traffic via ACL.\n",
|
||||
"\n",
|
||||
"Another potential option a blue agent could take is by placing an ACL rule which blocks traffic between the C2 Server can C2 Beacon.\n",
|
||||
"Another potential option a blue agent could take is by placing an ACL rule which blocks traffic between the C2 Server and C2 Beacon.\n",
|
||||
"\n",
|
||||
"It's worth noting the potential effectiveness of this approach is connected to the current green agent traffic on the network. For example, if there are multiple green agents using the C2 Beacon's host node then blocking all traffic would lead to a negative reward. The same applies for the previous example."
|
||||
]
|
||||
@@ -1450,7 +1450,7 @@
|
||||
"source": [
|
||||
"### **Command and Control** | Configurability | C2 Server IP Address\n",
|
||||
"\n",
|
||||
"As with a majority of client and server based application configuration in primaite, the remote IP of server must be supplied.\n",
|
||||
"As with a majority of client and server based application configurations in primaite, the remote IP of a server must be supplied.\n",
|
||||
"\n",
|
||||
"In the case of the C2 Beacon, the C2 Server's IP address must be supplied before the C2 beacon will be able to perform any other actions (including ``APPLICATION EXECUTE``).\n",
|
||||
"\n",
|
||||
@@ -1727,7 +1727,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"The next set of code cells will demonstrate the impact this option from a blue agent perspective."
|
||||
"The next set of code cells will demonstrate the impact of this option from a blue agent perspective."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
" .environment(env=PrimaiteRayMARLEnv, env_config=cfg)\n",
|
||||
" .env_runners(num_env_runners=0)\n",
|
||||
" .training(train_batch_size=128)\n",
|
||||
" .evaluation(evaluation_duration=1)\n",
|
||||
" )\n"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
" .environment(env=PrimaiteRayEnv, env_config=env_config)\n",
|
||||
" .env_runners(num_env_runners=0)\n",
|
||||
" .training(train_batch_size=128)\n",
|
||||
" .evaluation(evaluation_duration=1)\n",
|
||||
")\n"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Episode 0\n",
|
||||
"Let' run the episodes to verify that the agents are changing as expected. In episode 0, there should be no green or red agents, just the defender blue agent."
|
||||
"Let's run the episodes to verify that the agents are changing as expected. In episode 0, there should be no green or red agents, just the defender blue agent."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ class ARP(Service):
|
||||
|
||||
:param markdown: If True, format the output as Markdown. Otherwise, use plain text.
|
||||
"""
|
||||
table = PrettyTable(["IP Address", "MAC Address", "Via"])
|
||||
table = PrettyTable(["IP Address", "MAC Address", "Via", "Port"])
|
||||
if markdown:
|
||||
table.set_style(MARKDOWN)
|
||||
table.align = "l"
|
||||
@@ -58,6 +58,7 @@ class ARP(Service):
|
||||
str(ip),
|
||||
arp.mac_address,
|
||||
self.software_manager.node.network_interfaces[arp.network_interface_uuid].mac_address,
|
||||
self.software_manager.node.network_interfaces[arp.network_interface_uuid].port_num,
|
||||
]
|
||||
)
|
||||
print(table)
|
||||
|
||||
Reference in New Issue
Block a user