#1355 - Carried out full renaming in node.py, active_node.py, passive_node.py, and service_node.py to make params and variable names explicit.
- Made the same renaming in the yaml laydown config files. - Added Type hints wherever I've been. - Added a custom NodeType in custom_typing.py to encompass the Union of ActiveNode, PassiveNode, ServiceNode.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""A class that implements the access control list implementation for the network."""
|
||||
from typing import Dict
|
||||
|
||||
from primaite.acl.acl_rule import ACLRule
|
||||
|
||||
@@ -9,7 +10,7 @@ class AccessControlList:
|
||||
|
||||
def __init__(self):
|
||||
"""Init."""
|
||||
self.acl = {} # A dictionary of ACL Rules
|
||||
self.acl: Dict[str, AccessControlList] = {} # A dictionary of ACL Rules
|
||||
|
||||
def check_address_match(self, _rule, _source_ip_address, _dest_ip_address):
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""The config class."""
|
||||
|
||||
|
||||
class config_values_main(object):
|
||||
class ConfigValuesMain(object):
|
||||
"""Class to hold main config values."""
|
||||
|
||||
def __init__(self):
|
||||
@@ -23,7 +23,7 @@ class config_values_main(object):
|
||||
# Reward values
|
||||
# Generic
|
||||
self.all_ok = 0
|
||||
# Node Operating State
|
||||
# Node Hardware State
|
||||
self.off_should_be_on = 0
|
||||
self.off_should_be_resetting = 0
|
||||
self.on_should_be_off = 0
|
||||
@@ -31,7 +31,7 @@ class config_values_main(object):
|
||||
self.resetting_should_be_on = 0
|
||||
self.resetting_should_be_off = 0
|
||||
self.resetting = 0
|
||||
# Node O/S or Service State
|
||||
# Node Software or Service State
|
||||
self.good_should_be_patching = 0
|
||||
self.good_should_be_compromised = 0
|
||||
self.good_should_be_overwhelmed = 0
|
||||
|
||||
8
src/primaite/common/custom_typing.py
Normal file
8
src/primaite/common/custom_typing.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from typing import Type, Union
|
||||
|
||||
from primaite.nodes.active_node import ActiveNode
|
||||
from primaite.nodes.passive_node import PassiveNode
|
||||
from primaite.nodes.service_node import ServiceNode
|
||||
|
||||
NodeType: Type = Union[ActiveNode, PassiveNode, ServiceNode]
|
||||
"""A Union of ActiveNode, PassiveNode, and ServiceNode."""
|
||||
@@ -4,7 +4,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class TYPE(Enum):
|
||||
class NodeType(Enum):
|
||||
"""Node type enumeration."""
|
||||
|
||||
CCTV = 1
|
||||
@@ -19,7 +19,7 @@ class TYPE(Enum):
|
||||
SERVER = 10
|
||||
|
||||
|
||||
class PRIORITY(Enum):
|
||||
class Priority(Enum):
|
||||
"""Node priority enumeration."""
|
||||
|
||||
P1 = 1
|
||||
@@ -29,7 +29,7 @@ class PRIORITY(Enum):
|
||||
P5 = 5
|
||||
|
||||
|
||||
class HARDWARE_STATE(Enum):
|
||||
class HardwareState(Enum):
|
||||
"""Node hardware state enumeration."""
|
||||
|
||||
ON = 1
|
||||
@@ -37,8 +37,8 @@ class HARDWARE_STATE(Enum):
|
||||
RESETTING = 3
|
||||
|
||||
|
||||
class SOFTWARE_STATE(Enum):
|
||||
"""O/S or Service state enumeration."""
|
||||
class SoftwareState(Enum):
|
||||
"""Software or Service state enumeration."""
|
||||
|
||||
GOOD = 1
|
||||
PATCHING = 2
|
||||
@@ -46,7 +46,7 @@ class SOFTWARE_STATE(Enum):
|
||||
OVERWHELMED = 4
|
||||
|
||||
|
||||
class NODE_POL_TYPE(Enum):
|
||||
class NodePOLType(Enum):
|
||||
"""Node Pattern of Life type enumeration."""
|
||||
|
||||
OPERATING = 1
|
||||
@@ -55,7 +55,7 @@ class NODE_POL_TYPE(Enum):
|
||||
FILE = 4
|
||||
|
||||
|
||||
class NODE_POL_INITIATOR(Enum):
|
||||
class NodePOLInitiator(Enum):
|
||||
"""Node Pattern of Life initiator enumeration."""
|
||||
|
||||
DIRECT = 1
|
||||
@@ -63,7 +63,7 @@ class NODE_POL_INITIATOR(Enum):
|
||||
SERVICE = 3
|
||||
|
||||
|
||||
class PROTOCOL(Enum):
|
||||
class Protocol(Enum):
|
||||
"""Service protocol enumeration."""
|
||||
|
||||
LDAP = 0
|
||||
@@ -76,14 +76,14 @@ class PROTOCOL(Enum):
|
||||
NONE = 7
|
||||
|
||||
|
||||
class ACTION_TYPE(Enum):
|
||||
class ActionType(Enum):
|
||||
"""Action type enumeration."""
|
||||
|
||||
NODE = 0
|
||||
ACL = 1
|
||||
|
||||
|
||||
class FILE_SYSTEM_STATE(Enum):
|
||||
class FileSystemState(Enum):
|
||||
"""File System State."""
|
||||
|
||||
GOOD = 1
|
||||
|
||||
@@ -1,83 +1,28 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""The Service class."""
|
||||
|
||||
from primaite.common.enums import SOFTWARE_STATE
|
||||
from primaite.common.enums import SoftwareState
|
||||
|
||||
|
||||
class Service(object):
|
||||
"""Service class."""
|
||||
|
||||
def __init__(self, _name, _port, _state):
|
||||
def __init__(self, name: str, port: str, software_state: SoftwareState):
|
||||
"""
|
||||
Init.
|
||||
|
||||
Args:
|
||||
_name: The service name
|
||||
_port: The service port
|
||||
_state: The service state
|
||||
:param name: The service name.
|
||||
:param port: The service port.
|
||||
:param software_state: The service SoftwareState.
|
||||
"""
|
||||
self.name = _name
|
||||
self.port = _port
|
||||
self.state = _state
|
||||
self.name = name
|
||||
self.port = port
|
||||
self.software_state = software_state
|
||||
self.patching_count = 0
|
||||
|
||||
def set_name(self, _name):
|
||||
"""
|
||||
Sets the service name.
|
||||
|
||||
Args:
|
||||
_name: The service name
|
||||
"""
|
||||
self.name = _name
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Gets the service name.
|
||||
|
||||
Returns:
|
||||
The service name
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def set_port(self, _port):
|
||||
"""
|
||||
Sets the service port.
|
||||
|
||||
Args:
|
||||
_port: The service port
|
||||
"""
|
||||
self.port = _port
|
||||
|
||||
def get_port(self):
|
||||
"""
|
||||
Gets the service port.
|
||||
|
||||
Returns:
|
||||
The service port
|
||||
"""
|
||||
return self.port
|
||||
|
||||
def set_state(self, _state):
|
||||
"""
|
||||
Sets the service state.
|
||||
|
||||
Args:
|
||||
_state: The service state
|
||||
"""
|
||||
self.state = _state
|
||||
|
||||
def get_state(self):
|
||||
"""
|
||||
Gets the service state.
|
||||
|
||||
Returns:
|
||||
The service state
|
||||
"""
|
||||
return self.state
|
||||
|
||||
def reduce_patching_count(self):
|
||||
"""Reduces the patching count for the service."""
|
||||
self.patching_count -= 1
|
||||
if self.patching_count <= 0:
|
||||
self.patching_count = 0
|
||||
self.state = SOFTWARE_STATE.GOOD
|
||||
self.software_state = SoftwareState.GOOD
|
||||
|
||||
@@ -9,77 +9,77 @@
|
||||
serviceList:
|
||||
- name: TCP
|
||||
- itemType: NODE
|
||||
id: '1'
|
||||
node_id: '1'
|
||||
name: PC1
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.2
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.2
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '2'
|
||||
node_id: '2'
|
||||
name: SERVER
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.3
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.3
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '3'
|
||||
node_id: '3'
|
||||
name: PC2
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.4
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.4
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '4'
|
||||
node_id: '4'
|
||||
name: SWITCH1
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.5
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.5
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '5'
|
||||
node_id: '5'
|
||||
name: SWITCH2
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.6
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.6
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '6'
|
||||
node_id: '6'
|
||||
name: SWITCH3
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.7
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.7
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: LINK
|
||||
id: '7'
|
||||
name: link1
|
||||
|
||||
@@ -9,133 +9,133 @@
|
||||
serviceList:
|
||||
- name: TCP
|
||||
- itemType: NODE
|
||||
id: '1'
|
||||
node_id: '1'
|
||||
name: PC1
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.11
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.11
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '2'
|
||||
node_id: '2'
|
||||
name: PC2
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.12
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.12
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '3'
|
||||
node_id: '3'
|
||||
name: PC3
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.13
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.13
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '4'
|
||||
node_id: '4'
|
||||
name: PC4
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.20.14
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.20.14
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '5'
|
||||
node_id: '5'
|
||||
name: SWITCH1
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.2
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.2
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '6'
|
||||
node_id: '6'
|
||||
name: IDS
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.4
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.4
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '7'
|
||||
node_id: '7'
|
||||
name: SWITCH2
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.3
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.3
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '8'
|
||||
node_id: '8'
|
||||
name: LOP1
|
||||
baseType: SERVICE
|
||||
nodeType: LOP
|
||||
node_class: SERVICE
|
||||
node_type: LOP
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.12
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.12
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '9'
|
||||
node_id: '9'
|
||||
name: SERVER1
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.14
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.14
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '10'
|
||||
node_id: '10'
|
||||
name: SERVER2
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.20.15
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.20.15
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
|
||||
@@ -9,53 +9,53 @@
|
||||
serviceList:
|
||||
- name: TCP
|
||||
- itemType: NODE
|
||||
id: '1'
|
||||
node_id: '1'
|
||||
name: PC1
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.2
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.2
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '2'
|
||||
node_id: '2'
|
||||
name: PC2
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.3
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.3
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '3'
|
||||
node_id: '3'
|
||||
name: SWITCH1
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.1
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.1
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '4'
|
||||
node_id: '4'
|
||||
name: SERVER1
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.4
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.4
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
|
||||
@@ -13,15 +13,15 @@
|
||||
- name: TCP_SQL
|
||||
- name: UDP
|
||||
- itemType: NODE
|
||||
id: '1'
|
||||
node_id: '1'
|
||||
name: CLIENT_1
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.11
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.11
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -30,39 +30,39 @@
|
||||
port: '53'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '2'
|
||||
node_id: '2'
|
||||
name: CLIENT_2
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.12
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.12
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '3'
|
||||
node_id: '3'
|
||||
name: SWITCH_1
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.1
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.1
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '4'
|
||||
node_id: '4'
|
||||
name: SECURITY_SUITE
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.10
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.10
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -71,15 +71,15 @@
|
||||
port: '53'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '5'
|
||||
node_id: '5'
|
||||
name: MANAGEMENT_CONSOLE
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.12
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.12
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -88,25 +88,25 @@
|
||||
port: '53'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '6'
|
||||
node_id: '6'
|
||||
name: SWITCH_2
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.2.1
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.2.1
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '7'
|
||||
node_id: '7'
|
||||
name: WEB_SERVER
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.2.10
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.2.10
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -115,15 +115,15 @@
|
||||
port: '1433'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '8'
|
||||
node_id: '8'
|
||||
name: DATABASE_SERVER
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.2.14
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.2.14
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -135,15 +135,15 @@
|
||||
port: '53'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '9'
|
||||
node_id: '9'
|
||||
name: BACKUP_SERVER
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.2.16
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.2.16
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
|
||||
@@ -13,15 +13,15 @@
|
||||
- name: TCP_SQL
|
||||
- name: UDP
|
||||
- itemType: NODE
|
||||
id: '1'
|
||||
node_id: '1'
|
||||
name: CLIENT_1
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.11
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.11
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -30,39 +30,39 @@
|
||||
port: '53'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '2'
|
||||
node_id: '2'
|
||||
name: CLIENT_2
|
||||
baseType: SERVICE
|
||||
nodeType: COMPUTER
|
||||
node_class: SERVICE
|
||||
node_type: COMPUTER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.12
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.12
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '3'
|
||||
node_id: '3'
|
||||
name: SWITCH_1
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.10.1
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.10.1
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '4'
|
||||
node_id: '4'
|
||||
name: SECURITY_SUITE
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.10
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.10
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -71,15 +71,15 @@
|
||||
port: '53'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '5'
|
||||
node_id: '5'
|
||||
name: MANAGEMENT_CONSOLE
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.1.12
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.1.12
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -88,25 +88,25 @@
|
||||
port: '53'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '6'
|
||||
node_id: '6'
|
||||
name: SWITCH_2
|
||||
baseType: ACTIVE
|
||||
nodeType: SWITCH
|
||||
node_class: ACTIVE
|
||||
node_type: SWITCH
|
||||
priority: P2
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.2.1
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.2.1
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
- itemType: NODE
|
||||
id: '7'
|
||||
node_id: '7'
|
||||
name: WEB_SERVER
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.2.10
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.2.10
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -115,15 +115,15 @@
|
||||
port: '1433'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '8'
|
||||
node_id: '8'
|
||||
name: DATABASE_SERVER
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.2.14
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.2.14
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
@@ -135,15 +135,15 @@
|
||||
port: '53'
|
||||
state: GOOD
|
||||
- itemType: NODE
|
||||
id: '9'
|
||||
node_id: '9'
|
||||
name: BACKUP_SERVER
|
||||
baseType: SERVICE
|
||||
nodeType: SERVER
|
||||
node_class: SERVICE
|
||||
node_type: SERVER
|
||||
priority: P5
|
||||
hardwareState: 'ON'
|
||||
ipAddress: 192.168.2.16
|
||||
softwareState: GOOD
|
||||
fileSystemState: GOOD
|
||||
hardware_state: 'ON'
|
||||
ip_address: 192.168.2.16
|
||||
software_state: GOOD
|
||||
file_system_state: GOOD
|
||||
services:
|
||||
- name: TCP
|
||||
port: '80'
|
||||
|
||||
@@ -26,7 +26,7 @@ observationSpaceHighValue: 1000000000
|
||||
# Reward values
|
||||
# Generic
|
||||
allOk: 0
|
||||
# Node Operating State
|
||||
# Node Hardware State
|
||||
offShouldBeOn: -10
|
||||
offShouldBeResetting: -5
|
||||
onShouldBeOff: -2
|
||||
@@ -34,7 +34,7 @@ onShouldBeResetting: -5
|
||||
resettingShouldBeOn: -5
|
||||
resettingShouldBeOff: -2
|
||||
resetting: -3
|
||||
# Node O/S or Service State
|
||||
# Node Software or Service State
|
||||
goodShouldBePatching: 2
|
||||
goodShouldBeCompromised: 5
|
||||
goodShouldBeOverwhelmed: 5
|
||||
|
||||
@@ -6,6 +6,7 @@ import csv
|
||||
import logging
|
||||
import os.path
|
||||
from datetime import datetime
|
||||
from typing import Dict
|
||||
|
||||
import networkx as nx
|
||||
import numpy as np
|
||||
@@ -14,20 +15,22 @@ from gym import Env, spaces
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from primaite.acl.access_control_list import AccessControlList
|
||||
from primaite.common.custom_typing import NodeType
|
||||
from primaite.common.enums import (
|
||||
ACTION_TYPE,
|
||||
FILE_SYSTEM_STATE,
|
||||
HARDWARE_STATE,
|
||||
NODE_POL_INITIATOR,
|
||||
NODE_POL_TYPE,
|
||||
PRIORITY,
|
||||
SOFTWARE_STATE,
|
||||
TYPE,
|
||||
ActionType,
|
||||
FileSystemState,
|
||||
HardwareState,
|
||||
NodePOLInitiator,
|
||||
NodePOLType,
|
||||
NodeType,
|
||||
Priority,
|
||||
SoftwareState,
|
||||
)
|
||||
from primaite.common.service import Service
|
||||
from primaite.environment.reward import calculate_reward_function
|
||||
from primaite.links.link import Link
|
||||
from primaite.nodes.active_node import ActiveNode
|
||||
from primaite.nodes.node import Node
|
||||
from primaite.nodes.node_state_instruction_green import NodeStateInstructionGreen
|
||||
from primaite.nodes.node_state_instruction_red import NodeStateInstructionRed
|
||||
from primaite.nodes.passive_node import PassiveNode
|
||||
@@ -77,19 +80,19 @@ class Primaite(Env):
|
||||
self.agent_identifier = self.config_values.agent_identifier
|
||||
|
||||
# Create a dictionary to hold all the nodes
|
||||
self.nodes = {}
|
||||
self.nodes: Dict[str, NodeType] = {}
|
||||
|
||||
# Create a dictionary to hold a reference set of nodes
|
||||
self.nodes_reference = {}
|
||||
self.nodes_reference: Dict[str, NodeType] = {}
|
||||
|
||||
# Create a dictionary to hold all the links
|
||||
self.links = {}
|
||||
self.links: Dict[str, Link] = {}
|
||||
|
||||
# Create a dictionary to hold a reference set of links
|
||||
self.links_reference = {}
|
||||
self.links_reference: Dict[str, Link] = {}
|
||||
|
||||
# Create a dictionary to hold all the green IERs (this will come from an external source)
|
||||
self.green_iers = {}
|
||||
self.green_iers: Dict[str, IER] = {}
|
||||
|
||||
# Create a dictionary to hold all the node PoLs (this will come from an external source)
|
||||
self.node_pol = {}
|
||||
@@ -190,8 +193,8 @@ class Primaite(Env):
|
||||
# For each item, we send:
|
||||
# - [For Nodes] | [For Links]
|
||||
# - node ID | link ID
|
||||
# - operating state | N/A
|
||||
# - operating system state | N/A
|
||||
# - hardware state | N/A
|
||||
# - Software State | N/A
|
||||
# - file system state | N/A
|
||||
# - service A state | service A loading
|
||||
# - service B state | service B loading
|
||||
@@ -205,7 +208,7 @@ class Primaite(Env):
|
||||
# observation space
|
||||
num_items = self.num_links + self.num_nodes
|
||||
# Set the number of observation parameters, being # of services plus id,
|
||||
# operating state, file system state and O/S state (i.e. 4)
|
||||
# hardware state, file system state and SoftwareState (i.e. 4)
|
||||
self.num_observation_parameters = (
|
||||
self.num_services + self.OBSERVATION_SPACE_FIXED_PARAMETERS
|
||||
)
|
||||
@@ -222,13 +225,13 @@ class Primaite(Env):
|
||||
self.env_obs = np.zeros(self.observation_shape, dtype=np.int64)
|
||||
|
||||
# Define Action Space - depends on action space type (Node or ACL)
|
||||
if self.action_type == ACTION_TYPE.NODE:
|
||||
if self.action_type == ActionType.NODE:
|
||||
_LOGGER.info("Action space type NODE selected")
|
||||
# Terms (for node action space):
|
||||
# [0, num nodes] - node ID (0 = nothing, node ID)
|
||||
# [0, 4] - what property it's acting on (0 = nothing, state, o/s state, service state, file system state)
|
||||
# [0, 3] - action on property (0 = nothing, On / Scan, Off / Repair, Reset / Patch / Restore)
|
||||
# [0, num services] - resolves to service ID (0 = nothing, resolves to service)
|
||||
# [0, 4] - what property it's acting on (0 = nothing, state, SoftwareState, service state, file system state) # noqa
|
||||
# [0, 3] - action on property (0 = nothing, On / Scan, Off / Repair, Reset / Patch / Restore) # noqa
|
||||
# [0, num services] - resolves to service ID (0 = nothing, resolves to service) # noqa
|
||||
self.action_space = spaces.MultiDiscrete(
|
||||
[
|
||||
self.num_nodes,
|
||||
@@ -402,7 +405,7 @@ class Primaite(Env):
|
||||
self.step_count,
|
||||
self.config_values,
|
||||
)
|
||||
print(f" Step {self.step_count} Reward: {str(reward)}")
|
||||
# print(f" Step {self.step_count} Reward: {str(reward)}")
|
||||
self.total_reward += reward
|
||||
if self.step_count == self.episode_steps:
|
||||
self.average_reward = self.total_reward / self.step_count
|
||||
@@ -441,7 +444,7 @@ class Primaite(Env):
|
||||
"""Output the link status of all links to the console."""
|
||||
for link_key, link_value in self.links.items():
|
||||
print("Link ID: " + link_value.get_id())
|
||||
for protocol in link_value.get_protocol_list():
|
||||
for protocol in link_value.protocol_list:
|
||||
print(
|
||||
" Protocol: "
|
||||
+ protocol.get_name().name
|
||||
@@ -457,7 +460,7 @@ class Primaite(Env):
|
||||
_action: The action space from the agent
|
||||
"""
|
||||
# At the moment, actions are only affecting nodes
|
||||
if self.action_type == ACTION_TYPE.NODE:
|
||||
if self.action_type == ActionType.NODE:
|
||||
self.apply_actions_to_nodes(_action)
|
||||
else:
|
||||
self.apply_actions_to_acl(_action)
|
||||
@@ -484,32 +487,32 @@ class Primaite(Env):
|
||||
# This is the do nothing action
|
||||
return
|
||||
elif node_property == 1:
|
||||
# This is an action on the node Operating State
|
||||
# This is an action on the node Hardware State
|
||||
if property_action == 0:
|
||||
# Do nothing
|
||||
return
|
||||
elif property_action == 1:
|
||||
# Turn on (only applicable if it's OFF, not if it's patching)
|
||||
if node.get_state() == HARDWARE_STATE.OFF:
|
||||
if node.hardware_state == HardwareState.OFF:
|
||||
node.turn_on()
|
||||
elif property_action == 2:
|
||||
# Turn off
|
||||
node.turn_off()
|
||||
elif property_action == 3:
|
||||
# Reset (only applicable if it's ON)
|
||||
if node.get_state() == HARDWARE_STATE.ON:
|
||||
if node.hardware_state == HardwareState.ON:
|
||||
node.reset()
|
||||
else:
|
||||
return
|
||||
elif node_property == 2:
|
||||
if isinstance(node, ActiveNode) or isinstance(node, ServiceNode):
|
||||
# This is an action on the node Operating System State
|
||||
# This is an action on the node Software State
|
||||
if property_action == 0:
|
||||
# Do nothing
|
||||
return
|
||||
elif property_action == 1:
|
||||
# Patch (valid action if it's good or compromised)
|
||||
node.set_os_state(SOFTWARE_STATE.PATCHING)
|
||||
node.software_state = SoftwareState.PATCHING
|
||||
else:
|
||||
# Node is not of Active or Service Type
|
||||
return
|
||||
@@ -523,7 +526,7 @@ class Primaite(Env):
|
||||
elif property_action == 1:
|
||||
# Patch (valid action if it's good or compromised)
|
||||
node.set_service_state(
|
||||
self.services_list[service_index], SOFTWARE_STATE.PATCHING
|
||||
self.services_list[service_index], SoftwareState.PATCHING
|
||||
)
|
||||
else:
|
||||
# Node is not of Service Type
|
||||
@@ -540,14 +543,11 @@ class Primaite(Env):
|
||||
elif property_action == 2:
|
||||
# Repair
|
||||
# You cannot repair a destroyed file system - it needs restoring
|
||||
if (
|
||||
node.get_file_system_state_actual()
|
||||
!= FILE_SYSTEM_STATE.DESTROYED
|
||||
):
|
||||
node.set_file_system_state(FILE_SYSTEM_STATE.REPAIRING)
|
||||
if node.file_system_state_actual != FileSystemState.DESTROYED:
|
||||
node.set_file_system_state(FileSystemState.REPAIRING)
|
||||
elif property_action == 3:
|
||||
# Restore
|
||||
node.set_file_system_state(FILE_SYSTEM_STATE.RESTORING)
|
||||
node.set_file_system_state(FileSystemState.RESTORING)
|
||||
else:
|
||||
# Node is not of Active Type
|
||||
return
|
||||
@@ -584,7 +584,7 @@ class Primaite(Env):
|
||||
else:
|
||||
node = list(self.nodes.values())[action_source_ip - 1]
|
||||
if isinstance(node, ServiceNode) or isinstance(node, ActiveNode):
|
||||
acl_rule_source = node.get_ip_address()
|
||||
acl_rule_source = node.ip_address
|
||||
else:
|
||||
return
|
||||
# Destination IP value
|
||||
@@ -593,7 +593,7 @@ class Primaite(Env):
|
||||
else:
|
||||
node = list(self.nodes.values())[action_destination_ip - 1]
|
||||
if isinstance(node, ServiceNode) or isinstance(node, ActiveNode):
|
||||
acl_rule_destination = node.get_ip_address()
|
||||
acl_rule_destination = node.ip_address
|
||||
else:
|
||||
return
|
||||
# Protocol value
|
||||
@@ -636,13 +636,13 @@ class Primaite(Env):
|
||||
e.g. reset / patching status
|
||||
"""
|
||||
for node_key, node in self.nodes.items():
|
||||
if node.get_state() == HARDWARE_STATE.RESETTING:
|
||||
if node.hardware_state == HardwareState.RESETTING:
|
||||
node.update_resetting_status()
|
||||
else:
|
||||
pass
|
||||
if isinstance(node, ActiveNode) or isinstance(node, ServiceNode):
|
||||
node.update_file_system_state()
|
||||
if node.get_os_state() == SOFTWARE_STATE.PATCHING:
|
||||
if node.software_state == SoftwareState.PATCHING:
|
||||
node.update_os_patching_status()
|
||||
else:
|
||||
pass
|
||||
@@ -654,13 +654,13 @@ class Primaite(Env):
|
||||
pass
|
||||
|
||||
for node_key, node in self.nodes_reference.items():
|
||||
if node.get_state() == HARDWARE_STATE.RESETTING:
|
||||
if node.hardware_state == HardwareState.RESETTING:
|
||||
node.update_resetting_status()
|
||||
else:
|
||||
pass
|
||||
if isinstance(node, ActiveNode) or isinstance(node, ServiceNode):
|
||||
node.update_file_system_state()
|
||||
if node.get_os_state() == SOFTWARE_STATE.PATCHING:
|
||||
if node.software_state == SoftwareState.PATCHING:
|
||||
node.update_os_patching_status()
|
||||
else:
|
||||
pass
|
||||
@@ -677,13 +677,11 @@ class Primaite(Env):
|
||||
|
||||
# Do nodes first
|
||||
for node_key, node in self.nodes.items():
|
||||
self.env_obs[item_index][0] = int(node.get_id())
|
||||
self.env_obs[item_index][1] = node.get_state().value
|
||||
self.env_obs[item_index][0] = int(node.node_id)
|
||||
self.env_obs[item_index][1] = node.hardware_state.value
|
||||
if isinstance(node, ActiveNode) or isinstance(node, ServiceNode):
|
||||
self.env_obs[item_index][2] = node.get_os_state().value
|
||||
self.env_obs[item_index][
|
||||
3
|
||||
] = node.get_file_system_state_observed().value
|
||||
self.env_obs[item_index][2] = node.software_state.value
|
||||
self.env_obs[item_index][3] = node.file_system_state_observed.value
|
||||
else:
|
||||
self.env_obs[item_index][2] = 0
|
||||
self.env_obs[item_index][3] = 0
|
||||
@@ -768,14 +766,14 @@ class Primaite(Env):
|
||||
item: A config data item
|
||||
"""
|
||||
# All nodes have these parameters
|
||||
node_id = item["id"]
|
||||
node_id = item["node_id"]
|
||||
node_name = item["name"]
|
||||
node_base_type = item["baseType"]
|
||||
node_type = TYPE[item["nodeType"]]
|
||||
node_priority = PRIORITY[item["priority"]]
|
||||
node_hardware_state = HARDWARE_STATE[item["hardwareState"]]
|
||||
node_class = item["node_class"]
|
||||
node_type = NodeType[item["node_type"]]
|
||||
node_priority = Priority[item["priority"]]
|
||||
node_hardware_state = HardwareState[item["hardware_state"]]
|
||||
|
||||
if node_base_type == "PASSIVE":
|
||||
if node_class == "PASSIVE":
|
||||
node = PassiveNode(
|
||||
node_id,
|
||||
node_name,
|
||||
@@ -784,11 +782,11 @@ class Primaite(Env):
|
||||
node_hardware_state,
|
||||
self.config_values,
|
||||
)
|
||||
elif node_base_type == "ACTIVE":
|
||||
# Active nodes have IP address, operating system state and file system state
|
||||
node_ip_address = item["ipAddress"]
|
||||
node_software_state = SOFTWARE_STATE[item["softwareState"]]
|
||||
node_file_system_state = FILE_SYSTEM_STATE[item["fileSystemState"]]
|
||||
elif node_class == "ACTIVE":
|
||||
# Active nodes have IP address, Software State and file system state
|
||||
node_ip_address = item["ip_address"]
|
||||
node_software_state = SoftwareState[item["software_state"]]
|
||||
node_file_system_state = FileSystemState[item["file_system_state"]]
|
||||
node = ActiveNode(
|
||||
node_id,
|
||||
node_name,
|
||||
@@ -800,11 +798,11 @@ class Primaite(Env):
|
||||
node_file_system_state,
|
||||
self.config_values,
|
||||
)
|
||||
elif node_base_type == "SERVICE":
|
||||
# Service nodes have IP address, operating system state, file system state and list of services
|
||||
node_ip_address = item["ipAddress"]
|
||||
node_software_state = SOFTWARE_STATE[item["softwareState"]]
|
||||
node_file_system_state = FILE_SYSTEM_STATE[item["fileSystemState"]]
|
||||
elif node_class == "SERVICE":
|
||||
# Service nodes have IP address, Software State, file system state and list of services
|
||||
node_ip_address = item["ip_address"]
|
||||
node_software_state = SoftwareState[item["software_state"]]
|
||||
node_file_system_state = FileSystemState[item["file_system_state"]]
|
||||
node = ServiceNode(
|
||||
node_id,
|
||||
node_name,
|
||||
@@ -820,7 +818,7 @@ class Primaite(Env):
|
||||
for service in node_services:
|
||||
service_protocol = service["name"]
|
||||
service_port = service["port"]
|
||||
service_state = SOFTWARE_STATE[service["state"]]
|
||||
service_state = SoftwareState[service["state"]]
|
||||
node.add_service(Service(service_protocol, service_port, service_state))
|
||||
else:
|
||||
# Bad formatting
|
||||
@@ -841,7 +839,7 @@ class Primaite(Env):
|
||||
# Add node to network (reference)
|
||||
self.network_reference.add_nodes_from([node_ref])
|
||||
|
||||
def create_link(self, item):
|
||||
def create_link(self, item: Dict):
|
||||
"""
|
||||
Creates a link from config data.
|
||||
|
||||
@@ -854,8 +852,8 @@ class Primaite(Env):
|
||||
link_source = item["source"]
|
||||
link_destination = item["destination"]
|
||||
|
||||
source_node = self.nodes[link_source]
|
||||
dest_node = self.nodes[link_destination]
|
||||
source_node: Node = self.nodes[link_source]
|
||||
dest_node: Node = self.nodes[link_destination]
|
||||
|
||||
# Add link to network
|
||||
self.network.add_edge(source_node, dest_node, id=link_name)
|
||||
@@ -864,14 +862,14 @@ class Primaite(Env):
|
||||
self.links[link_name] = Link(
|
||||
link_id,
|
||||
link_bandwidth,
|
||||
source_node.get_name(),
|
||||
dest_node.get_name(),
|
||||
source_node.name,
|
||||
dest_node.name,
|
||||
self.services_list,
|
||||
)
|
||||
|
||||
# Reference
|
||||
source_node_ref = self.nodes_reference[link_source]
|
||||
dest_node_ref = self.nodes_reference[link_destination]
|
||||
source_node_ref: Node = self.nodes_reference[link_source]
|
||||
dest_node_ref: Node = self.nodes_reference[link_destination]
|
||||
|
||||
# Add link to network (reference)
|
||||
self.network_reference.add_edge(source_node_ref, dest_node_ref, id=link_name)
|
||||
@@ -880,8 +878,8 @@ class Primaite(Env):
|
||||
self.links_reference[link_name] = Link(
|
||||
link_id,
|
||||
link_bandwidth,
|
||||
source_node_ref.get_name(),
|
||||
dest_node_ref.get_name(),
|
||||
source_node_ref.name,
|
||||
dest_node_ref.name,
|
||||
self.services_list,
|
||||
)
|
||||
|
||||
@@ -956,18 +954,18 @@ class Primaite(Env):
|
||||
pol_start_step = item["startStep"]
|
||||
pol_end_step = item["endStep"]
|
||||
pol_node = item["nodeId"]
|
||||
pol_type = NODE_POL_TYPE[item["type"]]
|
||||
pol_type = NodePOLType[item["type"]]
|
||||
|
||||
# State depends on whether this is Operating, O/S, file system or Service PoL type
|
||||
if pol_type == NODE_POL_TYPE.OPERATING:
|
||||
pol_state = HARDWARE_STATE[item["state"]]
|
||||
# State depends on whether this is Operating, Software, file system or Service PoL type
|
||||
if pol_type == NodePOLType.OPERATING:
|
||||
pol_state = HardwareState[item["state"]]
|
||||
pol_protocol = ""
|
||||
elif pol_type == NODE_POL_TYPE.FILE:
|
||||
pol_state = FILE_SYSTEM_STATE[item["state"]]
|
||||
elif pol_type == NodePOLType.FILE:
|
||||
pol_state = FileSystemState[item["state"]]
|
||||
pol_protocol = ""
|
||||
else:
|
||||
pol_protocol = item["protocol"]
|
||||
pol_state = SOFTWARE_STATE[item["state"]]
|
||||
pol_state = SoftwareState[item["state"]]
|
||||
|
||||
self.node_pol[pol_id] = NodeStateInstructionGreen(
|
||||
pol_id,
|
||||
@@ -990,17 +988,17 @@ class Primaite(Env):
|
||||
pol_start_step = item["startStep"]
|
||||
pol_end_step = item["endStep"]
|
||||
pol_target_node_id = item["targetNodeId"]
|
||||
pol_initiator = NODE_POL_INITIATOR[item["initiator"]]
|
||||
pol_type = NODE_POL_TYPE[item["type"]]
|
||||
pol_initiator = NodePOLInitiator[item["initiator"]]
|
||||
pol_type = NodePOLType[item["type"]]
|
||||
pol_protocol = item["protocol"]
|
||||
|
||||
# State depends on whether this is Operating, O/S, file system or Service PoL type
|
||||
if pol_type == NODE_POL_TYPE.OPERATING:
|
||||
pol_state = HARDWARE_STATE[item["state"]]
|
||||
elif pol_type == NODE_POL_TYPE.FILE:
|
||||
pol_state = FILE_SYSTEM_STATE[item["state"]]
|
||||
# State depends on whether this is Operating, Software, file system or Service PoL type
|
||||
if pol_type == NodePOLType.OPERATING:
|
||||
pol_state = HardwareState[item["state"]]
|
||||
elif pol_type == NodePOLType.FILE:
|
||||
pol_state = FileSystemState[item["state"]]
|
||||
else:
|
||||
pol_state = SOFTWARE_STATE[item["state"]]
|
||||
pol_state = SoftwareState[item["state"]]
|
||||
|
||||
pol_source_node_id = item["sourceNodeId"]
|
||||
pol_source_node_service = item["sourceNodeService"]
|
||||
@@ -1080,7 +1078,7 @@ class Primaite(Env):
|
||||
Args:
|
||||
item: A config data item representing action info
|
||||
"""
|
||||
self.action_type = ACTION_TYPE[action_info["type"]]
|
||||
self.action_type = ActionType[action_info["type"]]
|
||||
|
||||
def get_steps_info(self, steps_info):
|
||||
"""
|
||||
@@ -1126,38 +1124,38 @@ class Primaite(Env):
|
||||
item: A config data item
|
||||
"""
|
||||
# All nodes have these parameters
|
||||
node_id = item["id"]
|
||||
node_base_type = item["baseType"]
|
||||
node_hardware_state = HARDWARE_STATE[item["hardwareState"]]
|
||||
node_id = item["node_id"]
|
||||
node_class = item["node_class"]
|
||||
node_hardware_state: HardwareState = HardwareState[item["hardware_state"]]
|
||||
|
||||
node = self.nodes[node_id]
|
||||
node: NodeType = self.nodes[node_id]
|
||||
node_ref = self.nodes_reference[node_id]
|
||||
|
||||
# Reset the hardware state (common for all node types)
|
||||
node.set_state(node_hardware_state)
|
||||
node_ref.set_state(node_hardware_state)
|
||||
node.hardware_state = node_hardware_state
|
||||
node_ref.hardware_state = node_hardware_state
|
||||
|
||||
if node_base_type == "ACTIVE":
|
||||
# Active nodes have operating system state
|
||||
node_software_state = SOFTWARE_STATE[item["softwareState"]]
|
||||
node_file_system_state = FILE_SYSTEM_STATE[item["fileSystemState"]]
|
||||
node.set_os_state(node_software_state)
|
||||
node_ref.set_os_state(node_software_state)
|
||||
if node_class == "ACTIVE":
|
||||
# Active nodes have Software State
|
||||
node_software_state = SoftwareState[item["software_state"]]
|
||||
node_file_system_state = FileSystemState[item["file_system_state"]]
|
||||
node.software_state = node_software_state
|
||||
node_ref.software_state = node_software_state
|
||||
node.set_file_system_state(node_file_system_state)
|
||||
node_ref.set_file_system_state(node_file_system_state)
|
||||
elif node_base_type == "SERVICE":
|
||||
# Service nodes have operating system state and list of services
|
||||
node_software_state = SOFTWARE_STATE[item["softwareState"]]
|
||||
node_file_system_state = FILE_SYSTEM_STATE[item["fileSystemState"]]
|
||||
node.set_os_state(node_software_state)
|
||||
node_ref.set_os_state(node_software_state)
|
||||
elif node_class == "SERVICE":
|
||||
# Service nodes have Software State and list of services
|
||||
node_software_state = SoftwareState[item["software_state"]]
|
||||
node_file_system_state = FileSystemState[item["file_system_state"]]
|
||||
node.software_state = node_software_state
|
||||
node_ref.software_state = node_software_state
|
||||
node.set_file_system_state(node_file_system_state)
|
||||
node_ref.set_file_system_state(node_file_system_state)
|
||||
# Update service states
|
||||
node_services = item["services"]
|
||||
for service in node_services:
|
||||
service_protocol = service["name"]
|
||||
service_state = SOFTWARE_STATE[service["state"]]
|
||||
service_state = SoftwareState[service["state"]]
|
||||
# Update node service state
|
||||
node.set_service_state(service_protocol, service_state)
|
||||
# Update reference node service state
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""Implements reward function."""
|
||||
from primaite.common.enums import FILE_SYSTEM_STATE, HARDWARE_STATE, SOFTWARE_STATE
|
||||
from typing import Dict
|
||||
|
||||
from primaite.common.enums import FileSystemState, HardwareState, SoftwareState
|
||||
from primaite.common.service import Service
|
||||
from primaite.nodes.active_node import ActiveNode
|
||||
from primaite.nodes.service_node import ServiceNode
|
||||
|
||||
@@ -28,17 +31,17 @@ def calculate_reward_function(
|
||||
"""
|
||||
reward_value = 0
|
||||
|
||||
# For each node, compare operating state, o/s operating state, service states
|
||||
# For each node, compare hardware state, SoftwareState, service states
|
||||
for node_key, final_node in final_nodes.items():
|
||||
initial_node = initial_nodes[node_key]
|
||||
reference_node = reference_nodes[node_key]
|
||||
|
||||
# Operating State
|
||||
# Hardware State
|
||||
reward_value += score_node_operating_state(
|
||||
final_node, initial_node, reference_node, config_values
|
||||
)
|
||||
|
||||
# Operating System State
|
||||
# Software State
|
||||
if isinstance(final_node, ActiveNode) or isinstance(final_node, ServiceNode):
|
||||
reward_value += score_node_os_state(
|
||||
final_node, initial_node, reference_node, config_values
|
||||
@@ -80,7 +83,7 @@ def calculate_reward_function(
|
||||
|
||||
def score_node_operating_state(final_node, initial_node, reference_node, config_values):
|
||||
"""
|
||||
Calculates score relating to the operating state of a node.
|
||||
Calculates score relating to the hardware state of a node.
|
||||
|
||||
Args:
|
||||
final_node: The node after red and blue agents take effect
|
||||
@@ -89,9 +92,9 @@ def score_node_operating_state(final_node, initial_node, reference_node, config_
|
||||
config_values: Config values
|
||||
"""
|
||||
score = 0
|
||||
final_node_operating_state = final_node.get_state()
|
||||
initial_node_operating_state = initial_node.get_state()
|
||||
reference_node_operating_state = reference_node.get_state()
|
||||
final_node_operating_state = final_node.hardware_state
|
||||
initial_node_operating_state = initial_node.hardware_state
|
||||
reference_node_operating_state = reference_node.hardware_state
|
||||
|
||||
if final_node_operating_state == reference_node_operating_state:
|
||||
# All is well - we're no different from the reference situation
|
||||
@@ -99,26 +102,26 @@ def score_node_operating_state(final_node, initial_node, reference_node, config_
|
||||
else:
|
||||
# We're different from the reference situation
|
||||
# Need to compare initial and reference (current) state of node (i.e. at every step)
|
||||
if initial_node_operating_state == HARDWARE_STATE.ON:
|
||||
if reference_node_operating_state == HARDWARE_STATE.OFF:
|
||||
if initial_node_operating_state == HardwareState.ON:
|
||||
if reference_node_operating_state == HardwareState.OFF:
|
||||
score += config_values.off_should_be_on
|
||||
elif reference_node_operating_state == HARDWARE_STATE.RESETTING:
|
||||
elif reference_node_operating_state == HardwareState.RESETTING:
|
||||
score += config_values.resetting_should_be_on
|
||||
else:
|
||||
pass
|
||||
elif initial_node_operating_state == HARDWARE_STATE.OFF:
|
||||
if reference_node_operating_state == HARDWARE_STATE.ON:
|
||||
elif initial_node_operating_state == HardwareState.OFF:
|
||||
if reference_node_operating_state == HardwareState.ON:
|
||||
score += config_values.on_should_be_off
|
||||
elif reference_node_operating_state == HARDWARE_STATE.RESETTING:
|
||||
elif reference_node_operating_state == HardwareState.RESETTING:
|
||||
score += config_values.resetting_should_be_off
|
||||
else:
|
||||
pass
|
||||
elif initial_node_operating_state == HARDWARE_STATE.RESETTING:
|
||||
if reference_node_operating_state == HARDWARE_STATE.ON:
|
||||
elif initial_node_operating_state == HardwareState.RESETTING:
|
||||
if reference_node_operating_state == HardwareState.ON:
|
||||
score += config_values.on_should_be_resetting
|
||||
elif reference_node_operating_state == HARDWARE_STATE.OFF:
|
||||
elif reference_node_operating_state == HardwareState.OFF:
|
||||
score += config_values.off_should_be_resetting
|
||||
elif reference_node_operating_state == HARDWARE_STATE.RESETTING:
|
||||
elif reference_node_operating_state == HardwareState.RESETTING:
|
||||
score += config_values.resetting
|
||||
else:
|
||||
pass
|
||||
@@ -130,7 +133,7 @@ def score_node_operating_state(final_node, initial_node, reference_node, config_
|
||||
|
||||
def score_node_os_state(final_node, initial_node, reference_node, config_values):
|
||||
"""
|
||||
Calculates score relating to the operating system state of a node.
|
||||
Calculates score relating to the Software State of a node.
|
||||
|
||||
Args:
|
||||
final_node: The node after red and blue agents take effect
|
||||
@@ -139,9 +142,9 @@ def score_node_os_state(final_node, initial_node, reference_node, config_values)
|
||||
config_values: Config values
|
||||
"""
|
||||
score = 0
|
||||
final_node_os_state = final_node.get_os_state()
|
||||
initial_node_os_state = initial_node.get_os_state()
|
||||
reference_node_os_state = reference_node.get_os_state()
|
||||
final_node_os_state = final_node.software_state
|
||||
initial_node_os_state = initial_node.software_state
|
||||
reference_node_os_state = reference_node.software_state
|
||||
|
||||
if final_node_os_state == reference_node_os_state:
|
||||
# All is well - we're no different from the reference situation
|
||||
@@ -149,28 +152,28 @@ def score_node_os_state(final_node, initial_node, reference_node, config_values)
|
||||
else:
|
||||
# We're different from the reference situation
|
||||
# Need to compare initial and reference (current) state of node (i.e. at every step)
|
||||
if initial_node_os_state == SOFTWARE_STATE.GOOD:
|
||||
if reference_node_os_state == SOFTWARE_STATE.PATCHING:
|
||||
if initial_node_os_state == SoftwareState.GOOD:
|
||||
if reference_node_os_state == SoftwareState.PATCHING:
|
||||
score += config_values.patching_should_be_good
|
||||
elif reference_node_os_state == SOFTWARE_STATE.COMPROMISED:
|
||||
elif reference_node_os_state == SoftwareState.COMPROMISED:
|
||||
score += config_values.compromised_should_be_good
|
||||
else:
|
||||
pass
|
||||
elif initial_node_os_state == SOFTWARE_STATE.PATCHING:
|
||||
if reference_node_os_state == SOFTWARE_STATE.GOOD:
|
||||
elif initial_node_os_state == SoftwareState.PATCHING:
|
||||
if reference_node_os_state == SoftwareState.GOOD:
|
||||
score += config_values.good_should_be_patching
|
||||
elif reference_node_os_state == SOFTWARE_STATE.COMPROMISED:
|
||||
elif reference_node_os_state == SoftwareState.COMPROMISED:
|
||||
score += config_values.compromised_should_be_patching
|
||||
elif reference_node_os_state == SOFTWARE_STATE.PATCHING:
|
||||
elif reference_node_os_state == SoftwareState.PATCHING:
|
||||
score += config_values.patching
|
||||
else:
|
||||
pass
|
||||
elif initial_node_os_state == SOFTWARE_STATE.COMPROMISED:
|
||||
if reference_node_os_state == SOFTWARE_STATE.GOOD:
|
||||
elif initial_node_os_state == SoftwareState.COMPROMISED:
|
||||
if reference_node_os_state == SoftwareState.GOOD:
|
||||
score += config_values.good_should_be_compromised
|
||||
elif reference_node_os_state == SOFTWARE_STATE.PATCHING:
|
||||
elif reference_node_os_state == SoftwareState.PATCHING:
|
||||
score += config_values.patching_should_be_compromised
|
||||
elif reference_node_os_state == SOFTWARE_STATE.COMPROMISED:
|
||||
elif reference_node_os_state == SoftwareState.COMPROMISED:
|
||||
score += config_values.compromised
|
||||
else:
|
||||
pass
|
||||
@@ -191,59 +194,59 @@ def score_node_service_state(final_node, initial_node, reference_node, config_va
|
||||
config_values: Config values
|
||||
"""
|
||||
score = 0
|
||||
final_node_services = final_node.get_services()
|
||||
initial_node_services = initial_node.get_services()
|
||||
reference_node_services = reference_node.get_services()
|
||||
final_node_services: Dict[str, Service] = final_node.services
|
||||
initial_node_services: Dict[str, Service] = initial_node.services
|
||||
reference_node_services: Dict[str, Service] = reference_node.services
|
||||
|
||||
for service_key, final_service in final_node_services.items():
|
||||
reference_service = reference_node_services[service_key]
|
||||
initial_service = initial_node_services[service_key]
|
||||
|
||||
if final_service.get_state() == reference_service.get_state():
|
||||
if final_service.software_state == reference_service.software_state:
|
||||
# All is well - we're no different from the reference situation
|
||||
score += config_values.all_ok
|
||||
else:
|
||||
# We're different from the reference situation
|
||||
# Need to compare initial and reference state of node (i.e. at every step)
|
||||
if initial_service.get_state() == SOFTWARE_STATE.GOOD:
|
||||
if reference_service.get_state() == SOFTWARE_STATE.PATCHING:
|
||||
if initial_service.software_state == SoftwareState.GOOD:
|
||||
if reference_service.software_state == SoftwareState.PATCHING:
|
||||
score += config_values.patching_should_be_good
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.COMPROMISED:
|
||||
elif reference_service.software_state == SoftwareState.COMPROMISED:
|
||||
score += config_values.compromised_should_be_good
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.OVERWHELMED:
|
||||
elif reference_service.software_state == SoftwareState.OVERWHELMED:
|
||||
score += config_values.overwhelmed_should_be_good
|
||||
else:
|
||||
pass
|
||||
elif initial_service.get_state() == SOFTWARE_STATE.PATCHING:
|
||||
if reference_service.get_state() == SOFTWARE_STATE.GOOD:
|
||||
elif initial_service.software_state == SoftwareState.PATCHING:
|
||||
if reference_service.software_state == SoftwareState.GOOD:
|
||||
score += config_values.good_should_be_patching
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.COMPROMISED:
|
||||
elif reference_service.software_state == SoftwareState.COMPROMISED:
|
||||
score += config_values.compromised_should_be_patching
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.OVERWHELMED:
|
||||
elif reference_service.software_state == SoftwareState.OVERWHELMED:
|
||||
score += config_values.overwhelmed_should_be_patching
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.PATCHING:
|
||||
elif reference_service.software_state == SoftwareState.PATCHING:
|
||||
score += config_values.patching
|
||||
else:
|
||||
pass
|
||||
elif initial_service.get_state() == SOFTWARE_STATE.COMPROMISED:
|
||||
if reference_service.get_state() == SOFTWARE_STATE.GOOD:
|
||||
elif initial_service.software_state == SoftwareState.COMPROMISED:
|
||||
if reference_service.software_state == SoftwareState.GOOD:
|
||||
score += config_values.good_should_be_compromised
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.PATCHING:
|
||||
elif reference_service.software_state == SoftwareState.PATCHING:
|
||||
score += config_values.patching_should_be_compromised
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.COMPROMISED:
|
||||
elif reference_service.software_state == SoftwareState.COMPROMISED:
|
||||
score += config_values.compromised
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.OVERWHELMED:
|
||||
elif reference_service.software_state == SoftwareState.OVERWHELMED:
|
||||
score += config_values.overwhelmed_should_be_compromised
|
||||
else:
|
||||
pass
|
||||
elif initial_service.get_state() == SOFTWARE_STATE.OVERWHELMED:
|
||||
if reference_service.get_state() == SOFTWARE_STATE.GOOD:
|
||||
elif initial_service.software_state == SoftwareState.OVERWHELMED:
|
||||
if reference_service.software_state == SoftwareState.GOOD:
|
||||
score += config_values.good_should_be_overwhelmed
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.PATCHING:
|
||||
elif reference_service.software_state == SoftwareState.PATCHING:
|
||||
score += config_values.patching_should_be_overwhelmed
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.COMPROMISED:
|
||||
elif reference_service.software_state == SoftwareState.COMPROMISED:
|
||||
score += config_values.compromised_should_be_overwhelmed
|
||||
elif reference_service.get_state() == SOFTWARE_STATE.OVERWHELMED:
|
||||
elif reference_service.software_state == SoftwareState.OVERWHELMED:
|
||||
score += config_values.overwhelmed
|
||||
else:
|
||||
pass
|
||||
@@ -263,12 +266,12 @@ def score_node_file_system(final_node, initial_node, reference_node, config_valu
|
||||
reference_node: The node if there had been no red or blue effect
|
||||
"""
|
||||
score = 0
|
||||
final_node_file_system_state = final_node.get_file_system_state_actual()
|
||||
initial_node_file_system_state = initial_node.get_file_system_state_actual()
|
||||
reference_node_file_system_state = reference_node.get_file_system_state_actual()
|
||||
final_node_file_system_state = final_node.file_system_state_actual
|
||||
initial_node_file_system_state = initial_node.file_system_state_actual
|
||||
reference_node_file_system_state = reference_node.file_system_state_actual
|
||||
|
||||
final_node_scanning_state = final_node.is_scanning_file_system()
|
||||
reference_node_scanning_state = reference_node.is_scanning_file_system()
|
||||
final_node_scanning_state = final_node.file_system_scanning
|
||||
reference_node_scanning_state = reference_node.file_system_scanning
|
||||
|
||||
# File System State
|
||||
if final_node_file_system_state == reference_node_file_system_state:
|
||||
@@ -277,66 +280,66 @@ def score_node_file_system(final_node, initial_node, reference_node, config_valu
|
||||
else:
|
||||
# We're different from the reference situation
|
||||
# Need to compare initial and reference state of node (i.e. at every step)
|
||||
if initial_node_file_system_state == FILE_SYSTEM_STATE.GOOD:
|
||||
if reference_node_file_system_state == FILE_SYSTEM_STATE.REPAIRING:
|
||||
if initial_node_file_system_state == FileSystemState.GOOD:
|
||||
if reference_node_file_system_state == FileSystemState.REPAIRING:
|
||||
score += config_values.repairing_should_be_good
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.RESTORING:
|
||||
elif reference_node_file_system_state == FileSystemState.RESTORING:
|
||||
score += config_values.restoring_should_be_good
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.CORRUPT:
|
||||
elif reference_node_file_system_state == FileSystemState.CORRUPT:
|
||||
score += config_values.corrupt_should_be_good
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.DESTROYED:
|
||||
elif reference_node_file_system_state == FileSystemState.DESTROYED:
|
||||
score += config_values.destroyed_should_be_good
|
||||
else:
|
||||
pass
|
||||
elif initial_node_file_system_state == FILE_SYSTEM_STATE.REPAIRING:
|
||||
if reference_node_file_system_state == FILE_SYSTEM_STATE.GOOD:
|
||||
elif initial_node_file_system_state == FileSystemState.REPAIRING:
|
||||
if reference_node_file_system_state == FileSystemState.GOOD:
|
||||
score += config_values.good_should_be_repairing
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.RESTORING:
|
||||
elif reference_node_file_system_state == FileSystemState.RESTORING:
|
||||
score += config_values.restoring_should_be_repairing
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.CORRUPT:
|
||||
elif reference_node_file_system_state == FileSystemState.CORRUPT:
|
||||
score += config_values.corrupt_should_be_repairing
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.DESTROYED:
|
||||
elif reference_node_file_system_state == FileSystemState.DESTROYED:
|
||||
score += config_values.destroyed_should_be_repairing
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.REPAIRING:
|
||||
elif reference_node_file_system_state == FileSystemState.REPAIRING:
|
||||
score += config_values.repairing
|
||||
else:
|
||||
pass
|
||||
elif initial_node_file_system_state == FILE_SYSTEM_STATE.RESTORING:
|
||||
if reference_node_file_system_state == FILE_SYSTEM_STATE.GOOD:
|
||||
elif initial_node_file_system_state == FileSystemState.RESTORING:
|
||||
if reference_node_file_system_state == FileSystemState.GOOD:
|
||||
score += config_values.good_should_be_restoring
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.REPAIRING:
|
||||
elif reference_node_file_system_state == FileSystemState.REPAIRING:
|
||||
score += config_values.repairing_should_be_restoring
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.CORRUPT:
|
||||
elif reference_node_file_system_state == FileSystemState.CORRUPT:
|
||||
score += config_values.corrupt_should_be_restoring
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.DESTROYED:
|
||||
elif reference_node_file_system_state == FileSystemState.DESTROYED:
|
||||
score += config_values.destroyed_should_be_restoring
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.RESTORING:
|
||||
elif reference_node_file_system_state == FileSystemState.RESTORING:
|
||||
score += config_values.restoring
|
||||
else:
|
||||
pass
|
||||
elif initial_node_file_system_state == FILE_SYSTEM_STATE.CORRUPT:
|
||||
if reference_node_file_system_state == FILE_SYSTEM_STATE.GOOD:
|
||||
elif initial_node_file_system_state == FileSystemState.CORRUPT:
|
||||
if reference_node_file_system_state == FileSystemState.GOOD:
|
||||
score += config_values.good_should_be_corrupt
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.REPAIRING:
|
||||
elif reference_node_file_system_state == FileSystemState.REPAIRING:
|
||||
score += config_values.repairing_should_be_corrupt
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.RESTORING:
|
||||
elif reference_node_file_system_state == FileSystemState.RESTORING:
|
||||
score += config_values.restoring_should_be_corrupt
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.DESTROYED:
|
||||
elif reference_node_file_system_state == FileSystemState.DESTROYED:
|
||||
score += config_values.destroyed_should_be_corrupt
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.CORRUPT:
|
||||
elif reference_node_file_system_state == FileSystemState.CORRUPT:
|
||||
score += config_values.corrupt
|
||||
else:
|
||||
pass
|
||||
elif initial_node_file_system_state == FILE_SYSTEM_STATE.DESTROYED:
|
||||
if reference_node_file_system_state == FILE_SYSTEM_STATE.GOOD:
|
||||
elif initial_node_file_system_state == FileSystemState.DESTROYED:
|
||||
if reference_node_file_system_state == FileSystemState.GOOD:
|
||||
score += config_values.good_should_be_destroyed
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.REPAIRING:
|
||||
elif reference_node_file_system_state == FileSystemState.REPAIRING:
|
||||
score += config_values.repairing_should_be_destroyed
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.RESTORING:
|
||||
elif reference_node_file_system_state == FileSystemState.RESTORING:
|
||||
score += config_values.restoring_should_be_destroyed
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.CORRUPT:
|
||||
elif reference_node_file_system_state == FileSystemState.CORRUPT:
|
||||
score += config_values.corrupt_should_be_destroyed
|
||||
elif reference_node_file_system_state == FILE_SYSTEM_STATE.DESTROYED:
|
||||
elif reference_node_file_system_state == FileSystemState.DESTROYED:
|
||||
score += config_values.destroyed
|
||||
else:
|
||||
pass
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""The link class."""
|
||||
from typing import List
|
||||
|
||||
from primaite.common.protocol import Protocol
|
||||
|
||||
@@ -22,7 +23,7 @@ class Link(object):
|
||||
self.bandwidth = _bandwidth
|
||||
self.source_node_name = _source_node_name
|
||||
self.dest_node_name = _dest_node_name
|
||||
self.protocol_list = []
|
||||
self.protocol_list: List[Protocol] = []
|
||||
|
||||
# Add the default protocols
|
||||
for protocol_name in _services:
|
||||
|
||||
@@ -15,7 +15,7 @@ from stable_baselines3 import A2C, PPO
|
||||
from stable_baselines3.common.evaluation import evaluate_policy
|
||||
from stable_baselines3.ppo import MlpPolicy as PPOMlp
|
||||
|
||||
from primaite.common.config_values_main import config_values_main
|
||||
from primaite.common.config_values_main import ConfigValuesMain
|
||||
from primaite.environment.primaite_env import Primaite
|
||||
from primaite.transactions.transactions_to_file import write_transaction_to_file
|
||||
|
||||
@@ -178,7 +178,7 @@ def load_config_values():
|
||||
# Reward values
|
||||
# Generic
|
||||
config_values.all_ok = int(config_data["allOk"])
|
||||
# Node Operating State
|
||||
# Node Hardware State
|
||||
config_values.off_should_be_on = int(config_data["offShouldBeOn"])
|
||||
config_values.off_should_be_resetting = int(config_data["offShouldBeResetting"])
|
||||
config_values.on_should_be_off = int(config_data["onShouldBeOff"])
|
||||
@@ -186,7 +186,7 @@ def load_config_values():
|
||||
config_values.resetting_should_be_on = int(config_data["resettingShouldBeOn"])
|
||||
config_values.resetting_should_be_off = int(config_data["resettingShouldBeOff"])
|
||||
config_values.resetting = int(config_data["resetting"])
|
||||
# Node O/S or Service State
|
||||
# Node Software or Service State
|
||||
config_values.good_should_be_patching = int(config_data["goodShouldBePatching"])
|
||||
config_values.good_should_be_compromised = int(
|
||||
config_data["goodShouldBeCompromised"]
|
||||
@@ -331,7 +331,7 @@ try:
|
||||
config_file_main = open("config/config_main.yaml", "r")
|
||||
config_data = yaml.safe_load(config_file_main)
|
||||
# Create a config class
|
||||
config_values = config_values_main()
|
||||
config_values = ConfigValuesMain()
|
||||
# Load in config data
|
||||
load_config_values()
|
||||
except Exception:
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
import logging
|
||||
from typing import Final
|
||||
|
||||
from primaite.common.enums import FILE_SYSTEM_STATE, HARDWARE_STATE, SOFTWARE_STATE
|
||||
from primaite.common.config_values_main import ConfigValuesMain
|
||||
from primaite.common.enums import (
|
||||
FileSystemState,
|
||||
HardwareState,
|
||||
NodeType,
|
||||
Priority,
|
||||
SoftwareState,
|
||||
)
|
||||
from primaite.nodes.node import Node
|
||||
|
||||
_LOGGER: Final[logging.Logger] = logging.getLogger(__name__)
|
||||
@@ -14,216 +21,174 @@ class ActiveNode(Node):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
_id,
|
||||
_name,
|
||||
_type,
|
||||
_priority,
|
||||
_state,
|
||||
_ip_address,
|
||||
_os_state,
|
||||
_file_system_state,
|
||||
_config_values,
|
||||
node_id: str,
|
||||
name: str,
|
||||
node_type: NodeType,
|
||||
priority: Priority,
|
||||
hardware_state: HardwareState,
|
||||
ip_address: str,
|
||||
software_state: SoftwareState,
|
||||
file_system_state: FileSystemState,
|
||||
config_values: ConfigValuesMain,
|
||||
):
|
||||
"""
|
||||
Init.
|
||||
|
||||
Args:
|
||||
_id: The node ID
|
||||
_name: The node name
|
||||
_type: The node type (enum)
|
||||
_priority: The node priority (enum)
|
||||
_state: The node state (enum)
|
||||
_ip_address: The node IP address
|
||||
_os_state: The node Operating System state
|
||||
_file_system_state: The node file system state
|
||||
_config_values: The config values
|
||||
:param node_id: The node ID
|
||||
:param name: The node name
|
||||
:param node_type: The node type (enum)
|
||||
:param priority: The node priority (enum)
|
||||
:param hardware_state: The node Hardware State
|
||||
:param ip_address: The node IP address
|
||||
:param software_state: The node Software State
|
||||
:param file_system_state: The node file system state
|
||||
:param config_values: The config values
|
||||
"""
|
||||
super().__init__(_id, _name, _type, _priority, _state, _config_values)
|
||||
self.ip_address = _ip_address
|
||||
# Related to O/S
|
||||
self.os_state = _os_state
|
||||
self.patching_count = 0
|
||||
super().__init__(
|
||||
node_id, name, node_type, priority, hardware_state, config_values
|
||||
)
|
||||
self.ip_address: str = ip_address
|
||||
# Related to Software
|
||||
self._software_state: SoftwareState = software_state
|
||||
self.patching_count: int = 0
|
||||
# Related to File System
|
||||
self.file_system_state_actual = _file_system_state
|
||||
self.file_system_state_observed = _file_system_state
|
||||
self.file_system_scanning = False
|
||||
self.file_system_scanning_count = 0
|
||||
self.file_system_action_count = 0
|
||||
self.file_system_state_actual: FileSystemState = file_system_state
|
||||
self.file_system_state_observed: FileSystemState = file_system_state
|
||||
self.file_system_scanning: bool = False
|
||||
self.file_system_scanning_count: int = 0
|
||||
self.file_system_action_count: int = 0
|
||||
|
||||
def set_ip_address(self, _ip_address):
|
||||
@property
|
||||
def software_state(self) -> SoftwareState:
|
||||
"""
|
||||
Sets IP address.
|
||||
Get the software_state.
|
||||
|
||||
Args:
|
||||
_ip_address: IP address
|
||||
:return: The software_state.
|
||||
"""
|
||||
self.ip_address = _ip_address
|
||||
return self._software_state
|
||||
|
||||
def get_ip_address(self):
|
||||
@software_state.setter
|
||||
def software_state(self, software_state: SoftwareState):
|
||||
"""
|
||||
Gets IP address.
|
||||
Get the software_state.
|
||||
|
||||
Returns:
|
||||
IP address
|
||||
:param software_state: Software State.
|
||||
"""
|
||||
return self.ip_address
|
||||
|
||||
def set_os_state(self, _os_state):
|
||||
"""
|
||||
Sets operating system state.
|
||||
|
||||
Args:
|
||||
_os_state: Operating system state
|
||||
"""
|
||||
if self.operating_state != HARDWARE_STATE.OFF:
|
||||
self.os_state = _os_state
|
||||
if _os_state == SOFTWARE_STATE.PATCHING:
|
||||
if self.hardware_state != HardwareState.OFF:
|
||||
self._software_state = software_state
|
||||
if software_state == SoftwareState.PATCHING:
|
||||
self.patching_count = self.config_values.os_patching_duration
|
||||
else:
|
||||
_LOGGER.info(
|
||||
f"The Nodes operating state is OFF so OS State cannot be "
|
||||
f"The Nodes hardware state is OFF so OS State cannot be "
|
||||
f"changed. "
|
||||
f"Node:{self.id}, "
|
||||
f"Node Operating State:{self.operating_state}, "
|
||||
f"Node Operating System State:{self.os_state}"
|
||||
f"Node.node_id:{self.node_id}, "
|
||||
f"Node.hardware_state:{self.hardware_state}, "
|
||||
f"Node.software_state:{self._software_state}"
|
||||
)
|
||||
|
||||
def set_os_state_if_not_compromised(self, _os_state):
|
||||
def set_software_state_if_not_compromised(self, software_state: SoftwareState):
|
||||
"""
|
||||
Sets operating system state if the node is not compromised.
|
||||
Sets Software State if the node is not compromised.
|
||||
|
||||
Args:
|
||||
_os_state: Operating system state
|
||||
software_state: Software State
|
||||
"""
|
||||
if self.operating_state != HARDWARE_STATE.OFF:
|
||||
if self.os_state != SOFTWARE_STATE.COMPROMISED:
|
||||
self.os_state = _os_state
|
||||
if _os_state == SOFTWARE_STATE.PATCHING:
|
||||
if self.hardware_state != HardwareState.OFF:
|
||||
if self._software_state != SoftwareState.COMPROMISED:
|
||||
self._software_state = software_state
|
||||
if software_state == SoftwareState.PATCHING:
|
||||
self.patching_count = self.config_values.os_patching_duration
|
||||
else:
|
||||
_LOGGER.info(
|
||||
f"The Nodes operating state is OFF so OS State cannot be changed."
|
||||
f"Node:{self.id}, "
|
||||
f"Node Operating State:{self.operating_state}, "
|
||||
f"Node Operating System State:{self.os_state}",
|
||||
f"The Nodes hardware state is OFF so OS State cannot be changed."
|
||||
f"Node.node_id:{self.node_id}, "
|
||||
f"Node.hardware_state:{self.hardware_state}, "
|
||||
f"Node.software_state:{self._software_state}"
|
||||
)
|
||||
|
||||
def get_os_state(self):
|
||||
"""
|
||||
Gets operating system state.
|
||||
|
||||
Returns:
|
||||
Operating system state
|
||||
"""
|
||||
return self.os_state
|
||||
|
||||
def update_os_patching_status(self):
|
||||
"""Updates operating system status based on patching cycle."""
|
||||
self.patching_count -= 1
|
||||
if self.patching_count <= 0:
|
||||
self.patching_count = 0
|
||||
self.os_state = SOFTWARE_STATE.GOOD
|
||||
self._software_state = SoftwareState.GOOD
|
||||
|
||||
def set_file_system_state(self, _file_system_state):
|
||||
def set_file_system_state(self, file_system_state: FileSystemState):
|
||||
"""
|
||||
Sets the file system state (actual and observed).
|
||||
|
||||
Args:
|
||||
_file_system_state: File system state
|
||||
file_system_state: File system state
|
||||
"""
|
||||
if self.operating_state != HARDWARE_STATE.OFF:
|
||||
self.file_system_state_actual = _file_system_state
|
||||
if self.hardware_state != HardwareState.OFF:
|
||||
self.file_system_state_actual = file_system_state
|
||||
|
||||
if _file_system_state == FILE_SYSTEM_STATE.REPAIRING:
|
||||
if file_system_state == FileSystemState.REPAIRING:
|
||||
self.file_system_action_count = (
|
||||
self.config_values.file_system_repairing_limit
|
||||
)
|
||||
self.file_system_state_observed = FILE_SYSTEM_STATE.REPAIRING
|
||||
elif _file_system_state == FILE_SYSTEM_STATE.RESTORING:
|
||||
self.file_system_state_observed = FileSystemState.REPAIRING
|
||||
elif file_system_state == FileSystemState.RESTORING:
|
||||
self.file_system_action_count = (
|
||||
self.config_values.file_system_restoring_limit
|
||||
)
|
||||
self.file_system_state_observed = FILE_SYSTEM_STATE.RESTORING
|
||||
elif _file_system_state == FILE_SYSTEM_STATE.GOOD:
|
||||
self.file_system_state_observed = FILE_SYSTEM_STATE.GOOD
|
||||
self.file_system_state_observed = FileSystemState.RESTORING
|
||||
elif file_system_state == FileSystemState.GOOD:
|
||||
self.file_system_state_observed = FileSystemState.GOOD
|
||||
else:
|
||||
_LOGGER.info(
|
||||
f"The Nodes operating state is OFF so File System State "
|
||||
f"The Nodes hardware state is OFF so File System State "
|
||||
f"cannot be changed. "
|
||||
f"Node:{self.id}, "
|
||||
f"Node Operating State:{self.operating_state}, "
|
||||
f"Node File System State:{self.file_system_state_actual}",
|
||||
f"Node.node_id:{self.node_id}, "
|
||||
f"Node.hardware_state:{self.hardware_state}, "
|
||||
f"Node.file_system_state.actual:{self.file_system_state_actual}"
|
||||
)
|
||||
|
||||
def set_file_system_state_if_not_compromised(self, _file_system_state):
|
||||
def set_file_system_state_if_not_compromised(
|
||||
self, file_system_state: FileSystemState
|
||||
):
|
||||
"""
|
||||
Sets the file system state (actual and observed) if not in a compromised state.
|
||||
|
||||
Use for green PoL to prevent it overturning a compromised state
|
||||
|
||||
Args:
|
||||
_file_system_state: File system state
|
||||
file_system_state: File system state
|
||||
"""
|
||||
if self.operating_state != HARDWARE_STATE.OFF:
|
||||
if self.hardware_state != HardwareState.OFF:
|
||||
if (
|
||||
self.file_system_state_actual != FILE_SYSTEM_STATE.CORRUPT
|
||||
and self.file_system_state_actual != FILE_SYSTEM_STATE.DESTROYED
|
||||
self.file_system_state_actual != FileSystemState.CORRUPT
|
||||
and self.file_system_state_actual != FileSystemState.DESTROYED
|
||||
):
|
||||
self.file_system_state_actual = _file_system_state
|
||||
self.file_system_state_actual = file_system_state
|
||||
|
||||
if _file_system_state == FILE_SYSTEM_STATE.REPAIRING:
|
||||
if file_system_state == FileSystemState.REPAIRING:
|
||||
self.file_system_action_count = (
|
||||
self.config_values.file_system_repairing_limit
|
||||
)
|
||||
self.file_system_state_observed = FILE_SYSTEM_STATE.REPAIRING
|
||||
elif _file_system_state == FILE_SYSTEM_STATE.RESTORING:
|
||||
self.file_system_state_observed = FileSystemState.REPAIRING
|
||||
elif file_system_state == FileSystemState.RESTORING:
|
||||
self.file_system_action_count = (
|
||||
self.config_values.file_system_restoring_limit
|
||||
)
|
||||
self.file_system_state_observed = FILE_SYSTEM_STATE.RESTORING
|
||||
elif _file_system_state == FILE_SYSTEM_STATE.GOOD:
|
||||
self.file_system_state_observed = FILE_SYSTEM_STATE.GOOD
|
||||
self.file_system_state_observed = FileSystemState.RESTORING
|
||||
elif file_system_state == FileSystemState.GOOD:
|
||||
self.file_system_state_observed = FileSystemState.GOOD
|
||||
else:
|
||||
_LOGGER.info(
|
||||
f"The Nodes operating state is OFF so File System State (if not "
|
||||
f"The Nodes hardware state is OFF so File System State (if not "
|
||||
f"compromised) cannot be changed. "
|
||||
f"Node:{self.id}, "
|
||||
f"Node Operating State:{self.operating_state}, "
|
||||
f"Node File System State:{self.file_system_state_actual}",
|
||||
f"Node.node_id:{self.node_id}, "
|
||||
f"Node.hardware_state:{self.hardware_state}, "
|
||||
f"Node.file_system_state.actual:{self.file_system_state_actual}"
|
||||
)
|
||||
|
||||
def get_file_system_state_actual(self):
|
||||
"""
|
||||
Gets file system state (actual).
|
||||
|
||||
Returns:
|
||||
File system state (actual)
|
||||
"""
|
||||
return self.file_system_state_actual
|
||||
|
||||
def get_file_system_state_observed(self):
|
||||
"""
|
||||
Gets file system state (observed).
|
||||
|
||||
Returns:
|
||||
File system state (observed)
|
||||
"""
|
||||
return self.file_system_state_observed
|
||||
|
||||
def start_file_system_scan(self):
|
||||
"""Starts a file system scan."""
|
||||
self.file_system_scanning = True
|
||||
self.file_system_scanning_count = self.config_values.file_system_scanning_limit
|
||||
|
||||
def is_scanning_file_system(self):
|
||||
"""
|
||||
Gets true/false on whether file system is being scanned.
|
||||
|
||||
Returns:
|
||||
True if file system is being scanned
|
||||
"""
|
||||
return self.file_system_scanning
|
||||
|
||||
def update_file_system_state(self):
|
||||
"""Updates file system status based on scanning/restore/repair cycle."""
|
||||
# Deprecate both the action count (for restoring or reparing) and the scanning count
|
||||
@@ -234,11 +199,11 @@ class ActiveNode(Node):
|
||||
if self.file_system_action_count <= 0:
|
||||
self.file_system_action_count = 0
|
||||
if (
|
||||
self.file_system_state_actual == FILE_SYSTEM_STATE.REPAIRING
|
||||
or self.file_system_state_actual == FILE_SYSTEM_STATE.RESTORING
|
||||
self.file_system_state_actual == FileSystemState.REPAIRING
|
||||
or self.file_system_state_actual == FileSystemState.RESTORING
|
||||
):
|
||||
self.file_system_state_actual = FILE_SYSTEM_STATE.GOOD
|
||||
self.file_system_state_observed = FILE_SYSTEM_STATE.GOOD
|
||||
self.file_system_state_actual = FileSystemState.GOOD
|
||||
self.file_system_state_observed = FileSystemState.GOOD
|
||||
|
||||
# Scanning updates
|
||||
if self.file_system_scanning == True and self.file_system_scanning_count < 0:
|
||||
|
||||
@@ -1,135 +1,56 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""The base Node class."""
|
||||
from primaite.common.enums import HARDWARE_STATE
|
||||
from typing import Final
|
||||
|
||||
from primaite.common.config_values_main import ConfigValuesMain
|
||||
from primaite.common.enums import HardwareState, NodeType, Priority
|
||||
|
||||
|
||||
class Node:
|
||||
"""Node class."""
|
||||
|
||||
def __init__(self, _id, _name, _type, _priority, _state, _config_values):
|
||||
def __init__(
|
||||
self,
|
||||
node_id: str,
|
||||
name: str,
|
||||
node_type: NodeType,
|
||||
priority: Priority,
|
||||
hardware_state: HardwareState,
|
||||
config_values: ConfigValuesMain,
|
||||
):
|
||||
"""
|
||||
Init.
|
||||
|
||||
Args:
|
||||
_id: The node id
|
||||
_name: The name of the node
|
||||
_type: The type of the node
|
||||
_priority: The priority of the node
|
||||
_state: The state of the node
|
||||
:param node_id: The node id.
|
||||
:param name: The name of the node.
|
||||
:param node_type: The type of the node.
|
||||
:param priority: The priority of the node.
|
||||
:param hardware_state: The state of the node.
|
||||
:param config_values: Config values.
|
||||
"""
|
||||
self.id = _id
|
||||
self.name = _name
|
||||
self.type = _type
|
||||
self.priority = _priority
|
||||
self.operating_state = _state
|
||||
self.resetting_count = 0
|
||||
self.config_values = _config_values
|
||||
self.node_id: Final[str] = node_id
|
||||
self.name: Final[str] = name
|
||||
self.node_type: Final[NodeType] = node_type
|
||||
self.priority = priority
|
||||
self.hardware_state: HardwareState = hardware_state
|
||||
self.resetting_count: int = 0
|
||||
self.config_values: ConfigValuesMain = config_values
|
||||
|
||||
def __repr__(self):
|
||||
"""Returns the name of the node."""
|
||||
return self.name
|
||||
|
||||
def set_id(self, _id):
|
||||
"""
|
||||
Sets the node ID.
|
||||
|
||||
Args:
|
||||
_id: The node ID
|
||||
"""
|
||||
self.id = _id
|
||||
|
||||
def get_id(self):
|
||||
"""
|
||||
Gets the node ID.
|
||||
|
||||
Returns:
|
||||
The node ID
|
||||
"""
|
||||
return self.id
|
||||
|
||||
def set_name(self, _name):
|
||||
"""
|
||||
Sets the node name.
|
||||
|
||||
Args:
|
||||
_name: The node name
|
||||
"""
|
||||
self.name = _name
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Gets the node name.
|
||||
|
||||
Returns:
|
||||
The node name
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def set_type(self, _type):
|
||||
"""
|
||||
Sets the node type.
|
||||
|
||||
Args:
|
||||
_type: The node type
|
||||
"""
|
||||
self.type = _type
|
||||
|
||||
def get_type(self):
|
||||
"""
|
||||
Gets the node type.
|
||||
|
||||
Returns:
|
||||
The node type
|
||||
"""
|
||||
return self.type
|
||||
|
||||
def set_priority(self, _priority):
|
||||
"""
|
||||
Sets the node priority.
|
||||
|
||||
Args:
|
||||
_priority: The node priority
|
||||
"""
|
||||
self.priority = _priority
|
||||
|
||||
def get_priority(self):
|
||||
"""
|
||||
Gets the node priority.
|
||||
|
||||
Returns:
|
||||
The node priority
|
||||
"""
|
||||
return self.priority
|
||||
|
||||
def set_state(self, _state):
|
||||
"""
|
||||
Sets the node state.
|
||||
|
||||
Args:
|
||||
_state: The node state
|
||||
"""
|
||||
self.operating_state = _state
|
||||
|
||||
def get_state(self):
|
||||
"""
|
||||
Gets the node operating state.
|
||||
|
||||
Returns:
|
||||
The node operating state
|
||||
"""
|
||||
return self.operating_state
|
||||
|
||||
def turn_on(self):
|
||||
"""Sets the node state to ON."""
|
||||
self.operating_state = HARDWARE_STATE.ON
|
||||
self.hardware_state = HardwareState.ON
|
||||
|
||||
def turn_off(self):
|
||||
"""Sets the node state to OFF."""
|
||||
self.operating_state = HARDWARE_STATE.OFF
|
||||
self.hardware_state = HardwareState.OFF
|
||||
|
||||
def reset(self):
|
||||
"""Sets the node state to Resetting and starts the reset count."""
|
||||
self.operating_state = HARDWARE_STATE.RESETTING
|
||||
self.hardware_state = HardwareState.RESETTING
|
||||
self.resetting_count = self.config_values.node_reset_duration
|
||||
|
||||
def update_resetting_status(self):
|
||||
@@ -137,4 +58,4 @@ class Node:
|
||||
self.resetting_count -= 1
|
||||
if self.resetting_count <= 0:
|
||||
self.resetting_count = 0
|
||||
self.operating_state = HARDWARE_STATE.ON
|
||||
self.hardware_state = HardwareState.ON
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""Defines node behaviour for Green PoL."""
|
||||
from primaite.common.enums import NodePOLType
|
||||
|
||||
|
||||
class NodeStateInstructionRed(object):
|
||||
@@ -12,7 +13,7 @@ class NodeStateInstructionRed(object):
|
||||
_end_step,
|
||||
_target_node_id,
|
||||
_pol_initiator,
|
||||
_pol_type,
|
||||
_pol_type: NodePOLType,
|
||||
pol_protocol,
|
||||
_pol_state,
|
||||
_pol_source_node_id,
|
||||
@@ -40,7 +41,7 @@ class NodeStateInstructionRed(object):
|
||||
self.end_step = _end_step
|
||||
self.target_node_id = _target_node_id
|
||||
self.initiator = _pol_initiator
|
||||
self.pol_type = _pol_type
|
||||
self.pol_type: NodePOLType = _pol_type
|
||||
self.service_name = pol_protocol # Not used when not a service instruction
|
||||
self.state = _pol_state
|
||||
self.source_node_id = _pol_source_node_id
|
||||
@@ -83,7 +84,7 @@ class NodeStateInstructionRed(object):
|
||||
"""
|
||||
return self.initiator
|
||||
|
||||
def get_pol_type(self):
|
||||
def get_pol_type(self) -> NodePOLType:
|
||||
"""
|
||||
Gets the node pattern of life type (enum).
|
||||
|
||||
|
||||
@@ -1,32 +1,44 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""The Passive Node class (i.e. an actuator)."""
|
||||
|
||||
from primaite.common.config_values_main import ConfigValuesMain
|
||||
from primaite.common.enums import HardwareState, NodeType, Priority
|
||||
from primaite.nodes.node import Node
|
||||
|
||||
|
||||
class PassiveNode(Node):
|
||||
"""The Passive Node class."""
|
||||
|
||||
def __init__(self, _id, _name, _type, _priority, _state, _config_values):
|
||||
def __init__(
|
||||
self,
|
||||
node_id: str,
|
||||
name: str,
|
||||
node_type: NodeType,
|
||||
priority: Priority,
|
||||
hardware_state: HardwareState,
|
||||
config_values: ConfigValuesMain,
|
||||
):
|
||||
"""
|
||||
Init.
|
||||
|
||||
Args:
|
||||
_id: The node id
|
||||
_name: The name of the node
|
||||
_type: The type of the node
|
||||
_priority: The priority of the node
|
||||
_state: The state of the node
|
||||
:param node_id: The node id.
|
||||
:param name: The name of the node.
|
||||
:param node_type: The type of the node.
|
||||
:param priority: The priority of the node.
|
||||
:param hardware_state: The state of the node.
|
||||
:param config_values: Config values.
|
||||
"""
|
||||
# Pass through to Super for now
|
||||
super().__init__(_id, _name, _type, _priority, _state, _config_values)
|
||||
super().__init__(
|
||||
node_id, name, node_type, priority, hardware_state, config_values
|
||||
)
|
||||
|
||||
def get_ip_address(self):
|
||||
@property
|
||||
def ip_address(self) -> str:
|
||||
"""
|
||||
Gets the node IP address.
|
||||
Gets the node IP address as an empty string.
|
||||
|
||||
Returns:
|
||||
The node IP address
|
||||
No concept of IP address for passive nodes for now.
|
||||
|
||||
:return: The node IP address.
|
||||
"""
|
||||
# No concept of IP address for passive nodes for now
|
||||
return ""
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""A Service Node (i.e. not an actuator)."""
|
||||
import logging
|
||||
from typing import Final
|
||||
from typing import Dict, Final
|
||||
|
||||
from primaite.common.enums import HARDWARE_STATE, SOFTWARE_STATE
|
||||
from primaite.common.config_values_main import ConfigValuesMain
|
||||
from primaite.common.enums import (
|
||||
FileSystemState,
|
||||
HardwareState,
|
||||
NodeType,
|
||||
Priority,
|
||||
SoftwareState,
|
||||
)
|
||||
from primaite.common.service import Service
|
||||
from primaite.nodes.active_node import ActiveNode
|
||||
|
||||
_LOGGER: Final[logging.Logger] = logging.getLogger(__name__)
|
||||
@@ -14,185 +22,169 @@ class ServiceNode(ActiveNode):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
_id,
|
||||
_name,
|
||||
_type,
|
||||
_priority,
|
||||
_state,
|
||||
_ip_address,
|
||||
_os_state,
|
||||
_file_system_state,
|
||||
_config_values,
|
||||
node_id: str,
|
||||
name: str,
|
||||
node_type: NodeType,
|
||||
priority: Priority,
|
||||
hardware_state: HardwareState,
|
||||
ip_address: str,
|
||||
software_state: SoftwareState,
|
||||
file_system_state: FileSystemState,
|
||||
config_values: ConfigValuesMain,
|
||||
):
|
||||
"""
|
||||
Init.
|
||||
|
||||
Args:
|
||||
_id: The node id
|
||||
_name: The name of the node
|
||||
_type: The type of the node
|
||||
_priority: The priority of the node
|
||||
_state: The state of the node
|
||||
_ipAddress: The IP address of the node
|
||||
_osState: The operating system state of the node
|
||||
_file_system_state: The file system state of the node
|
||||
:param node_id: The node ID
|
||||
:param name: The node name
|
||||
:param node_type: The node type (enum)
|
||||
:param priority: The node priority (enum)
|
||||
:param hardware_state: The node Hardware State
|
||||
:param ip_address: The node IP address
|
||||
:param software_state: The node Software State
|
||||
:param file_system_state: The node file system state
|
||||
:param config_values: The config values
|
||||
"""
|
||||
super().__init__(
|
||||
_id,
|
||||
_name,
|
||||
_type,
|
||||
_priority,
|
||||
_state,
|
||||
_ip_address,
|
||||
_os_state,
|
||||
_file_system_state,
|
||||
_config_values,
|
||||
node_id,
|
||||
name,
|
||||
node_type,
|
||||
priority,
|
||||
hardware_state,
|
||||
ip_address,
|
||||
software_state,
|
||||
file_system_state,
|
||||
config_values,
|
||||
)
|
||||
self.services = {}
|
||||
self.services: Dict[str, Service] = {}
|
||||
|
||||
def add_service(self, _service):
|
||||
def add_service(self, service: Service):
|
||||
"""
|
||||
Adds a service to the node.
|
||||
|
||||
Args:
|
||||
_service: The service to add
|
||||
:param service: The service to add
|
||||
"""
|
||||
self.services[_service.get_name()] = _service
|
||||
self.services[service.name] = service
|
||||
|
||||
def get_services(self):
|
||||
"""
|
||||
Gets the dictionary of services on this node.
|
||||
|
||||
Returns:
|
||||
Dictionary of services on this node
|
||||
"""
|
||||
return self.services
|
||||
|
||||
def has_service(self, _protocol):
|
||||
def has_service(self, protocol_name: str) -> bool:
|
||||
"""
|
||||
Indicates whether a service is on a node.
|
||||
|
||||
Returns:
|
||||
True if service (protocol) is on the node
|
||||
:param protocol_name: The service (protocol)e.
|
||||
:return: True if service (protocol) is on the node, otherwise False.
|
||||
"""
|
||||
for service_key, service_value in self.services.items():
|
||||
if service_key == _protocol:
|
||||
if service_key == protocol_name:
|
||||
return True
|
||||
else:
|
||||
pass
|
||||
return False
|
||||
|
||||
def service_running(self, _protocol):
|
||||
def service_running(self, protocol_name: str) -> bool:
|
||||
"""
|
||||
Indicates whether a service is in a running state on the node.
|
||||
|
||||
Returns:
|
||||
True if service (protocol) is in a running state on the node
|
||||
:param protocol_name: The service (protocol)
|
||||
:return: True if service (protocol) is in a running state on the
|
||||
node, otherwise False.
|
||||
"""
|
||||
for service_key, service_value in self.services.items():
|
||||
if service_key == _protocol:
|
||||
if service_value.get_state() != SOFTWARE_STATE.PATCHING:
|
||||
if service_key == protocol_name:
|
||||
if service_value.software_state != SoftwareState.PATCHING:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
pass
|
||||
return False
|
||||
|
||||
def service_is_overwhelmed(self, _protocol):
|
||||
def service_is_overwhelmed(self, protocol_name: str) -> bool:
|
||||
"""
|
||||
Indicates whether a service is in an overwhelmed state on the node.
|
||||
|
||||
Returns:
|
||||
True if service (protocol) is in an overwhelmed state on the node
|
||||
:param protocol_name: The service (protocol)
|
||||
:return: True if service (protocol) is in an overwhelmed state on the
|
||||
node, otherwise False.
|
||||
"""
|
||||
for service_key, service_value in self.services.items():
|
||||
if service_key == _protocol:
|
||||
if service_value.get_state() == SOFTWARE_STATE.OVERWHELMED:
|
||||
if service_key == protocol_name:
|
||||
if service_value.software_state == SoftwareState.OVERWHELMED:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
pass
|
||||
return False
|
||||
|
||||
def set_service_state(self, _protocol, _state):
|
||||
def set_service_state(self, protocol_name: str, software_state: SoftwareState):
|
||||
"""
|
||||
Sets the state of a service (protocol) on the node.
|
||||
Sets the software_state of a service (protocol) on the node.
|
||||
|
||||
Args:
|
||||
_protocol: The service (protocol)
|
||||
_state: The state value
|
||||
:param protocol_name: The service (protocol).
|
||||
:param software_state: The software_state.
|
||||
"""
|
||||
if self.operating_state != HARDWARE_STATE.OFF:
|
||||
for service_key, service_value in self.services.items():
|
||||
if service_key == _protocol:
|
||||
# Can't set to compromised if you're in a patching state
|
||||
if (
|
||||
_state == SOFTWARE_STATE.COMPROMISED
|
||||
and service_value.get_state() != SOFTWARE_STATE.PATCHING
|
||||
) or _state != SOFTWARE_STATE.COMPROMISED:
|
||||
service_value.set_state(_state)
|
||||
else:
|
||||
# Do nothing
|
||||
pass
|
||||
if _state == SOFTWARE_STATE.PATCHING:
|
||||
if self.hardware_state != HardwareState.OFF:
|
||||
service_key = protocol_name
|
||||
service_value = self.services.get(service_key)
|
||||
if service_value:
|
||||
# Can't set to compromised if you're in a patching state
|
||||
if (
|
||||
software_state == SoftwareState.COMPROMISED
|
||||
and service_value.software_state != SoftwareState.PATCHING
|
||||
) or software_state != SoftwareState.COMPROMISED:
|
||||
service_value.software_state = software_state
|
||||
if software_state == SoftwareState.PATCHING:
|
||||
service_value.patching_count = (
|
||||
self.config_values.service_patching_duration
|
||||
)
|
||||
else:
|
||||
_LOGGER.info(
|
||||
f"The Nodes hardware state is OFF so the state of a service "
|
||||
f"cannot be changed. "
|
||||
f"Node.node_id:{self.node_id}, "
|
||||
f"Node.hardware_state:{self.hardware_state}, "
|
||||
f"Node.services[<key>]:{protocol_name}, "
|
||||
f"Node.services[<key>].software_state:{software_state}"
|
||||
)
|
||||
|
||||
def set_service_state_if_not_compromised(
|
||||
self, protocol_name: str, software_state: SoftwareState
|
||||
):
|
||||
"""
|
||||
Sets the software_state of a service (protocol) on the node.
|
||||
|
||||
Done if the software_state is not "compromised".
|
||||
|
||||
:param protocol_name: The service (protocol).
|
||||
:param software_state: The software_state.
|
||||
"""
|
||||
if self.hardware_state != HardwareState.OFF:
|
||||
service_key = protocol_name
|
||||
service_value = self.services.get(service_key)
|
||||
if service_value:
|
||||
if service_value.software_state != SoftwareState.COMPROMISED:
|
||||
service_value.software_state = software_state
|
||||
if software_state == SoftwareState.PATCHING:
|
||||
service_value.patching_count = (
|
||||
self.config_values.service_patching_duration
|
||||
)
|
||||
else:
|
||||
# Do nothing
|
||||
pass
|
||||
else:
|
||||
_LOGGER.info(
|
||||
f"The Nodes operating state is OFF so the state of a service "
|
||||
f"The Nodes hardware state is OFF so the state of a service "
|
||||
f"cannot be changed. "
|
||||
f"Node:{self.id}, "
|
||||
f"Node Operating State:{self.operating_state}, "
|
||||
f"Node Service Protocol:{_protocol}, "
|
||||
f"Node Service State: {_state}"
|
||||
f"Node.node_id:{self.node_id}, "
|
||||
f"Node.hardware_state:{self.hardware_state}, "
|
||||
f"Node.services[<key>]:{protocol_name}, "
|
||||
f"Node.services[<key>].software_state:{software_state}"
|
||||
)
|
||||
|
||||
def set_service_state_if_not_compromised(self, _protocol, _state):
|
||||
"""
|
||||
Sets the state of a service (protocol) on the node if the operating state is not "compromised".
|
||||
|
||||
Args:
|
||||
_protocol: The service (protocol)
|
||||
_state: The state value
|
||||
"""
|
||||
if self.operating_state != HARDWARE_STATE.OFF:
|
||||
for service_key, service_value in self.services.items():
|
||||
if service_key == _protocol:
|
||||
if service_value.get_state() != SOFTWARE_STATE.COMPROMISED:
|
||||
service_value.set_state(_state)
|
||||
if _state == SOFTWARE_STATE.PATCHING:
|
||||
service_value.patching_count = (
|
||||
self.config_values.service_patching_duration
|
||||
)
|
||||
else:
|
||||
_LOGGER.info(
|
||||
f"The Nodes operating state is OFF so the state of a service "
|
||||
f"cannot be changed. "
|
||||
f"Node:{self.id}, "
|
||||
f"Node Operating State:{self.operating_state}, "
|
||||
f"Node Service Protocol:{_protocol}, "
|
||||
f"Node Service State:{_state}"
|
||||
)
|
||||
|
||||
def get_service_state(self, _protocol):
|
||||
def get_service_state(self, protocol_name):
|
||||
"""
|
||||
Gets the state of a service.
|
||||
|
||||
Returns:
|
||||
The state of the service
|
||||
:return: The software_state of the service.
|
||||
"""
|
||||
for service_key, service_value in self.services.items():
|
||||
if service_key == _protocol:
|
||||
return service_value.get_state()
|
||||
service_key = protocol_name
|
||||
service_value = self.services.get(service_key)
|
||||
if service_value:
|
||||
return service_value.software_state
|
||||
|
||||
def update_services_patching_status(self):
|
||||
"""Updates the patching counter for any service that are patching."""
|
||||
for service_key, service_value in self.services.items():
|
||||
if service_value.get_state() == SOFTWARE_STATE.PATCHING:
|
||||
if service_value.software_state == SoftwareState.PATCHING:
|
||||
service_value.reduce_patching_count()
|
||||
|
||||
@@ -1,16 +1,30 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""Implements Pattern of Life on the network (nodes and links)."""
|
||||
from typing import Dict, Union
|
||||
|
||||
from networkx import shortest_path
|
||||
from networkx import MultiGraph, shortest_path
|
||||
|
||||
from primaite.common.enums import HARDWARE_STATE, NODE_POL_TYPE, SOFTWARE_STATE, TYPE
|
||||
from primaite.acl.access_control_list import AccessControlList
|
||||
from primaite.common.custom_typing import NodeType
|
||||
from primaite.common.enums import HardwareState, NodePOLType, NodeType, SoftwareState
|
||||
from primaite.links.link import Link
|
||||
from primaite.nodes.active_node import ActiveNode
|
||||
from primaite.nodes.node_state_instruction_green import NodeStateInstructionGreen
|
||||
from primaite.nodes.node_state_instruction_red import NodeStateInstructionRed
|
||||
from primaite.nodes.service_node import ServiceNode
|
||||
from primaite.pol.ier import IER
|
||||
|
||||
_VERBOSE = False
|
||||
|
||||
|
||||
def apply_iers(network, nodes, links, iers, acl, step):
|
||||
def apply_iers(
|
||||
network: MultiGraph,
|
||||
nodes: Dict[str, NodeType],
|
||||
links: Dict[str, Link],
|
||||
iers: Dict[str, IER],
|
||||
acl: AccessControlList,
|
||||
step: int,
|
||||
):
|
||||
"""
|
||||
Applies IERs to the links (link pattern of life).
|
||||
|
||||
@@ -51,25 +65,25 @@ def apply_iers(network, nodes, links, iers, acl, step):
|
||||
dest_node = nodes[dest_node_id]
|
||||
|
||||
# 1. Check the source node situation
|
||||
if source_node.get_type() == TYPE.SWITCH:
|
||||
if source_node.node_type == NodeType.SWITCH:
|
||||
# It's a switch
|
||||
if (
|
||||
source_node.get_state() == HARDWARE_STATE.ON
|
||||
and source_node.get_os_state() != SOFTWARE_STATE.PATCHING
|
||||
source_node.hardware_state == HardwareState.ON
|
||||
and source_node.software_state != SoftwareState.PATCHING
|
||||
):
|
||||
source_valid = True
|
||||
else:
|
||||
# IER no longer valid
|
||||
source_valid = False
|
||||
elif source_node.get_type() == TYPE.ACTUATOR:
|
||||
elif source_node.node_type == NodeType.ACTUATOR:
|
||||
# It's an actuator
|
||||
# TO DO
|
||||
pass
|
||||
else:
|
||||
# It's not a switch or an actuator (so active node)
|
||||
if (
|
||||
source_node.get_state() == HARDWARE_STATE.ON
|
||||
and source_node.get_os_state() != SOFTWARE_STATE.PATCHING
|
||||
source_node.hardware_state == HardwareState.ON
|
||||
and source_node.software_state != SoftwareState.PATCHING
|
||||
):
|
||||
if source_node.has_service(protocol):
|
||||
if source_node.service_running(
|
||||
@@ -87,24 +101,24 @@ def apply_iers(network, nodes, links, iers, acl, step):
|
||||
source_valid = False
|
||||
|
||||
# 2. Check the dest node situation
|
||||
if dest_node.get_type() == TYPE.SWITCH:
|
||||
if dest_node.node_type == NodeType.SWITCH:
|
||||
# It's a switch
|
||||
if (
|
||||
dest_node.get_state() == HARDWARE_STATE.ON
|
||||
and dest_node.get_os_state() != SOFTWARE_STATE.PATCHING
|
||||
dest_node.hardware_state == HardwareState.ON
|
||||
and dest_node.software_state != SoftwareState.PATCHING
|
||||
):
|
||||
dest_valid = True
|
||||
else:
|
||||
# IER no longer valid
|
||||
dest_valid = False
|
||||
elif dest_node.get_type() == TYPE.ACTUATOR:
|
||||
elif dest_node.node_type == NodeType.ACTUATOR:
|
||||
# It's an actuator
|
||||
pass
|
||||
else:
|
||||
# It's not a switch or an actuator (so active node)
|
||||
if (
|
||||
dest_node.get_state() == HARDWARE_STATE.ON
|
||||
and dest_node.get_os_state() != SOFTWARE_STATE.PATCHING
|
||||
dest_node.hardware_state == HardwareState.ON
|
||||
and dest_node.software_state != SoftwareState.PATCHING
|
||||
):
|
||||
if dest_node.has_service(protocol):
|
||||
if dest_node.service_running(
|
||||
@@ -123,15 +137,15 @@ def apply_iers(network, nodes, links, iers, acl, step):
|
||||
|
||||
# 3. Check that the ACL doesn't block it
|
||||
acl_block = acl.is_blocked(
|
||||
source_node.get_ip_address(), dest_node.get_ip_address(), protocol, port
|
||||
source_node.ip_address, dest_node.ip_address, protocol, port
|
||||
)
|
||||
if acl_block:
|
||||
if _VERBOSE:
|
||||
print(
|
||||
"ACL block on source: "
|
||||
+ source_node.get_ip_address()
|
||||
+ source_node.ip_address
|
||||
+ ", dest: "
|
||||
+ dest_node.get_ip_address()
|
||||
+ dest_node.ip_address
|
||||
+ ", protocol: "
|
||||
+ protocol
|
||||
+ ", port: "
|
||||
@@ -156,8 +170,8 @@ def apply_iers(network, nodes, links, iers, acl, step):
|
||||
# We might have a switch in the path, so check all nodes are operational
|
||||
for node in path_node_list:
|
||||
if (
|
||||
node.get_state() != HARDWARE_STATE.ON
|
||||
or node.get_os_state() == SOFTWARE_STATE.PATCHING
|
||||
node.hardware_state != HardwareState.ON
|
||||
or node.software_state == SoftwareState.PATCHING
|
||||
):
|
||||
path_valid = False
|
||||
|
||||
@@ -215,7 +229,11 @@ def apply_iers(network, nodes, links, iers, acl, step):
|
||||
pass
|
||||
|
||||
|
||||
def apply_node_pol(nodes, node_pol, step):
|
||||
def apply_node_pol(
|
||||
nodes: Dict[str, NodeType],
|
||||
node_pol: Dict[any, Union[NodeStateInstructionGreen, NodeStateInstructionRed]],
|
||||
step: int,
|
||||
):
|
||||
"""
|
||||
Applies node pattern of life.
|
||||
|
||||
@@ -239,15 +257,15 @@ def apply_node_pol(nodes, node_pol, step):
|
||||
# continue --------------------------
|
||||
node = nodes[node_id]
|
||||
|
||||
if node_pol_type == NODE_POL_TYPE.OPERATING:
|
||||
# Change operating state
|
||||
node.set_state(state)
|
||||
elif node_pol_type == NODE_POL_TYPE.OS:
|
||||
if node_pol_type == NodePOLType.OPERATING:
|
||||
# Change hardware state
|
||||
node.hardware_state = state
|
||||
elif node_pol_type == NodePOLType.OS:
|
||||
# Change OS state
|
||||
# Don't allow PoL to fix something that is compromised. Only the Blue agent can do this
|
||||
if isinstance(node, ActiveNode) or isinstance(node, ServiceNode):
|
||||
node.set_os_state_if_not_compromised(state)
|
||||
elif node_pol_type == NODE_POL_TYPE.SERVICE:
|
||||
node.set_software_state_if_not_compromised(state)
|
||||
elif node_pol_type == NodePOLType.SERVICE:
|
||||
# Change a service state
|
||||
# Don't allow PoL to fix something that is compromised. Only the Blue agent can do this
|
||||
if isinstance(node, ServiceNode):
|
||||
|
||||
@@ -1,22 +1,35 @@
|
||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||
"""Implements POL on the network (nodes and links) resulting from the red agent attack."""
|
||||
from typing import Dict
|
||||
|
||||
from networkx import shortest_path
|
||||
from networkx import MultiGraph, shortest_path
|
||||
|
||||
from primaite.acl.access_control_list import AccessControlList
|
||||
from primaite.common.custom_typing import NodeType
|
||||
from primaite.common.enums import (
|
||||
HARDWARE_STATE,
|
||||
NODE_POL_INITIATOR,
|
||||
NODE_POL_TYPE,
|
||||
SOFTWARE_STATE,
|
||||
TYPE,
|
||||
HardwareState,
|
||||
NodePOLInitiator,
|
||||
NodePOLType,
|
||||
NodeType,
|
||||
SoftwareState,
|
||||
)
|
||||
from primaite.links.link import Link
|
||||
from primaite.nodes.active_node import ActiveNode
|
||||
from primaite.nodes.node_state_instruction_red import NodeStateInstructionRed
|
||||
from primaite.nodes.service_node import ServiceNode
|
||||
from primaite.pol.ier import IER
|
||||
|
||||
_VERBOSE = False
|
||||
|
||||
|
||||
def apply_red_agent_iers(network, nodes, links, iers, acl, step):
|
||||
def apply_red_agent_iers(
|
||||
network: MultiGraph,
|
||||
nodes: Dict[str, NodeType],
|
||||
links: Dict[str, Link],
|
||||
iers: Dict[str, IER],
|
||||
acl: AccessControlList,
|
||||
step: int,
|
||||
):
|
||||
"""
|
||||
Applies IERs to the links (link POL) resulting from red agent attack.
|
||||
|
||||
@@ -54,25 +67,25 @@ def apply_red_agent_iers(network, nodes, links, iers, acl, step):
|
||||
dest_node = nodes[dest_node_id]
|
||||
|
||||
# 1. Check the source node situation
|
||||
if source_node.get_type() == TYPE.SWITCH:
|
||||
if source_node.node_type == NodeType.SWITCH:
|
||||
# It's a switch
|
||||
if source_node.get_state() == HARDWARE_STATE.ON:
|
||||
if source_node.hardware_state == HardwareState.ON:
|
||||
source_valid = True
|
||||
else:
|
||||
# IER no longer valid
|
||||
source_valid = False
|
||||
elif source_node.get_type() == TYPE.ACTUATOR:
|
||||
elif source_node.node_type == NodeType.ACTUATOR:
|
||||
# It's an actuator
|
||||
# TO DO
|
||||
pass
|
||||
else:
|
||||
# It's not a switch or an actuator (so active node)
|
||||
if source_node.get_state() == HARDWARE_STATE.ON:
|
||||
if source_node.hardware_state == HardwareState.ON:
|
||||
if source_node.has_service(protocol):
|
||||
# Red agents IERs can only be valid if the source service is in a compromised state
|
||||
if (
|
||||
source_node.get_service_state(protocol)
|
||||
== SOFTWARE_STATE.COMPROMISED
|
||||
== SoftwareState.COMPROMISED
|
||||
):
|
||||
source_valid = True
|
||||
else:
|
||||
@@ -86,19 +99,19 @@ def apply_red_agent_iers(network, nodes, links, iers, acl, step):
|
||||
source_valid = False
|
||||
|
||||
# 2. Check the dest node situation
|
||||
if dest_node.get_type() == TYPE.SWITCH:
|
||||
if dest_node.node_type == NodeType.SWITCH:
|
||||
# It's a switch
|
||||
if dest_node.get_state() == HARDWARE_STATE.ON:
|
||||
if dest_node.hardware_state == HardwareState.ON:
|
||||
dest_valid = True
|
||||
else:
|
||||
# IER no longer valid
|
||||
dest_valid = False
|
||||
elif dest_node.get_type() == TYPE.ACTUATOR:
|
||||
elif dest_node.node_type == NodeType.ACTUATOR:
|
||||
# It's an actuator
|
||||
pass
|
||||
else:
|
||||
# It's not a switch or an actuator (so active node)
|
||||
if dest_node.get_state() == HARDWARE_STATE.ON:
|
||||
if dest_node.hardware_state == HardwareState.ON:
|
||||
if dest_node.has_service(protocol):
|
||||
# We don't care what state the destination service is in for an IER
|
||||
dest_valid = True
|
||||
@@ -112,15 +125,15 @@ def apply_red_agent_iers(network, nodes, links, iers, acl, step):
|
||||
|
||||
# 3. Check that the ACL doesn't block it
|
||||
acl_block = acl.is_blocked(
|
||||
source_node.get_ip_address(), dest_node.get_ip_address(), protocol, port
|
||||
source_node.ip_address, dest_node.ip_address, protocol, port
|
||||
)
|
||||
if acl_block:
|
||||
if _VERBOSE:
|
||||
print(
|
||||
"ACL block on source: "
|
||||
+ source_node.get_ip_address()
|
||||
+ source_node.ip_address
|
||||
+ ", dest: "
|
||||
+ dest_node.get_ip_address()
|
||||
+ dest_node.ip_address
|
||||
+ ", protocol: "
|
||||
+ protocol
|
||||
+ ", port: "
|
||||
@@ -145,7 +158,7 @@ def apply_red_agent_iers(network, nodes, links, iers, acl, step):
|
||||
# We might have a switch in the path, so check all nodes are operational
|
||||
# We're assuming here that red agents can get past switches that are patching
|
||||
for node in path_node_list:
|
||||
if node.get_state() != HARDWARE_STATE.ON:
|
||||
if node.hardware_state != HardwareState.ON:
|
||||
path_valid = False
|
||||
|
||||
if path_valid:
|
||||
@@ -207,7 +220,12 @@ def apply_red_agent_iers(network, nodes, links, iers, acl, step):
|
||||
pass
|
||||
|
||||
|
||||
def apply_red_agent_node_pol(nodes, iers, node_pol, step):
|
||||
def apply_red_agent_node_pol(
|
||||
nodes: NodeType,
|
||||
iers: Dict[str, IER],
|
||||
node_pol: Dict[str, NodeStateInstructionRed],
|
||||
step: int,
|
||||
):
|
||||
"""
|
||||
Applies node pattern of life.
|
||||
|
||||
@@ -238,22 +256,22 @@ def apply_red_agent_node_pol(nodes, iers, node_pol, step):
|
||||
|
||||
if step >= start_step and step <= stop_step:
|
||||
# continue --------------------------
|
||||
target_node = nodes[target_node_id]
|
||||
target_node: NodeType = nodes[target_node_id]
|
||||
|
||||
# Based the action taken on the initiator type
|
||||
if initiator == NODE_POL_INITIATOR.DIRECT:
|
||||
if initiator == NodePOLInitiator.DIRECT:
|
||||
# No conditions required, just apply the change
|
||||
passed_checks = True
|
||||
elif initiator == NODE_POL_INITIATOR.IER:
|
||||
elif initiator == NodePOLInitiator.IER:
|
||||
# Need to check there is a red IER incoming
|
||||
passed_checks = is_red_ier_incoming(target_node, iers, pol_type)
|
||||
elif initiator == NODE_POL_INITIATOR.SERVICE:
|
||||
elif initiator == NodePOLInitiator.SERVICE:
|
||||
# Need to check the condition of a service on another node
|
||||
source_node = nodes[source_node_id]
|
||||
if source_node.has_service(source_node_service_name):
|
||||
if (
|
||||
source_node.get_service_state(source_node_service_name)
|
||||
== SOFTWARE_STATE[source_node_service_state_value]
|
||||
== SoftwareState[source_node_service_state_value]
|
||||
):
|
||||
passed_checks = True
|
||||
else:
|
||||
@@ -269,16 +287,16 @@ def apply_red_agent_node_pol(nodes, iers, node_pol, step):
|
||||
# Only apply the PoL if the checks have passed (based on the initiator type)
|
||||
if passed_checks:
|
||||
# Apply the change
|
||||
if pol_type == NODE_POL_TYPE.OPERATING:
|
||||
# Change operating state
|
||||
target_node.set_state(state)
|
||||
elif pol_type == NODE_POL_TYPE.OS:
|
||||
if pol_type == NodePOLType.OPERATING:
|
||||
# Change hardware state
|
||||
target_node.hardware_state = state
|
||||
elif pol_type == NodePOLType.OS:
|
||||
# Change OS state
|
||||
if isinstance(target_node, ActiveNode) or isinstance(
|
||||
target_node, ServiceNode
|
||||
):
|
||||
target_node.set_os_state(state)
|
||||
elif pol_type == NODE_POL_TYPE.SERVICE:
|
||||
target_node.software_state = state
|
||||
elif pol_type == NodePOLType.SERVICE:
|
||||
# Change a service state
|
||||
if isinstance(target_node, ServiceNode):
|
||||
target_node.set_service_state(service_name, state)
|
||||
@@ -302,18 +320,18 @@ def is_red_ier_incoming(node, iers, node_pol_type):
|
||||
|
||||
TODO: Write more descriptive docstring with params and returns.
|
||||
"""
|
||||
node_id = node.get_id()
|
||||
node_id = node.node_id
|
||||
|
||||
for ier_key, ier_value in iers.items():
|
||||
if ier_value.get_is_running() and ier_value.get_dest_node_id() == node_id:
|
||||
if (
|
||||
node_pol_type == NODE_POL_TYPE.OPERATING
|
||||
or node_pol_type == NODE_POL_TYPE.OS
|
||||
or node_pol_type == NODE_POL_TYPE.FILE
|
||||
node_pol_type == NodePOLType.OPERATING
|
||||
or node_pol_type == NodePOLType.OS
|
||||
or node_pol_type == NodePOLType.FILE
|
||||
):
|
||||
# It's looking to change operating state, file system or O/S state, so valid
|
||||
# It's looking to change hardware state, file system or SoftwareState, so valid
|
||||
return True
|
||||
elif node_pol_type == NODE_POL_TYPE.SERVICE:
|
||||
elif node_pol_type == NodePOLType.SERVICE:
|
||||
# Check if the service is present on the node and running
|
||||
ier_protocol = ier_value.get_protocol()
|
||||
if isinstance(node, ServiceNode):
|
||||
|
||||
Reference in New Issue
Block a user