Align observations to Common approach
This commit is contained in:
@@ -2,10 +2,10 @@ training_config:
|
|||||||
rl_framework: SB3
|
rl_framework: SB3
|
||||||
rl_algorithm: PPO
|
rl_algorithm: PPO
|
||||||
seed: 333
|
seed: 333
|
||||||
n_learn_episodes: 4
|
n_learn_episodes: 1
|
||||||
n_learn_steps: 128
|
n_learn_steps: 8
|
||||||
n_eval_episodes: 1
|
n_eval_episodes: 0
|
||||||
n_eval_steps: 128
|
n_eval_steps: 8
|
||||||
|
|
||||||
|
|
||||||
game_config:
|
game_config:
|
||||||
@@ -39,10 +39,10 @@ game_config:
|
|||||||
options:
|
options:
|
||||||
nodes:
|
nodes:
|
||||||
- node_ref: client_2
|
- node_ref: client_2
|
||||||
max_folders_per_node: 2
|
max_folders_per_node: 1
|
||||||
max_files_per_folder: 2
|
max_files_per_folder: 1
|
||||||
max_services_per_node: 2
|
max_services_per_node: 1
|
||||||
max_nics_per_node: 8
|
max_nics_per_node: 2
|
||||||
max_acl_rules: 10
|
max_acl_rules: 10
|
||||||
|
|
||||||
reward_function:
|
reward_function:
|
||||||
@@ -93,9 +93,9 @@ game_config:
|
|||||||
options:
|
options:
|
||||||
nodes:
|
nodes:
|
||||||
- node_ref: client_1
|
- node_ref: client_1
|
||||||
max_folders_per_node: 2
|
max_folders_per_node: 1
|
||||||
max_files_per_folder: 2
|
max_files_per_folder: 1
|
||||||
max_services_per_node: 2
|
max_services_per_node: 1
|
||||||
|
|
||||||
reward_function:
|
reward_function:
|
||||||
reward_components:
|
reward_components:
|
||||||
@@ -113,9 +113,10 @@ game_config:
|
|||||||
observation_space:
|
observation_space:
|
||||||
type: UC2BlueObservation
|
type: UC2BlueObservation
|
||||||
options:
|
options:
|
||||||
num_services_per_node: 2
|
num_services_per_node: 1
|
||||||
num_folders_per_node: 2
|
num_folders_per_node: 1
|
||||||
num_files_per_folder: 2
|
num_files_per_folder: 1
|
||||||
|
num_nics_per_node: 2
|
||||||
nodes:
|
nodes:
|
||||||
- node_ref: domain_controller
|
- node_ref: domain_controller
|
||||||
services:
|
services:
|
||||||
@@ -148,6 +149,8 @@ game_config:
|
|||||||
- link_ref: switch_2___client_2
|
- link_ref: switch_2___client_2
|
||||||
- link_ref: switch_2___security_suite
|
- link_ref: switch_2___security_suite
|
||||||
acl:
|
acl:
|
||||||
|
options:
|
||||||
|
max_acl_rules: 10
|
||||||
router_node_ref: router_1
|
router_node_ref: router_1
|
||||||
ip_address_order:
|
ip_address_order:
|
||||||
- node_ref: domain_controller
|
- node_ref: domain_controller
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class ServiceObservation(AbstractObservation):
|
|||||||
class LinkObservation(AbstractObservation):
|
class LinkObservation(AbstractObservation):
|
||||||
"""Observation of a link in the network."""
|
"""Observation of a link in the network."""
|
||||||
|
|
||||||
default_observation: spaces.Space = {"protocols": {"all": {"load": 0}}}
|
default_observation: spaces.Space = {"PROTOCOLS": {"ALL": 0}}
|
||||||
"Default observation is what should be returned when the link doesn't exist."
|
"Default observation is what should be returned when the link doesn't exist."
|
||||||
|
|
||||||
def __init__(self, where: Optional[Tuple[str]] = None) -> None:
|
def __init__(self, where: Optional[Tuple[str]] = None) -> None:
|
||||||
@@ -206,7 +206,7 @@ class LinkObservation(AbstractObservation):
|
|||||||
utilisation_category = int(utilisation_fraction * 10) + 1
|
utilisation_category = int(utilisation_fraction * 10) + 1
|
||||||
|
|
||||||
# TODO: once the links support separte load per protocol, this needs amendment to reflect that.
|
# TODO: once the links support separte load per protocol, this needs amendment to reflect that.
|
||||||
return {"protocols": {"all": {"load": utilisation_category}}}
|
return {"PROTOCOLS": {"ALL": utilisation_category}}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def space(self) -> spaces.Space:
|
def space(self) -> spaces.Space:
|
||||||
@@ -215,7 +215,7 @@ class LinkObservation(AbstractObservation):
|
|||||||
:return: Gymnasium space
|
:return: Gymnasium space
|
||||||
:rtype: spaces.Space
|
:rtype: spaces.Space
|
||||||
"""
|
"""
|
||||||
return spaces.Dict({"protocols": spaces.Dict({"all": spaces.Dict({"load": spaces.Discrete(11)})})})
|
return spaces.Dict({"PROTOCOLS": spaces.Dict({"ALL": spaces.Discrete(11)})})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_config(cls, config: Dict, session: "PrimaiteSession") -> "LinkObservation":
|
def from_config(cls, config: Dict, session: "PrimaiteSession") -> "LinkObservation":
|
||||||
@@ -264,7 +264,6 @@ class FolderObservation(AbstractObservation):
|
|||||||
truncated_file = self.files.pop()
|
truncated_file = self.files.pop()
|
||||||
msg = f"Too many files in folde observation. Truncating file {truncated_file}"
|
msg = f"Too many files in folde observation. Truncating file {truncated_file}"
|
||||||
_LOGGER.warn(msg)
|
_LOGGER.warn(msg)
|
||||||
raise UserWarning(msg)
|
|
||||||
|
|
||||||
self.default_observation = {
|
self.default_observation = {
|
||||||
"health_status": 0,
|
"health_status": 0,
|
||||||
@@ -407,6 +406,7 @@ class NodeObservation(AbstractObservation):
|
|||||||
num_services_per_node: int = 2,
|
num_services_per_node: int = 2,
|
||||||
num_folders_per_node: int = 2,
|
num_folders_per_node: int = 2,
|
||||||
num_files_per_folder: int = 2,
|
num_files_per_folder: int = 2,
|
||||||
|
num_nics_per_node: int = 2,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Configurable observation for a node in the simulation.
|
Configurable observation for a node in the simulation.
|
||||||
@@ -440,18 +440,25 @@ class NodeObservation(AbstractObservation):
|
|||||||
truncated_service = self.services.pop()
|
truncated_service = self.services.pop()
|
||||||
msg = f"Too many services in Node observation space for node. Truncating service {truncated_service.where}"
|
msg = f"Too many services in Node observation space for node. Truncating service {truncated_service.where}"
|
||||||
_LOGGER.warn(msg)
|
_LOGGER.warn(msg)
|
||||||
raise UserWarning(msg)
|
|
||||||
# truncate service list
|
# truncate service list
|
||||||
|
|
||||||
self.folders: List[FolderObservation] = folders
|
self.folders: List[FolderObservation] = folders
|
||||||
# add empty folder observation without `where` parameter that will always return default (blank) observations
|
# add empty folder observation without `where` parameter that will always return default (blank) observations
|
||||||
while len(self.folders) < num_folders_per_node:
|
while len(self.folders) < num_folders_per_node:
|
||||||
self.folders.append(FolderObservation())
|
self.folders.append(FolderObservation(num_files_per_folder=num_files_per_folder))
|
||||||
while len(self.folders) > num_folders_per_node:
|
while len(self.folders) > num_folders_per_node:
|
||||||
truncated_folder = self.folders.pop()
|
truncated_folder = self.folders.pop()
|
||||||
msg = f"Too many folders in Node observation for node. Truncating service {truncated_folder.where[-1]}"
|
msg = f"Too many folders in Node observation for node. Truncating service {truncated_folder.where[-1]}"
|
||||||
|
_LOGGER.warn(msg)
|
||||||
|
|
||||||
self.nics: List[NicObservation] = nics
|
self.nics: List[NicObservation] = nics
|
||||||
|
while len(self.nics) < num_nics_per_node:
|
||||||
|
self.nics.append(NicObservation())
|
||||||
|
while len(self.nics) > num_nics_per_node:
|
||||||
|
truncated_nic = self.nics.pop()
|
||||||
|
msg = f"Too many NICs in Node observation for node. Truncating service {truncated_nic.where[-1]}"
|
||||||
|
_LOGGER.warn(msg)
|
||||||
|
|
||||||
self.logon_status: bool = logon_status
|
self.logon_status: bool = logon_status
|
||||||
|
|
||||||
self.default_observation: Dict = {
|
self.default_observation: Dict = {
|
||||||
@@ -512,6 +519,7 @@ class NodeObservation(AbstractObservation):
|
|||||||
num_services_per_node: int = 2,
|
num_services_per_node: int = 2,
|
||||||
num_folders_per_node: int = 2,
|
num_folders_per_node: int = 2,
|
||||||
num_files_per_folder: int = 2,
|
num_files_per_folder: int = 2,
|
||||||
|
num_nics_per_node: int = 2,
|
||||||
) -> "NodeObservation":
|
) -> "NodeObservation":
|
||||||
"""Create node observation from a config. Also creates child service, folder and NIC observations.
|
"""Create node observation from a config. Also creates child service, folder and NIC observations.
|
||||||
|
|
||||||
@@ -562,6 +570,7 @@ class NodeObservation(AbstractObservation):
|
|||||||
num_services_per_node=num_services_per_node,
|
num_services_per_node=num_services_per_node,
|
||||||
num_folders_per_node=num_folders_per_node,
|
num_folders_per_node=num_folders_per_node,
|
||||||
num_files_per_folder=num_files_per_folder,
|
num_files_per_folder=num_files_per_folder,
|
||||||
|
num_nics_per_node=num_nics_per_node,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -605,7 +614,6 @@ class AclObservation(AbstractObservation):
|
|||||||
self.protocol_to_id: Dict[str, int] = {protocol: i + 2 for i, protocol in enumerate(protocols)}
|
self.protocol_to_id: Dict[str, int] = {protocol: i + 2 for i, protocol in enumerate(protocols)}
|
||||||
"List of protocols which are part of the game, defines ordering when converting to an ID"
|
"List of protocols which are part of the game, defines ordering when converting to an ID"
|
||||||
self.default_observation: Dict = {
|
self.default_observation: Dict = {
|
||||||
"RULES": {
|
|
||||||
i
|
i
|
||||||
+ 1: {
|
+ 1: {
|
||||||
"position": i,
|
"position": i,
|
||||||
@@ -618,7 +626,6 @@ class AclObservation(AbstractObservation):
|
|||||||
}
|
}
|
||||||
for i in range(self.num_rules)
|
for i in range(self.num_rules)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def observe(self, state: Dict) -> Dict:
|
def observe(self, state: Dict) -> Dict:
|
||||||
"""Generate observation based on the current state of the simulation.
|
"""Generate observation based on the current state of the simulation.
|
||||||
@@ -636,10 +643,9 @@ class AclObservation(AbstractObservation):
|
|||||||
|
|
||||||
# TODO: what if the ACL has more rules than num of max rules for obs space
|
# TODO: what if the ACL has more rules than num of max rules for obs space
|
||||||
obs = {}
|
obs = {}
|
||||||
obs["RULES"] = {}
|
|
||||||
for i, rule_state in acl_state.items():
|
for i, rule_state in acl_state.items():
|
||||||
if rule_state is None:
|
if rule_state is None:
|
||||||
obs["RULES"][i + 1] = {
|
obs[i + 1] = {
|
||||||
"position": i,
|
"position": i,
|
||||||
"permission": 0,
|
"permission": 0,
|
||||||
"source_node_id": 0,
|
"source_node_id": 0,
|
||||||
@@ -649,7 +655,7 @@ class AclObservation(AbstractObservation):
|
|||||||
"protocol": 0,
|
"protocol": 0,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
obs["RULES"][i + 1] = {
|
obs[i + 1] = {
|
||||||
"position": i,
|
"position": i,
|
||||||
"permission": rule_state["action"],
|
"permission": rule_state["action"],
|
||||||
"source_node_id": self.node_to_id[rule_state["src_ip_address"]],
|
"source_node_id": self.node_to_id[rule_state["src_ip_address"]],
|
||||||
@@ -668,8 +674,6 @@ class AclObservation(AbstractObservation):
|
|||||||
:rtype: spaces.Space
|
:rtype: spaces.Space
|
||||||
"""
|
"""
|
||||||
return spaces.Dict(
|
return spaces.Dict(
|
||||||
{
|
|
||||||
"RULES": spaces.Dict(
|
|
||||||
{
|
{
|
||||||
i
|
i
|
||||||
+ 1: spaces.Dict(
|
+ 1: spaces.Dict(
|
||||||
@@ -687,8 +691,6 @@ class AclObservation(AbstractObservation):
|
|||||||
for i in range(self.num_rules)
|
for i in range(self.num_rules)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_config(cls, config: Dict, session: "PrimaiteSession") -> "AclObservation":
|
def from_config(cls, config: Dict, session: "PrimaiteSession") -> "AclObservation":
|
||||||
@@ -701,6 +703,7 @@ class AclObservation(AbstractObservation):
|
|||||||
:return: Observation object
|
:return: Observation object
|
||||||
:rtype: AclObservation
|
:rtype: AclObservation
|
||||||
"""
|
"""
|
||||||
|
max_acl_rules = config["options"]["max_acl_rules"]
|
||||||
node_ip_to_idx = {}
|
node_ip_to_idx = {}
|
||||||
for ip_idx, ip_map_config in enumerate(config["ip_address_order"]):
|
for ip_idx, ip_map_config in enumerate(config["ip_address_order"]):
|
||||||
node_ref = ip_map_config["node_ref"]
|
node_ref = ip_map_config["node_ref"]
|
||||||
@@ -715,6 +718,7 @@ class AclObservation(AbstractObservation):
|
|||||||
ports=session.options.ports,
|
ports=session.options.ports,
|
||||||
protocols=session.options.protocols,
|
protocols=session.options.protocols,
|
||||||
where=["network", "nodes", router_uuid, "acl", "acl"],
|
where=["network", "nodes", router_uuid, "acl", "acl"],
|
||||||
|
num_rules=max_acl_rules,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -846,6 +850,7 @@ class UC2BlueObservation(AbstractObservation):
|
|||||||
num_services_per_node = config["num_services_per_node"]
|
num_services_per_node = config["num_services_per_node"]
|
||||||
num_folders_per_node = config["num_folders_per_node"]
|
num_folders_per_node = config["num_folders_per_node"]
|
||||||
num_files_per_folder = config["num_files_per_folder"]
|
num_files_per_folder = config["num_files_per_folder"]
|
||||||
|
num_nics_per_node = config["num_nics_per_node"]
|
||||||
nodes = [
|
nodes = [
|
||||||
NodeObservation.from_config(
|
NodeObservation.from_config(
|
||||||
config=n,
|
config=n,
|
||||||
@@ -853,6 +858,7 @@ class UC2BlueObservation(AbstractObservation):
|
|||||||
num_services_per_node=num_services_per_node,
|
num_services_per_node=num_services_per_node,
|
||||||
num_folders_per_node=num_folders_per_node,
|
num_folders_per_node=num_folders_per_node,
|
||||||
num_files_per_folder=num_files_per_folder,
|
num_files_per_folder=num_files_per_folder,
|
||||||
|
num_nics_per_node=num_nics_per_node,
|
||||||
)
|
)
|
||||||
for n in node_configs
|
for n in node_configs
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user