Merge remote-tracking branch 'origin/dev' into feature/2319-database-admin

This commit is contained in:
Marek Wolan
2024-03-01 09:23:00 +00:00
88 changed files with 3595 additions and 668 deletions

View File

@@ -1,3 +1,10 @@
# Basic Switched network
#
# -------------- -------------- --------------
# | client_1 |------| switch_1 |------| client_2 |
# -------------- -------------- --------------
#
training_config:
rl_framework: SB3
rl_algorithm: PPO
@@ -134,6 +141,17 @@ simulation:
default_gateway: 192.168.10.1
dns_server: 192.168.1.10
# pre installed services and applications
- ref: client_3
type: computer
hostname: client_3
ip_address: 192.168.10.23
subnet_mask: 255.255.255.0
default_gateway: 192.168.10.1
dns_server: 192.168.1.10
start_up_duration: 0
shut_down_duration: 0
operating_state: "OFF"
# pre installed services and applications
links:
- ref: switch_1___client_1

View File

@@ -0,0 +1,300 @@
# Network with DMZ
#
# An example network configuration with an internal network, a DMZ network and a couple of external networks.
#
# ............................................................................
# . .
# . Internal Network .
# . .
# . -------------- -------------- -------------- .
# . | client_1 |------| switch_1 |--------| router_1 | .
# . -------------- -------------- -------------- .
# . (Computer) | .
# ........................................................|...................
# |
# |
# ........................................................|...................
# . | .
# . DMZ Network | .
# . | .
# . ---------------- -------------- -------------- .
# . | dmz_server |------| switch_2 |------| firewall | .
# . ---------------- -------------- -------------- .
# . (Server) | .
# ........................................................|...................
# |
# External Network |
# |
# |
# ----------------------- -------------- ---------------------
# | external_computer |------| switch_3 |------| external_server |
# ----------------------- -------------- ---------------------
#
training_config:
rl_framework: SB3
rl_algorithm: PPO
seed: 333
n_learn_episodes: 1
n_eval_episodes: 5
max_steps_per_episode: 128
deterministic_eval: false
n_agents: 1
agent_references:
- defender
io_settings:
save_checkpoints: true
checkpoint_interval: 5
save_step_metadata: false
save_pcap_logs: true
save_sys_logs: true
game:
max_episode_length: 256
ports:
- ARP
- DNS
- HTTP
- POSTGRES_SERVER
protocols:
- ICMP
- TCP
- UDP
agents:
- ref: client_1_green_user
team: GREEN
type: GreenWebBrowsingAgent
observation_space:
type: UC2GreenObservation
action_space:
action_list:
- type: DONOTHING
- type: NODE_APPLICATION_EXECUTE
options:
nodes:
- node_name: client_1
applications:
- application_name: WebBrowser
max_folders_per_node: 1
max_files_per_folder: 1
max_services_per_node: 1
max_applications_per_node: 1
reward_function:
reward_components:
- type: DUMMY
agent_settings:
start_settings:
start_step: 5
frequency: 4
variance: 3
simulation:
network:
nodes:
- ref: client_1
type: computer
hostname: client_1
ip_address: 192.168.0.10
subnet_mask: 255.255.255.0
default_gateway: 192.168.0.1
dns_server: 192.168.20.11
start_up_duration: 0
shut_down_duration: 0
- ref: switch_1
type: switch
hostname: switch_1
num_ports: 8
start_up_duration: 0
shut_down_duration: 0
- ref: router_1
type: router
hostname: router_1
num_ports: 5
start_up_duration: 0
shut_down_duration: 0
ports:
1:
ip_address: 192.168.0.1
subnet_mask: 255.255.255.0
2:
ip_address: 192.168.1.1
subnet_mask: 255.255.255.0
acl:
22:
action: PERMIT
src_port: ARP
dst_port: ARP
23:
action: PERMIT
protocol: ICMP
routes:
- address: 192.168.10.10 # route to dmz_server
subnet_mask: 255.255.255.0
next_hop_ip_address: 192.168.1.2
metric: 0
- address: 192.168.20.10 # route to external_computer
subnet_mask: 255.255.255.0
next_hop_ip_address: 192.168.1.2
metric: 0
- address: 192.168.20.11 # route to external_server
subnet_mask: 255.255.255.0
next_hop_ip_address: 192.168.1.2
metric: 0
- ref: dmz_server
type: server
hostname: dmz_server
ip_address: 192.168.10.10
subnet_mask: 255.255.255.0
default_gateway: 192.168.10.1
dns_server: 192.168.20.11
start_up_duration: 0
shut_down_duration: 0
- ref: switch_2
type: switch
hostname: switch_2
num_ports: 8
start_up_duration: 0
shut_down_duration: 0
- ref: firewall
type: firewall
hostname: firewall
start_up_duration: 0
shut_down_duration: 0
ports:
external_port: # port 1
ip_address: 192.168.20.1
subnet_mask: 255.255.255.0
internal_port: # port 2
ip_address: 192.168.1.2
subnet_mask: 255.255.255.0
dmz_port: # port 3
ip_address: 192.168.10.1
subnet_mask: 255.255.255.0
acl:
internal_inbound_acl:
22:
action: PERMIT
src_port: ARP
dst_port: ARP
23:
action: PERMIT
protocol: ICMP
internal_outbound_acl:
22:
action: PERMIT
src_port: ARP
dst_port: ARP
23:
action: PERMIT
protocol: ICMP
dmz_inbound_acl:
22:
action: PERMIT
src_port: ARP
dst_port: ARP
23:
action: PERMIT
protocol: ICMP
dmz_outbound_acl:
22:
action: PERMIT
src_port: ARP
dst_port: ARP
23:
action: PERMIT
protocol: ICMP
external_inbound_acl:
22:
action: PERMIT
src_port: ARP
dst_port: ARP
external_outbound_acl:
22:
action: PERMIT
src_port: ARP
dst_port: ARP
routes:
- address: 192.168.0.10 # route to client_1
subnet_mask: 255.255.255.0
next_hop_ip_address: 192.168.1.1
metric: 0
- ref: switch_3
type: switch
hostname: switch_3
num_ports: 8
start_up_duration: 0
shut_down_duration: 0
- ref: external_computer
type: computer
hostname: external_computer
ip_address: 192.168.20.10
subnet_mask: 255.255.255.0
default_gateway: 192.168.20.1
dns_server: 192.168.20.11
start_up_duration: 0
shut_down_duration: 0
- ref: external_server
type: server
hostname: external_server
ip_address: 192.168.20.11
subnet_mask: 255.255.255.0
default_gateway: 192.168.20.1
start_up_duration: 0
shut_down_duration: 0
services:
- ref: domain_controller_dns_server
type: DNSServer
links:
- ref: client_1___switch_1
endpoint_a_ref: client_1
endpoint_a_port: 1
endpoint_b_ref: switch_1
endpoint_b_port: 1
- ref: router_1___switch_1
endpoint_a_ref: router_1
endpoint_a_port: 1
endpoint_b_ref: switch_1
endpoint_b_port: 8
- ref: router_1___firewall
endpoint_a_ref: firewall
endpoint_a_port: 2 # internal firewall port
endpoint_b_ref: router_1
endpoint_b_port: 2
- ref: firewall___switch_2
endpoint_a_ref: firewall
endpoint_a_port: 3 # dmz firewall port
endpoint_b_ref: switch_2
endpoint_b_port: 8
- ref: dmz_server___switch_2
endpoint_a_ref: dmz_server
endpoint_a_port: 1
endpoint_b_ref: switch_2
endpoint_b_port: 1
- ref: firewall___switch_3
endpoint_a_ref: firewall
endpoint_a_port: 1 # external firewall port
endpoint_b_ref: switch_3
endpoint_b_port: 8
- ref: external_computer___switch_3
endpoint_a_ref: external_computer
endpoint_a_port: 1
endpoint_b_ref: switch_3
endpoint_b_port: 1
- ref: external_server___switch_3
endpoint_a_ref: external_server
endpoint_a_port: 1
endpoint_b_ref: switch_3
endpoint_b_port: 2

