Initial commit of v1.0.0. Updated the .gitignore for the standard Python gitignore. Added Azure DevOps release pipeline for proper artifact release from the start.

This commit is contained in:
Chris McCarthy
2023-03-28 17:33:34 +01:00
parent fdbd5903bc
commit 8fc0316253
45 changed files with 5086 additions and 20 deletions

View File

@@ -0,0 +1 @@
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.

View File

@@ -0,0 +1,95 @@
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
"""
An Active Node (i.e. not an actuator)
"""
from nodes.node import Node
from common.enums import *
class ActiveNode(Node):
"""
Active Node class
"""
def __init__(self, _id, _name, _type, _priority, _state, _ip_address, _os_state, _config_values):
"""
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
"""
super().__init__(_id, _name, _type, _priority, _state, _config_values)
self.ip_address = _ip_address
self.os_state = _os_state
self.patching_count = 0
def set_ip_address(self, _ip_address):
"""
Sets IP address
Args:
_ip_address: IP address
"""
self.ip_address = _ip_address
def get_ip_address(self):
"""
Gets IP address
Returns:
IP address
"""
return self.ip_address
def set_os_state(self, _os_state):
"""
Sets operating system state
Args:
_os_state: Operating system state
"""
self.os_state = _os_state
if _os_state == SOFTWARE_STATE.PATCHING:
self.patching_count = self.config_values.os_patching_duration
def set_os_state_if_not_compromised(self, _os_state):
"""
Sets operating system state if the node is not compromised
Args:
_os_state: Operating system state
"""
if self.os_state != SOFTWARE_STATE.COMPROMISED:
self.os_state = _os_state
if _os_state == SOFTWARE_STATE.PATCHING:
self.patching_count = self.config_values.os_patching_duration
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

176
PRIMAITE/nodes/node.py Normal file
View File

@@ -0,0 +1,176 @@
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
"""
The base Node class
"""
from common.enums import *
class Node:
"""
Node class
"""
def __init__(self, _id, _name, _type, _priority, _state, _config_values):
"""
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
"""
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
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
def turn_off(self):
"""
Sets the node state to OFF
"""
self.operating_state = HARDWARE_STATE.OFF
def reset(self):
"""
Sets the node state to Resetting and starts the reset count
"""
self.operating_state = HARDWARE_STATE.RESETTING
self.resetting_count = self.config_values.node_reset_duration
def update_resetting_status(self):
"""
Updates the resetting count
"""
self.resetting_count -= 1
if self.resetting_count <= 0:
self.resetting_count = 0
self.operating_state = HARDWARE_STATE.ON

View File

@@ -0,0 +1,104 @@
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
"""
Defines node behaviour for PoL
"""
class NodeStateInstruction(object):
"""
The Node State Instruction class
"""
def __init__(self, _id, _start_step, _end_step, _node_id, _node_pol_type, _service_name, _state, _is_entry_node=False):
"""
Init
Args:
_id: The node state instruction id
_start_step: The start step of the instruction
_end_step: The end step of the instruction
_node_id: The id of the associated node
_node_pol_type: The pattern of life type
_service_name: The service name
_state: The state (node or service)
_is_entry_node: Indicator for entry node (default = False)
"""
self.id = _id
self.start_step = _start_step
self.end_step = _end_step
self.node_id = _node_id
self.node_pol_type = _node_pol_type
self.service_name = _service_name # Not used when not a service instruction
self.state = _state
self.is_entry_node = _is_entry_node
def get_start_step(self):
"""
Gets the start step
Returns:
The start step
"""
return self.start_step
def get_end_step(self):
"""
Gets the end step
Returns:
The end step
"""
return self.end_step
def get_node_id(self):
"""
Gets the node ID
Returns:
The node ID
"""
return self.node_id
def get_node_pol_type(self):
"""
Gets the node pattern of life type (enum)
Returns:
The node pattern of life type (enum)
"""
return self.node_pol_type
def get_service_name(self):
"""
Gets the service name
Returns:
The service name
"""
return self.service_name
def get_state(self):
"""
Gets the state (node or service)
Returns:
The state (node or service)
"""
return self.state
def get_is_entry_node(self):
"""
Informs of entry node
Returns:
True if entry node
"""
return self.is_entry_node

View File

@@ -0,0 +1,37 @@
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
"""
The Passive Node class (i.e. an actuator)
"""
from nodes.node import Node
class PassiveNode(Node):
"""
The Passive Node class
"""
def __init__(self, _id, _name, _type, _priority, _state, _config_values):
"""
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
"""
# Pass through to Super for now
super().__init__(_id, _name, _type, _priority, _state, _config_values)
def get_ip_address(self):
"""
Gets the node IP address
Returns:
The node IP address
"""
# No concept of IP address for passive nodes for now
return ""

View File

@@ -0,0 +1,161 @@
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
"""
A Service Node (i.e. not an actuator)
"""
from nodes.active_node import ActiveNode
from common.enums import *
class ServiceNode(ActiveNode):
"""
ServiceNode class
"""
def __init__(self, _id, _name, _type, _priority, _state, _ip_address, _os_state, _config_values):
"""
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
"""
super().__init__(_id, _name, _type, _priority, _state, _ip_address, _os_state, _config_values)
self.services = {}
def add_service(self, _service):
"""
Adds a service to the node
Args:
_service: The service to add
"""
self.services[_service.get_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):
"""
Indicates whether a service is on a node
Returns:
True if service (protocol) is on the node
"""
for service_key, service_value in self.services.items():
if service_key == _protocol:
return True
else:
pass
return False
def service_running(self, _protocol):
"""
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
"""
for service_key, service_value in self.services.items():
if service_key == _protocol:
if service_value.get_state() != SOFTWARE_STATE.PATCHING:
return True
else:
return False
else:
pass
return False
def service_is_overwhelmed(self, _protocol):
"""
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
"""
for service_key, service_value in self.services.items():
if service_key == _protocol:
if service_value.get_state() == SOFTWARE_STATE.OVERWHELMED:
return True
else:
return False
else:
pass
return False
def set_service_state(self, _protocol, _state):
"""
Sets the state of a service (protocol) on the node
Args:
_protocol: The service (protocol)
_state: The state value
"""
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:
service_value.patching_count = self.config_values.service_patching_duration
else:
# Do nothing
pass
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
"""
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
def get_service_state(self, _protocol):
"""
Gets the state of a service
Returns:
The state of the service
"""
for service_key, service_value in self.services.items():
if service_key == _protocol:
return service_value.get_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:
service_value.reduce_patching_count()