Merge remote-tracking branch 'origin/dev' into feature/2417-observation-refactor

This commit is contained in:
Marek Wolan
2024-04-01 19:21:40 +01:00
28 changed files with 3073 additions and 512 deletions

View File

@@ -10,16 +10,24 @@
# 4. Check that the simulation has changed in the way that I expect.
# 5. Repeat for all actions.
from ipaddress import IPv4Address
from typing import Tuple
import pytest
import yaml
from primaite.game.agent.interface import ProxyAgent
from primaite.game.game import PrimaiteGame
from primaite.session.environment import PrimaiteGymEnv
from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus
from primaite.simulator.network.transmission.network_layer import IPProtocol
from primaite.simulator.network.transmission.transport_layer import Port
from primaite.simulator.system.applications.application import ApplicationOperatingState
from primaite.simulator.system.applications.web_browser import WebBrowser
from primaite.simulator.system.software import SoftwareHealthState
from tests import TEST_ASSETS_ROOT
FIREWALL_ACTIONS_NETWORK = TEST_ASSETS_ROOT / "configs/firewall_actions_network.yaml"
def test_do_nothing_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
@@ -93,9 +101,9 @@ def test_node_service_fix_integration(game_and_agent: Tuple[PrimaiteGame, ProxyA
assert svc.health_state_actual == SoftwareHealthState.GOOD
def test_network_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
def test_router_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
"""
Test that the NetworkACLAddRuleAction can form a request and that it is accepted by the simulation.
Test that the RouterACLAddRuleAction can form a request and that it is accepted by the simulation.
The ACL starts off with 4 rules, and we add a rule, and check that the ACL now has 5 rules.
"""
@@ -112,8 +120,9 @@ def test_network_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, Pro
# 2: Add a rule to block client 1 from reaching server 2 on router
action = (
"NETWORK_ACL_ADDRULE",
"ROUTER_ACL_ADDRULE",
{
"target_router_nodename": "router",
"position": 4, # 4th rule
"permission": 2, # DENY
"source_ip_id": 3, # 10.0.1.2 (client_1)
@@ -136,8 +145,9 @@ def test_network_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, Pro
# 4: Add a rule to block server_1 from reaching server_2 on router (this should not affect comms as they are on same subnet)
action = (
"NETWORK_ACL_ADDRULE",
"ROUTER_ACL_ADDRULE",
{
"target_router_nodename": "router",
"position": 5, # 5th rule
"permission": 2, # DENY
"source_ip_id": 5, # 10.0.2.2 (server_1)
@@ -155,8 +165,8 @@ def test_network_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, Pro
assert server_1.ping("10.0.2.3") # Can ping server_2
def test_network_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
"""Test that the NetworkACLRemoveRuleAction can form a request and that it is accepted by the simulation."""
def test_router_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
"""Test that the RouterACLRemoveRuleAction can form a request and that it is accepted by the simulation."""
game, agent = game_and_agent
# 1: Check that http traffic is going across the network nicely.
@@ -171,8 +181,9 @@ def test_network_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame,
# 2: Remove rule that allows HTTP traffic across the network
action = (
"NETWORK_ACL_REMOVERULE",
"ROUTER_ACL_REMOVERULE",
{
"target_router_nodename": "router",
"position": 3, # 4th rule
},
)
@@ -187,8 +198,8 @@ def test_network_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame,
assert client_1.ping("10.0.2.3")
def test_network_nic_disable_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
"""Test that the NetworkNICDisableAction can form a request and that it is accepted by the simulation."""
def test_host_nic_disable_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
"""Test that the HostNICDisableAction can form a request and that it is accepted by the simulation."""
game, agent = game_and_agent
# 1: Check that client_1 can access the network
@@ -203,7 +214,7 @@ def test_network_nic_disable_integration(game_and_agent: Tuple[PrimaiteGame, Pro
# 2: Disable the NIC on client_1
action = (
"NETWORK_NIC_DISABLE",
"HOST_NIC_DISABLE",
{
"node_id": 0, # client_1
"nic_id": 0, # the only nic (eth-1)
@@ -222,8 +233,8 @@ def test_network_nic_disable_integration(game_and_agent: Tuple[PrimaiteGame, Pro
assert server_1.ping("10.0.2.3")
def test_network_nic_enable_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
"""Test that the NetworkNICEnableAction can form a request and that it is accepted by the simulation."""
def test_host_nic_enable_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
"""Test that the HostNICEnableAction can form a request and that it is accepted by the simulation."""
game, agent = game_and_agent
@@ -234,7 +245,7 @@ def test_network_nic_enable_integration(game_and_agent: Tuple[PrimaiteGame, Prox
# 2: Use action to enable nic
action = (
"NETWORK_NIC_ENABLE",
"HOST_NIC_ENABLE",
{
"node_id": 0, # client_1
"nic_id": 0, # the only nic (eth-1)
@@ -332,8 +343,8 @@ def test_network_router_port_disable_integration(game_and_agent: Tuple[PrimaiteG
action = (
"NETWORK_PORT_DISABLE",
{
"node_id": 3, # router
"port_id": 0, # port 1
"target_nodename": "router", # router
"port_id": 1, # port 1
},
)
agent.store_action(action)
@@ -364,8 +375,8 @@ def test_network_router_port_enable_integration(game_and_agent: Tuple[PrimaiteGa
action = (
"NETWORK_PORT_ENABLE",
{
"node_id": 3, # router
"port_id": 0, # port 1
"target_nodename": "router", # router
"port_id": 1, # port 1
},
)
agent.store_action(action)
@@ -455,3 +466,141 @@ def test_node_application_close_integration(game_and_agent: Tuple[PrimaiteGame,
game.step()
assert browser.operating_state == ApplicationOperatingState.CLOSED
def test_node_application_install_and_uninstall_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]):
"""Test that the NodeApplicationInstallAction and NodeApplicationRemoveAction can form a request and that
it is accepted by the simulation.
When you initiate a install action, the Application will be installed and configured on the node.
The remove action will uninstall the application from the node."""
game, agent = game_and_agent
client_1 = game.simulation.network.get_node_by_hostname("client_1")
assert client_1.software_manager.software.get("DoSBot") is None
action = ("NODE_APPLICATION_INSTALL", {"node_id": 0, "application_name": "DoSBot", "ip_address": "192.168.1.14"})
agent.store_action(action)
game.step()
assert client_1.software_manager.software.get("DoSBot") is not None
action = ("NODE_APPLICATION_REMOVE", {"node_id": 0, "application_name": "DoSBot"})
agent.store_action(action)
game.step()
assert client_1.software_manager.software.get("DoSBot") is None
def test_firewall_acl_add_remove_rule_integration():
"""
Test that FirewallACLAddRuleAction and FirewallACLRemoveRuleAction can form a request and that it is accepted by the simulation.
Check that all the details of the ACL rules are correctly added to each ACL list of the Firewall.
Check that rules are removed as expected.
"""
with open(FIREWALL_ACTIONS_NETWORK, "r") as f:
cfg = yaml.safe_load(f)
env = PrimaiteGymEnv(game_config=cfg)
# 1: Check that traffic is normal and acl starts off with 4 rules.
firewall = env.game.simulation.network.get_node_by_hostname("firewall")
assert firewall.internal_inbound_acl.num_rules == 2
assert firewall.internal_outbound_acl.num_rules == 2
assert firewall.dmz_inbound_acl.num_rules == 2
assert firewall.dmz_outbound_acl.num_rules == 2
assert firewall.external_inbound_acl.num_rules == 1
assert firewall.external_outbound_acl.num_rules == 1
env.step(1) # Add ACL rule to Internal Inbound
assert firewall.internal_inbound_acl.num_rules == 3
assert firewall.internal_inbound_acl.acl[1].action.name == "PERMIT"
assert firewall.internal_inbound_acl.acl[1].src_ip_address == IPv4Address("192.168.0.10")
assert firewall.internal_inbound_acl.acl[1].dst_ip_address is None
assert firewall.internal_inbound_acl.acl[1].dst_port is None
assert firewall.internal_inbound_acl.acl[1].src_port is None
assert firewall.internal_inbound_acl.acl[1].protocol is None
env.step(2) # Remove ACL rule from Internal Inbound
assert firewall.internal_inbound_acl.num_rules == 2
env.step(3) # Add ACL rule to Internal Outbound
assert firewall.internal_outbound_acl.num_rules == 3
assert firewall.internal_outbound_acl.acl[1].action.name == "DENY"
assert firewall.internal_outbound_acl.acl[1].src_ip_address == IPv4Address("192.168.0.10")
assert firewall.internal_outbound_acl.acl[1].dst_ip_address is None
assert firewall.internal_outbound_acl.acl[1].dst_port == Port.DNS
assert firewall.internal_outbound_acl.acl[1].src_port == Port.ARP
assert firewall.internal_outbound_acl.acl[1].protocol == IPProtocol.ICMP
env.step(4) # Remove ACL rule from Internal Outbound
assert firewall.internal_outbound_acl.num_rules == 2
env.step(5) # Add ACL rule to DMZ Inbound
assert firewall.dmz_inbound_acl.num_rules == 3
assert firewall.dmz_inbound_acl.acl[1].action.name == "DENY"
assert firewall.dmz_inbound_acl.acl[1].src_ip_address == IPv4Address("192.168.10.10")
assert firewall.dmz_inbound_acl.acl[1].dst_ip_address == IPv4Address("192.168.0.10")
assert firewall.dmz_inbound_acl.acl[1].dst_port == Port.HTTP
assert firewall.dmz_inbound_acl.acl[1].src_port == Port.HTTP
assert firewall.dmz_inbound_acl.acl[1].protocol == IPProtocol.UDP
env.step(6) # Remove ACL rule from DMZ Inbound
assert firewall.dmz_inbound_acl.num_rules == 2
env.step(7) # Add ACL rule to DMZ Outbound
assert firewall.dmz_outbound_acl.num_rules == 3
assert firewall.dmz_outbound_acl.acl[2].action.name == "DENY"
assert firewall.dmz_outbound_acl.acl[2].src_ip_address == IPv4Address("192.168.10.10")
assert firewall.dmz_outbound_acl.acl[2].dst_ip_address == IPv4Address("192.168.0.10")
assert firewall.dmz_outbound_acl.acl[2].dst_port == Port.HTTP
assert firewall.dmz_outbound_acl.acl[2].src_port == Port.HTTP
assert firewall.dmz_outbound_acl.acl[2].protocol == IPProtocol.TCP
env.step(8) # Remove ACL rule from DMZ Outbound
assert firewall.dmz_outbound_acl.num_rules == 2
env.step(9) # Add ACL rule to External Inbound
assert firewall.external_inbound_acl.num_rules == 2
assert firewall.external_inbound_acl.acl[10].action.name == "DENY"
assert firewall.external_inbound_acl.acl[10].src_ip_address == IPv4Address("192.168.20.10")
assert firewall.external_inbound_acl.acl[10].dst_ip_address == IPv4Address("192.168.10.10")
assert firewall.external_inbound_acl.acl[10].dst_port == Port.POSTGRES_SERVER
assert firewall.external_inbound_acl.acl[10].src_port == Port.POSTGRES_SERVER
assert firewall.external_inbound_acl.acl[10].protocol == IPProtocol.ICMP
env.step(10) # Remove ACL rule from External Inbound
assert firewall.external_inbound_acl.num_rules == 1
env.step(11) # Add ACL rule to External Outbound
assert firewall.external_outbound_acl.num_rules == 2
assert firewall.external_outbound_acl.acl[1].action.name == "DENY"
assert firewall.external_outbound_acl.acl[1].src_ip_address == IPv4Address("192.168.20.10")
assert firewall.external_outbound_acl.acl[1].dst_ip_address == IPv4Address("192.168.0.10")
assert firewall.external_outbound_acl.acl[1].dst_port is None
assert firewall.external_outbound_acl.acl[1].src_port is None
assert firewall.external_outbound_acl.acl[1].protocol is None
env.step(12) # Remove ACL rule from External Outbound
assert firewall.external_outbound_acl.num_rules == 1
def test_firewall_port_disable_enable_integration():
"""
Test that NetworkPortEnableAction and NetworkPortDisableAction can form a request and that it is accepted by the simulation.
"""
with open(FIREWALL_ACTIONS_NETWORK, "r") as f:
cfg = yaml.safe_load(f)
env = PrimaiteGymEnv(game_config=cfg)
firewall = env.game.simulation.network.get_node_by_hostname("firewall")
assert firewall.dmz_port.enabled == True
env.step(13) # Disable Firewall DMZ Port
assert firewall.dmz_port.enabled == False
env.step(14) # Enable Firewall DMZ Port
assert firewall.dmz_port.enabled == True