View File

@@ -1,9 +1,11 @@
# © Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Optional, Tuple, Union
import pytest
import yaml
from _pytest.monkeypatch import MonkeyPatch
from primaite import getLogger, PRIMAITE_PATHS
from primaite.game.agent.actions import ActionManager
@@ -12,6 +14,7 @@ from primaite.game.agent.observations import ICSObservation, ObservationManager
from primaite.game.agent.rewards import RewardFunction
from primaite.game.game import PrimaiteGame
from primaite.session.session import PrimaiteSession
from primaite.simulator import SIM_OUTPUT
from primaite.simulator.file_system.file_system import FileSystem
from primaite.simulator.network.container import Network
from primaite.simulator.network.hardware.nodes.host.computer import Computer
@@ -29,6 +32,7 @@ from primaite.simulator.system.services.dns.dns_client import DNSClient
from primaite.simulator.system.services.dns.dns_server import DNSServer
from primaite.simulator.system.services.service import Service
from primaite.simulator.system.services.web_server.web_server import WebServer
from tests import TEST_ASSETS_ROOT
from tests.mock_and_patch.get_session_path_mock import temp_user_sessions_path
ACTION_SPACE_NODE_VALUES = 1
@@ -37,6 +41,21 @@ ACTION_SPACE_NODE_ACTION_VALUES = 1
_LOGGER = getLogger(__name__)
@pytest.fixture(scope="function", autouse=True)
def set_syslog_output_to_true():
"""Will be run before each test."""
monkeypatch = MonkeyPatch()
monkeypatch.setattr(
SIM_OUTPUT,
"path",
Path(TEST_ASSETS_ROOT.parent.parent / "simulation_output" / datetime.now().strftime("%Y-%m-%d_%H-%M-%S")),
)
monkeypatch.setattr(SIM_OUTPUT, "save_pcap_logs", True)
monkeypatch.setattr(SIM_OUTPUT, "save_sys_logs", True)
yield
class TestService(Service):
"""Test Service class"""

