diff --git a/src/primaite/notebooks/Terminal-Processing.ipynb b/src/primaite/notebooks/Terminal-Processing.ipynb new file mode 100644 index 00000000..6a197b03 --- /dev/null +++ b/src/primaite/notebooks/Terminal-Processing.ipynb @@ -0,0 +1,157 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Terminal Processing\n", + "\n", + "© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook serves as a guide on the functionality and use of the new Terminal simulation component.\n", + "\n", + "By default, the Terminal will come pre-installed on any simulation component which inherits from `HostNode`, and simulates the Secure Shell (SSH) protocol as the communication method." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.simulator.system.services.terminal.terminal import Terminal\n", + "from primaite.simulator.network.container import Network\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def basic_network() -> Network:\n", + " \"\"\"Utility function for creating a default network to demonstrate Terminal functionality\"\"\"\n", + " network = Network()\n", + " node_a = Computer(hostname=\"node_a\", ip_address=\"192.168.0.10\", subnet_mask=\"255.255.255.0\", start_up_duration=0)\n", + " node_a.power_on()\n", + " node_b = Computer(hostname=\"node_b\", ip_address=\"192.168.0.11\", subnet_mask=\"255.255.255.0\", start_up_duration=0)\n", + " node_b.power_on()\n", + " network.connect(node_a.network_interface[1], node_b.network_interface[1])\n", + " return network" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "demonstrate how we obtain the Terminal component" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "network: Network = basic_network()\n", + "computer_a: Computer = network.get_node_by_hostname(\"node_a\")\n", + "terminal_a: Terminal = computer_a.software_manager.software.get(\"Terminal\")\n", + "computer_b: Computer = network.get_node_by_hostname(\"node_b\")\n", + "\n", + "# The below can be un-commented when UserSessionManager is implemented. Will need to login before sending any SSH commands\n", + "# to remote.\n", + "# terminal_a.login(username=\"admin\", password=\"Admin123!\", ip_address=computer_b.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Terminal can be used to install new software. The code block below demonstrates how the Terminal class allows the user of `terminal_a`, on `computer_a`, to send a command to `computer_b` to install the `RansomwareScript` application. \n", + "\n", + "Once ran and the command sent, the `RansomwareScript` can be seen in the list of applications on the `node_b Software Manager`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.simulator.network.protocols.ssh import SSHConnectionMessage, SSHPacket, SSHTransportMessage\n", + "from primaite.simulator.system.applications.red_applications.ransomware_script import RansomwareScript\n", + "\n", + "computer_b.software_manager.show()\n", + "\n", + "payload: SSHPacket = SSHPacket(\n", + " payload=[\"software_manager\", \"application\", \"install\", \"RansomwareScript\"],\n", + " transport_message=SSHTransportMessage.SSH_MSG_SERVICE_REQUEST,\n", + " connection_message=SSHConnectionMessage.SSH_MSG_CHANNEL_OPEN,\n", + " sender_ip_address=computer_a.network_interface[1].ip_address,\n", + " target_ip_address=computer_b.network_interface[1].ip_address,\n", + ")\n", + "\n", + "terminal_a.send(payload=payload, dest_ip_address=computer_b.network_interface[1].ip_address)\n", + "\n", + "computer_b.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The below example shows how you can send a command via the terminal to create a folder on the target Node.\n", + "\n", + "Here, we send a command to `computer_b` to create a new folder titled \"Downloads\"." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "computer_b.file_system.show()\n", + "\n", + "payload: SSHPacket = SSHPacket(\n", + " payload=[\"file_system\", \"create\", \"folder\", \"Downloads\"],\n", + " transport_message=SSHTransportMessage.SSH_MSG_SERVICE_REQUEST,\n", + " connection_message=SSHConnectionMessage.SSH_MSG_CHANNEL_OPEN,\n", + " sender_ip_address=computer_a.network_interface[1].ip_address,\n", + " target_ip_address=computer_b.network_interface[1].ip_address,\n", + ")\n", + "\n", + "terminal_a.send(payload=payload, dest_ip_address=computer_b.network_interface[1].ip_address)\n", + "\n", + "computer_b.file_system.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/simulator/system/services/terminal/terminal.py b/src/primaite/simulator/system/services/terminal/terminal.py index 884d3f5b..eae21804 100644 --- a/src/primaite/simulator/system/services/terminal/terminal.py +++ b/src/primaite/simulator/system/services/terminal/terminal.py @@ -20,6 +20,7 @@ from primaite.simulator.network.transmission.network_layer import IPProtocol from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.system.core.software_manager import SoftwareManager from primaite.simulator.system.services.service import Service, ServiceOperatingState +from primaite.simulator.system.software import SoftwareHealthState class TerminalClientConnection(BaseModel): @@ -62,7 +63,10 @@ class Terminal(Service): "Uuid for connection requests" operating_state: ServiceOperatingState = ServiceOperatingState.RUNNING - """Initial Operating State""" + "Initial Operating State" + + health_state_actual: SoftwareHealthState = SoftwareHealthState.GOOD + "Service Health State" remote_connection: Dict[str, TerminalClientConnection] = {}