Repository navigation
Methodology Final
The general layout of the project consists of three major programming pieces that we will create; a backend server, an endpoint agent program, and a management application for the system administrator to use. We will briefly describe the design that we wish to implement for each.
The backend server is a Python program using the Flask library to listen for HTTP requests from the endpoint agent and the management program. It stores information about each endpoint and their respective tasks as TOML files in a standardized format, which is discussed later in this document.
The agent is an executable which is deployed onto each host system that is to be monitored; in our case being two VMs, discussed in more detail below. Our agent executable is written in Go, for ease of portability between different host operating systems, and is responsible for checking in with the backend server, then executing the assigned tasks, and posting the output and any error messages to the backend should the task fail in any way. Status of endpoint agents will be acquired using functions in our API and tools such as Osquery which will then be sent to the backend server for further monitoring and task assignment.
The management program is an Electron NodeJS application that the system administrator(s) run on their own machines in order to view the status of their monitored fleet of hosts, and dispatch tasks to hosts as well as view information about their systems and tasks in progress. We chose Electron because it allows us to develop a desktop-class application while using the standards and technologies that power the modern web, making lots of resources available to us, and allowing for ease of portability between a system administrator’s choice of operating system for their personal computer.
To test that we've successfully built a useful tool for securing and managing endpoints (mostly virtual in Proxmox), we will build our code and test for completeness using the following setup.
To simulate the smallest possible deployment, while still ensuring that we cover both Windows and Linux, the most likely OS types that one might want to monitor, either as a physical machine or a virtual resource, we have chosen the following set of VMs.
- Backend Server - Debian 13 VM - Communicates with endpoints to send tasks and receive results back
- Mgmt Box - Xubuntu 22.04 - Give tasks to endpoints by posting to the backend server
- Test Endpoint 01 - Windows 11 - Sample endpoint system to process our tasks
- Test Endpoint 02 - Debian 13 - Sample endpoint system to process our tasks
To show that we have achieved the level of technical completeness that we strive to have done during this semester, we will run the following tasks through the management system:
On the Debian endpoint, this will entail running apt update && apt list-upgradable, as part of a “get available updates” task, and then after approval from the user, a second task to run apt update && apt upgrade -y.
On Windows, this will entail checking the windows client version [run “ver” command in cmd terminal]. Then running a second powershell script task to start the update process if needed after user approval, [Get-WindowsUpdate; Get-WindowsUpdate -Install -AcceptAll].
To demonstrate that our project takes advantage of having virtualized endpoints in Proxmox, we will also demonstrate the backend server communicating with the hypervisor to take a snapshot of each sample endpoint VM, before dispatching the above noted tasks to apply system updates to each host.
On Debian, we will demonstrate the ability to manage system services by dispatching a task to “start a systemd service” and also “stop a systemd service”, using the cron.service as an example.
On Windows, we will use Powershell with Start-Service and Stop-Service to control system units, using SSH as an example.
On Debian, we will demonstrate the program’s ability to audit the entries of the Cron daemon, to reveal an hourly task called fakerevshell.sh which will be a placeholder for what could be a malicious script or executable.
On Windows, we will use Powershell to examine scheduled tasks, and locate an hourly task called fakerevshell.ps1.
The data storage method that we have chosen for the backend server is flat-file storage of the details of each host in TOML format. This is an easy format for humans to read, while still being structured enough for it to be easy for computer programs to read. Given that we’re running a backend server on just one VM, we don’t expect any real performance overhead from not using a proper database system like MySQL or MariaDB. An example of a host TOML file is shown below, for an Ubuntu system that has received, processed, and posted the result of one sample task, and has one more in a pending state.
ip = "172.16.20.42"
hostname = "freighter-01"
osfamily = "Linux"
os = "Ubuntu 22.04 LTS"
last_seen = "2025-02-18T14:22:03Z"
next_expected = "2025-02-18T14:27:03Z"
[[tasks]]
id = "f981b7c2-8f1d-4f86-a35c-b3fefc0a1dd2"
action = "inventory"
label = "Collect basic host inventory"
status = "queued"
queued_at = "2025-02-18T14:21:45Z"
[tasks.data]
commands = [
"uname -a",
"lsblk --json",
"df -h"
]
[[tasks]]
id = "a0a4a73a-6f26-4f91-a7e7-0fcd5a10c6e9"
action = "debug"
label = "Connectivity probe"
status = "completed"
queued_at = "2025-02-18T14:05:01Z"
started_at = "2025-02-18T14:05:37Z"
finished_at = "2025-02-18T14:06:12Z"
stdout = """Connectivity probe
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=63 time=0.512 ms
--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.512/0.512/0.512/0.000 ms
"""
stderr = ""
[tasks.data]
commands = [
"echo 'Connectivity probe'",
"ping -c 1 10.0.0.1"
]
To ensure that each element of the system communicates securely with each other, we will be using Mutual TLS because it provides bidirectional authentication where both the backend server and the endpoint program can verify each other's identities using X.509 certificates before establishing a connection.
Mutual TLS provides strong bidirectional authentication, ensuring that both the backend API and the endpoint agent verify each other’s identities. This prevents unauthorized hosts from connecting to the backend and blocks the endpoint from talking to rogue servers, directly mitigating impersonation attacks. All communication—such as task dispatch, status reports, and configuration pulls—is encrypted by default, and certificates avoid the common pitfalls of API keys or tokens since they’re harder to extract, can be revoked, and don’t expose static credentials.
Because the endpoint always initiates outbound connections, mTLS fits naturally into the pull-based design without introducing extra complexity. It also works reliably across Linux, Windows, macOS, and BSD through Go’s crypto/tls package, simplifying multi-platform support. Once a basic PKI is in place, issuing certificates to scale from a handful of hosts to hundreds becomes straightforward, making mTLS an operationally efficient choice.
However, it is true that using mTLS introduces some operational overhead because it requires a full public key infrastructure to issue, renew, and revoke certificates for every monitored host. The initial setup involves creating a certificate authority and generating certificates for both the backend and all endpoint agent instances, and ongoing maintenance includes rotating certificates before they expire as well as revoking those belonging to compromised or decommissioned systems. While managing certificates manually is feasible for fewer than ten hosts, scaling beyond a hundred quickly demands automated tooling and secure distribution processes. For the scope of testing our project, we will be establishing a Windows server based CA, with manual certificates, however we will build a mechanism for endpoint agents to request renewal through the backend server.
The stones are capped....