View File

@@ -0,0 +1,19 @@
from pathlib import Path
from typing import Union
import yaml
from primaite.game.game import PrimaiteGame
from tests import TEST_ASSETS_ROOT
BASIC_CONFIG = TEST_ASSETS_ROOT / "configs/basic_switched_network.yaml"
DMZ_NETWORK = TEST_ASSETS_ROOT / "configs/dmz_network.yaml"
def load_config(config_path: Union[str, Path]) -> PrimaiteGame:
"""Returns a PrimaiteGame object which loads the contents of a given yaml path."""
with open(config_path, "r") as f:
cfg = yaml.safe_load(f)
return PrimaiteGame.from_config(cfg)

View File

@@ -0,0 +1,111 @@
import pytest
from primaite.simulator.network.container import Network
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
from primaite.simulator.network.hardware.nodes.host.computer import Computer
from primaite.simulator.network.hardware.nodes.host.server import Server
from primaite.simulator.network.hardware.nodes.network.firewall import Firewall
from primaite.simulator.network.hardware.nodes.network.router import ACLAction
from primaite.simulator.network.transmission.network_layer import IPProtocol
from primaite.simulator.network.transmission.transport_layer import Port
from tests.integration_tests.configuration_file_parsing import DMZ_NETWORK, load_config
@pytest.fixture(scope="function")
def dmz_config() -> Network:
game = load_config(DMZ_NETWORK)
return game.simulation.network
def test_firewall_is_in_configuration(dmz_config):
"""Test that the firewall exists in the configuration file."""
network: Network = dmz_config
firewall: Firewall = network.get_node_by_hostname("firewall")
assert firewall
assert firewall.operating_state == NodeOperatingState.ON
def test_firewall_routes_are_correctly_added(dmz_config):
"""Test that the firewall routes have been correctly added to and configured in the network."""
network: Network = dmz_config
firewall: Firewall = network.get_node_by_hostname("firewall")
client_1: Computer = network.get_node_by_hostname("client_1")
dmz_server: Server = network.get_node_by_hostname("dmz_server")
external_computer: Computer = network.get_node_by_hostname("external_computer")
external_server: Server = network.get_node_by_hostname("external_server")
# there should be a route to client_1
assert firewall.route_table.find_best_route(client_1.network_interface[1].ip_address)
assert dmz_server.ping(client_1.network_interface[1].ip_address)
assert external_computer.ping(client_1.network_interface[1].ip_address)
assert external_server.ping(client_1.network_interface[1].ip_address)
# client_1 should be able to ping other nodes
assert client_1.ping(dmz_server.network_interface[1].ip_address)
assert client_1.ping(external_computer.network_interface[1].ip_address)
assert client_1.ping(external_server.network_interface[1].ip_address)
def test_firewall_acl_rules_correctly_added(dmz_config):
"""
Test that makes sure that the firewall ACLs have been configured onto the firewall
node via configuration file.
"""
firewall: Firewall = dmz_config.get_node_by_hostname("firewall")
# ICMP and ARP should be allowed internal_inbound
assert firewall.internal_inbound_acl.num_rules == 2
assert firewall.internal_inbound_acl.acl[22].action == ACLAction.PERMIT
assert firewall.internal_inbound_acl.acl[22].src_port == Port.ARP
assert firewall.internal_inbound_acl.acl[22].dst_port == Port.ARP
assert firewall.internal_inbound_acl.acl[23].action == ACLAction.PERMIT
assert firewall.internal_inbound_acl.acl[23].protocol == IPProtocol.ICMP
assert firewall.internal_inbound_acl.implicit_action == ACLAction.DENY
# ICMP and ARP should be allowed internal_outbound
assert firewall.internal_outbound_acl.num_rules == 2
assert firewall.internal_outbound_acl.acl[22].action == ACLAction.PERMIT
assert firewall.internal_outbound_acl.acl[22].src_port == Port.ARP
assert firewall.internal_outbound_acl.acl[22].dst_port == Port.ARP
assert firewall.internal_outbound_acl.acl[23].action == ACLAction.PERMIT
assert firewall.internal_outbound_acl.acl[23].protocol == IPProtocol.ICMP
assert firewall.internal_outbound_acl.implicit_action == ACLAction.DENY
# ICMP and ARP should be allowed dmz_inbound
assert firewall.dmz_inbound_acl.num_rules == 2
assert firewall.dmz_inbound_acl.acl[22].action == ACLAction.PERMIT
assert firewall.dmz_inbound_acl.acl[22].src_port == Port.ARP
assert firewall.dmz_inbound_acl.acl[22].dst_port == Port.ARP
assert firewall.dmz_inbound_acl.acl[23].action == ACLAction.PERMIT
assert firewall.dmz_inbound_acl.acl[23].protocol == IPProtocol.ICMP
assert firewall.dmz_inbound_acl.implicit_action == ACLAction.DENY
# ICMP and ARP should be allowed dmz_outbound
assert firewall.dmz_outbound_acl.num_rules == 2
assert firewall.dmz_outbound_acl.acl[22].action == ACLAction.PERMIT
assert firewall.dmz_outbound_acl.acl[22].src_port == Port.ARP
assert firewall.dmz_outbound_acl.acl[22].dst_port == Port.ARP
assert firewall.dmz_outbound_acl.acl[23].action == ACLAction.PERMIT
assert firewall.dmz_outbound_acl.acl[23].protocol == IPProtocol.ICMP
assert firewall.dmz_outbound_acl.implicit_action == ACLAction.DENY
# ICMP and ARP should be allowed external_inbound
assert firewall.external_inbound_acl.num_rules == 1
assert firewall.external_inbound_acl.acl[22].action == ACLAction.PERMIT
assert firewall.external_inbound_acl.acl[22].src_port == Port.ARP
assert firewall.external_inbound_acl.acl[22].dst_port == Port.ARP
# external_inbound should have implicit action PERMIT
# ICMP does not have a provided ACL Rule but implicit action should allow anything
assert firewall.external_inbound_acl.implicit_action == ACLAction.PERMIT
# ICMP and ARP should be allowed external_outbound
assert firewall.external_outbound_acl.num_rules == 1
assert firewall.external_outbound_acl.acl[22].action == ACLAction.PERMIT
assert firewall.external_outbound_acl.acl[22].src_port == Port.ARP
assert firewall.external_outbound_acl.acl[22].dst_port == Port.ARP
# external_outbound should have implicit action PERMIT
# ICMP does not have a provided ACL Rule but implicit action should allow anything
assert firewall.external_outbound_acl.implicit_action == ACLAction.PERMIT

