- changelog added
- added documentation + example of using web server + web browser
- extended web server so that it also accepts ip addresses
- web server can differentiate between a normal page request and one that propagates into a DB request
- rename WebServerService -> WebServer
This commit is contained in:
Czar.Echavez
2023-10-03 16:56:35 +01:00
parent 4b5a73bd32
commit 82da21b073
9 changed files with 180 additions and 16 deletions

View File

@@ -31,6 +31,7 @@ SessionManager.
- Data Manipulator Bot - A red agent service which sends a payload to a target machine. (By default this payload is a SQL query that breaks a database)
- DNS Services: `DNSClient` and `DNSServer`
- FTP Services: `FTPClient` and `FTPServer`
- HTTP Services: `WebBrowser` to simulate a web client and `WebServer`
## [2.0.0] - 2023-07-26

View File

@@ -63,7 +63,7 @@ Implementation
Example Usage
----------
-------------
Dependencies
^^^^^^^^^^^^

View File

@@ -19,3 +19,4 @@ Contents
data_manipulation_bot
dns_client_server
ftp_client_server
web_browser_and_web_server_service

View File

@@ -0,0 +1,110 @@
.. only:: comment
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
Web Browser and Web Server Service
==================================
Web Server Service
------------------
Provides a Web Server simulation by extending the base Service class.
Key capabilities
^^^^^^^^^^^^^^^^
- Simulates a web server with the capability to also request data from a database
- Allows the emulation of HTTP requests between client (e.g. a web browser) and server
- GET request sends a get all users request to the database server and returns an HTTP 200 status if the database is responsive
- Leverages the Service base class for install/uninstall, status tracking, etc.
Usage
^^^^^
- Install on a Node via the ``SoftwareManager`` to start the `WebServer`.
- Service runs on HTTP port 80 by default. (TODO: HTTPS)
Implementation
^^^^^^^^^^^^^^
- HTTP request uses a ``HTTPRequestPacket`` object
- HTTP reaponse uses a ``HTTPResponsePacket`` object
- Extends Service class for integration with ``SoftwareManager``.
Web Browser (Web Client)
------------------------
The ``WebBrowser`` provides a client interface for connecting to the ``WebServer``.
Key features
^^^^^^^^^^^^
- Connects to the ``WebServer`` via the ``SoftwareManager``.
- Simulates HTTP requests and HTTP packet transfer across a network
- Allows the emulation of HTTP requests between client and server:
- Automatically uses ``DNSClient`` to resolve domain names
- GET: performs an HTTP GET request from client to server
- Leverages the Service base class for install/uninstall, status tracking, etc.
Usage
^^^^^
- Install on a Node via the ``SoftwareManager`` to start the ``WebBrowser``.
- Service runs on HTTP port 80 by default. (TODO: HTTPS)
- Execute sending an HTTP GET request with ``get_webpage``
Implementation
^^^^^^^^^^^^^^
- Leverages ``SoftwareManager`` for sending payloads over the network.
- Provides easy interface for making HTTP requests between an HTTP client and server.
- Extends base Service class.
Example Usage
-------------
Dependencies
^^^^^^^^^^^^
.. code-block:: python
from primaite.simulator.network.container import Network
from primaite.simulator.network.hardware.nodes.computer import Computer
from primaite.simulator.network.hardware.nodes.server import Server
from primaite.simulator.system.applications.web_browser import WebBrowser
from primaite.simulator.system.services.web_server.web_server_service import WebServer
Example peer to peer network
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
net = Network()
pc1 = Computer(hostname="pc1", ip_address="192.168.1.50", subnet_mask="255.255.255.0")
srv = Server(hostname="srv", ip_address="192.168.1.10", subnet_mask="255.255.255.0")
pc1.power_on()
srv.power_on()
net.connect(pc1.ethernet_port[1], srv.ethernet_port[1])
Install the Web Server
^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
# web browser is automatically installed in computer nodes
# IRL this is usually included with an OS
client: WebBrowser = pc1.software_manager.software['WebBrowser']
# install web server
srv.software_manager.install(WebServer)
webserv: WebServer = srv.software_manager.software['WebServer']
Open the web page
^^^^^^^^^^^^^^^^^
Using a domain name to connect to a website requires setting up DNS Servers. For this example, it is possible to use the IP address directly
.. code-block:: python
# check that the get request succeeded
print(client.get_webpage("http://192.168.1.10")) # should be True

View File

@@ -14,7 +14,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.ftp.ftp_client import FTPClient
from primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot
from primaite.simulator.system.services.web_server.web_server_service import WebServerService
from primaite.simulator.system.services.web_server.web_server_service import WebServer
def client_server_routed() -> Network:
@@ -260,7 +260,7 @@ def arcd_uc2_network() -> Network:
database_client.run()
database_client.connect()
web_server.software_manager.install(WebServerService)
web_server.software_manager.install(WebServer)
# register the web_server to a domain
dns_server_service: DNSServer = domain_controller.software_manager.software["DNSServer"] # noqa

View File