View File

@@ -0,0 +1,69 @@
import pytest
from primaite.simulator.network.container import Network
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
from primaite.simulator.network.hardware.nodes.host.computer import Computer
from primaite.simulator.network.hardware.nodes.host.server import Server
from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router
from primaite.simulator.network.transmission.network_layer import IPProtocol
from primaite.simulator.network.transmission.transport_layer import Port
from tests.integration_tests.configuration_file_parsing import DMZ_NETWORK, load_config
@pytest.fixture(scope="function")
def dmz_config() -> Network:
game = load_config(DMZ_NETWORK)
return game.simulation.network
def test_router_is_in_configuration(dmz_config):
"""Test that the router exists in the configuration file."""
network: Network = dmz_config
router_1: Router = network.get_node_by_hostname("router_1")
assert router_1
assert router_1.operating_state == NodeOperatingState.ON
def test_router_routes_are_correctly_added(dmz_config):
"""Test that makes sure that router routes have been added from the configuration file."""
network: Network = dmz_config
router_1: Router = network.get_node_by_hostname("router_1")
client_1: Computer = network.get_node_by_hostname("client_1")
dmz_server: Server = network.get_node_by_hostname("dmz_server")
external_computer: Computer = network.get_node_by_hostname("external_computer")
external_server: Server = network.get_node_by_hostname("external_server")
# there should be a route to dmz_server
assert router_1.route_table.find_best_route(dmz_server.network_interface[1].ip_address)
assert client_1.ping(dmz_server.network_interface[1].ip_address)
assert external_computer.ping(dmz_server.network_interface[1].ip_address)
assert external_server.ping(dmz_server.network_interface[1].ip_address)
# there should be a route to external_computer
assert router_1.route_table.find_best_route(external_computer.network_interface[1].ip_address)
assert client_1.ping(external_computer.network_interface[1].ip_address)
assert dmz_server.ping(external_computer.network_interface[1].ip_address)
assert external_server.ping(external_computer.network_interface[1].ip_address)
# there should be a route to external_server
assert router_1.route_table.find_best_route(external_server.network_interface[1].ip_address)
assert client_1.ping(external_server.network_interface[1].ip_address)
assert dmz_server.ping(external_server.network_interface[1].ip_address)
assert external_computer.ping(external_server.network_interface[1].ip_address)
def test_router_acl_rules_correctly_added(dmz_config):
"""Test that makes sure that the router ACLs have been configured onto the router node via configuration file."""
router_1: Router = dmz_config.get_node_by_hostname("router_1")
# ICMP and ARP should be allowed
assert router_1.acl.num_rules == 2
assert router_1.acl.acl[22].action == ACLAction.PERMIT
assert router_1.acl.acl[22].src_port == Port.ARP
assert router_1.acl.acl[22].dst_port == Port.ARP
assert router_1.acl.acl[23].action == ACLAction.PERMIT
assert router_1.acl.acl[23].protocol == IPProtocol.ICMP
assert router_1.acl.implicit_action == ACLAction.DENY