@@ -74,11 +74,17 @@ class WebBrowser(Application):
domain_exists = dns_client.check_domain_exists(target_domain=parsed_url.hostname)
# if domain does not exist, the request fails
if not domain_exists:
return False
# set current domain name IP address
self.domain_name_ip_address = dns_client.dns_cache[parsed_url.hostname]
if domain_exists:
# set current domain name IP address
self.domain_name_ip_address = dns_client.dns_cache[parsed_url.hostname]
else:
# check if url is an ip address
try:
self.domain_name_ip_address = IPv4Address(parsed_url.hostname)
except Exception:
# unable to deal with this request
self.sys_log.error(f"{self.name}: Unable to resolve URL {url}")
return False
# create HTTPRequest payload
payload = HTTPRequestPacket(request_method=HTTPRequestMethod.GET, request_url=url)

View File

@@ -72,6 +72,11 @@ class DNSClient(Service):
:param: session_id: The Session ID the payload is to originate from. Optional.
:param: is_reattempt: Checks if the request has been reattempted. Default is False.
"""
# check if DNS server is configured
if self.dns_server is None:
self.sys_log.error(f"{self.name}: DNS Server is not configured")
return False
# check if the target domain is in the client's DNS cache
payload = DNSPacket(dns_request=DNSRequest(domain_name_request=target_domain))

View File

@@ -1,5 +1,6 @@
from ipaddress import IPv4Address
from typing import Any, Optional
from urllib.parse import urlparse
from primaite.simulator.network.protocols.http import (
HTTPRequestMethod,
@@ -13,7 +14,7 @@ from primaite.simulator.system.applications.database_client import DatabaseClien
from primaite.simulator.system.services.service import Service
class WebServerService(Service):
class WebServer(Service):
"""Class used to represent a Web Server Service in simulation."""
def __init__(self, **kwargs):
@@ -76,13 +77,20 @@ class WebServerService(Service):
"""
response = HTTPResponsePacket(status_code=HTTPStatusCode.BAD_REQUEST, payload=payload)
try:
# get data from DatabaseServer
db_client: DatabaseClient = self.software_manager.software["DatabaseClient"]
# get all users
if db_client.query("SELECT * FROM user;"):
parsed_url = urlparse(payload.request_url)
if parsed_url.path is None or len(parsed_url.path) < 1:
# query succeeded
response.status_code = HTTPStatusCode.OK
if parsed_url.path.startswith("/users"):
# get data from DatabaseServer
db_client: DatabaseClient = self.software_manager.software["DatabaseClient"]
# get all users
if db_client.query("SELECT * FROM user;"):
# query succeeded
response.status_code = HTTPStatusCode.OK
return response
except Exception:
# something went wrong on the server

View File

@@ -1,18 +1,51 @@
from primaite.simulator.network.hardware.nodes.computer import Computer
from primaite.simulator.network.hardware.nodes.server import Server
from primaite.simulator.network.protocols.http import HTTPStatusCode
from primaite.simulator.system.applications.application import ApplicationOperatingState
from primaite.simulator.system.applications.web_browser import WebBrowser
from primaite.simulator.system.services.service import ServiceOperatingState
def test_web_page_get_request(uc2_network):
"""Test to see if the client retrieves the correct web files."""
def test_web_page_home_page(uc2_network):
"""Test to see if the browser is able to open the main page of the web server."""
client_1: Computer = uc2_network.get_node_by_hostname("client_1")
web_client: WebBrowser = client_1.software_manager.software["WebBrowser"]
web_client.run()
assert web_client.operating_state == ApplicationOperatingState.RUNNING
assert web_client.get_webpage("http://arcd.com/index.html") is True
assert web_client.get_webpage("http://arcd.com/") is True
# latest reponse should have status code 200
assert web_client.latest_response is not None
assert web_client.latest_response.status_code == HTTPStatusCode.OK
def test_web_page_get_users_page_request_with_domain_name(uc2_network):
"""Test to see if the client can handle requests with domain names"""
client_1: Computer = uc2_network.get_node_by_hostname("client_1")
web_client: WebBrowser = client_1.software_manager.software["WebBrowser"]
web_client.run()
assert web_client.operating_state == ApplicationOperatingState.RUNNING
assert web_client.get_webpage("http://arcd.com/users/") is True
# latest reponse should have status code 200
assert web_client.latest_response is not None
assert web_client.latest_response.status_code == HTTPStatusCode.OK
def test_web_page_get_users_page_request_with_ip_address(uc2_network):
"""Test to see if the client can handle requests that use ip_address."""
client_1: Computer = uc2_network.get_node_by_hostname("client_1")
web_client: WebBrowser = client_1.software_manager.software["WebBrowser"]
web_client.run()
web_server: Server = uc2_network.get_node_by_hostname("web_server")
web_server_ip = web_server.nics.get(next(iter(web_server.nics))).ip_address
assert web_client.operating_state == ApplicationOperatingState.RUNNING
assert web_client.get_webpage(f"http://{web_server_ip}/users/") is True
# latest reponse should have status code 200
assert web_client.latest_response is not None