View File

@@ -0,0 +1,44 @@
from primaite.config.load import example_config_path
from primaite.simulator.network.container import Network
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
from primaite.simulator.network.hardware.nodes.host.computer import Computer
from tests.integration_tests.configuration_file_parsing import BASIC_CONFIG, DMZ_NETWORK, load_config
def test_example_config():
"""Test that the example config can be parsed properly."""
game = load_config(example_config_path())
network: Network = game.simulation.network
assert len(network.nodes) == 10 # 10 nodes in example network
assert len(network.routers) == 1 # 1 router in network
assert len(network.switches) == 2 # 2 switches in network
assert len(network.servers) == 5 # 5 servers in network
def test_dmz_config():
"""Test that the DMZ network config can be parsed properly."""
game = load_config(DMZ_NETWORK)
network: Network = game.simulation.network
assert len(network.nodes) == 9 # 9 nodes in network
assert len(network.routers) == 2 # 2 routers in network
assert len(network.switches) == 3 # 3 switches in network
assert len(network.servers) == 2 # 2 servers in network
def test_basic_config():
"""Test that the basic_switched_network config can be parsed properly."""
game = load_config(BASIC_CONFIG)
network: Network = game.simulation.network
assert len(network.nodes) == 4 # 4 nodes in network
client_1: Computer = network.get_node_by_hostname("client_1")
assert client_1.operating_state == NodeOperatingState.ON
client_2: Computer = network.get_node_by_hostname("client_2")
assert client_2.operating_state == NodeOperatingState.ON
# client 3 should not be online
client_3: Computer = network.get_node_by_hostname("client_3")
assert client_3.operating_state == NodeOperatingState.OFF