Skip to content
This repository was archived by the owner on Jun 11, 2026. It is now read-only.

JoaoRuss0/sirs

Repository files navigation

DeathNode

Contents

This repository contains documentation and source code for the Network and Computer Security (SIRS) project.

The REPORT document provides a detailed overview of the key technical decisions and various components of the implemented project. It offers insights into the rationale behind these choices, the project's architecture, and the impact of these decisions on the overall functionality and performance of the system.

This document presents installation and demonstration instructions.

Installation

To see the project in action, it is necessary to setup a virtual environment, with 3 networks and 7 machines.

The following diagram shows the networks and machines:

Network Topology

Prerequisites

All the virtual machines are based on Kali Linux 64-bit.

Follow the official SIRS guide to install VirtualBox / VMware Fusion and set up a Kali base VM (KALI):

SIRS Kali VM Setup Guide

The KALI VM itself is never used directly, it acts as a template for all other machines.

Machine configurations

For each machine, there is an initialization script named init.sh located in the folder corresponding to that machine. The script installs all the necessary packages and makes all required configurations.

To avoid repeating some steps of the setup common to every VM, do these next steps on a single virtual machine that you will then clone for every component in our project.

Start by cloning this repository:

NOTE: You may need to create and add a ssh key to your github profile in order to clone the repository. Follow this guide.

$ git clone https://github.com/tecnico-sec/A57-DeathNode

Run the common-init.sh script that performs all common setup tasks required on all machines.

Note: If any pop up appears, choose YES.

$ cd A57-DeathNode
$ sudo ./common-init.sh

After running common-init.sh, the script will shut down the KALI VM. There is no need to boot it up again, proceed to the next step.

Cloning the Project Machines

After preparing an initial VM linked clone it n times, once for each of the following components: DATABASE, API, VIGILANT, GATEWAY, CLIENT1, CLIENT2 and CLIENT3

Setup

Networking Setup

Custom networks will have to be created for the project to work as intended. In VMWare Fusion, create it by editing /Library/Preferences/VMware\ Fusion/networking, and add the following lines to the file:

    answer VNET_2_HOSTONLY_NETMASK 255.255.255.0
    answer VNET_2_HOSTONLY_SUBNET 192.168.0.0
    answer VNET_3_HOSTONLY_NETMASK 255.255.255.0
    answer VNET_3_HOSTONLY_SUBNET 192.168.1.0
    answer VNET_4_HOSTONLY_NETMASK 255.255.255.0
    answer VNET_4_HOSTONLY_SUBNET 192.168.2.0
  • You can add them right after the last answer VNET_1... line.
  • Very important: do NOT edit anything else on this file. VMWare reserves vmnet1 and vmnet8 for its own use, and you should not change those.
  • Save and exit;
  • Re-open VMWare Fusion;

The VMs will have to have the following adapters (each associated with a specific network):

VM ADAPTER TYPE IP ADDRESS
DATABASE CUSTOM (vmnet4) 192.168.2.10/24
API CUSTOM (vmnet4) 192.168.2.250/24
CUSTOM (vmnet3) 192.168.1.10/24
VIGILANT CUSTOM (vmnet4) 192.168.2.240/24
CUSTOM (vmnet3) 192.168.1.20/24
GATEWAY CUSTOM (vmnet3) 192.168.1.250/24
CUSTOM (vmnet2) 192.168.0.250/24
SHARED WITH THE HOST -
CLIENT 1 CUSTOM (vmnet2) 192.168.0.2/24
CLIENT 2 CUSTOM (vmnet2) 192.168.0.3/24
CLIENT 3 CUSTOM (vmnet2) 192.168.0.4/24

After adding the adapters to each VM, you can boot all of them. Proceed to fill the GATEWAY's, API's and VIGILANT's init.sh interface MAC variables in the beginning of the file, with the corresponding interface MAC addresses of each connected interface/adapter.

Note: (The MAC addresses can be found in each VM settings, on the adapter section.) The 'SHARED WITH THE HOST' NIC/adapter has the tendency to change its MAC address after the VM boots, be careful.``

Initializing the VMs

The init.sh will set up the VMs with the required interface and firewall configurations, additionally, it also installs some needed packages specific to the current VM.

  • On the GATEWAY VM, inside A57-DeathNode/gateway/, run sudo ./init.sh.
  • On the API VM, inside A57-DeathNode/api/, run sudo ./init.sh.
  • On the VIGILANT VM, inside A57-DeathNode/vigilant/, run sudo ./init.sh.
  • On the DATABASE VM, inside A57-DeathNode/database/, run sudo ./init.sh.
  • On the CLIENT VMs, inside A57-DeathNode/client/, run sudo ./init.sh and choose a different IP for each, additionally, create your own 'local.env' file based on the template.

Note: Follow this exact order, the gateway and other VMs close to the gateway forward internet traffic from inside the network. If not, e.g. Postgres will not be installed on the Database VM.

Running the Project

  • On the API VM, run ./start.sh.
  • On the Vigilant VM, run ./start.sh.
  • On each Client VM run ./start.sh

Database Machine

This machine runs a PostgreSQL 17 and will store the reports that were submitted to the network, as well as which clients reported which reports as invalid, it also tracks which clients are currently banned from using the network.

Verify the following:

$ systemctl status postgresql
$ tail -f /var/log/postgresql/postgresql-17-main.log
  • The first command should report that postgresql.service is active and enabled.
  • The second command should report:
LOG:  listening on IPv4 address "0.0.0.0", port 5432
LOG:  listening on IPv6 address "::", port 5432
LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
LOG:  database system was shut down at 2025-12-20 15:12:01 WET
LOG:  database system is ready to accept connections

Additionally, run:

$ sudo -u postgres psql
# Type the password, 'postgresql'
postgres=# \c deathnode;
postgres=# \dt;

We expect the follwing output:

You are now connected to database "deathnode" as user "postgres".
...
                   List of relations
 Schema |           Name            | Type  |  Owner   
--------+---------------------------+-------+----------
 public | ban                       | table | postgres
 public | peer_report_invalidations | table | postgres
 public | report                    | table | postgres
(3 rows)

API Machine

This machine runs a Quarkus 3.30.3 REST API, it allows clients to submit, invalidate and pull new reports.

After running ./start.sh verify that no JDBC or any other type of errors pop up in quarkus logs. The following should appear:

__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
...
(...) Listening on: https://0.0.0.0:443

When it comes to network interfaces, you should have two configured, one in which the assigned IP is 192.168.1.10 (to the Gateway) and another in which the assigned IP is 192.168.2.250 (to the database).

Vigilant Machine

This machine runs a Quarkus 3.30.3 REST API, it allows client requests to be authenticated by using the TLS certificate used to open the TLS TCP session with the gateway.

After running ./start.sh verify that no JDBC or any other type of errors pop up in quarkus logs. The following should appear:

__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
...
(...) Listening on: https://0.0.0.0:443

When it comes to network interfaces, you should have two configured, one in which the assigned IP is 192.168.1.20 (to the Gateway) and another in which the assigned IP is 192.168.2.240 (to the database).

Gateway Machine

This machine runs NGINX, it redirects client requests to be authenticated first (using the Vigilant API), if needed, and also redirects requests to the Server API.

Verify the following:

$ systemctl status nginx
$ tail -f /var/log/nginx/error.log
  • The first command should report that nginx.service is active and enabled.
  • The second command should not print any errors.

When it comes to network interfaces, you should have three configured, one in which the assigned IP is 192.168.1.250 (connected to the API and Vigilant), another in which the assigned IP is 192.168.0.250 (to the clients) and another configured with DHCP, used to install the needed software.

Client Machine 1-3

This machine runs a Java CLI which is able to communicate with the Server API to push newly created reports or pull new reports from it.

Verify the follwing after running ./start.sh:

DocumentTool> sync

We expect no errors in the output, it should return the following:

Pulling new reports from server...
Sync complete.

When it comes to network interfaces, you should have a single one configured on each VM, in which the assigned IP was the one chosen in the init.sh script (to the Gateway).

Demonstration

Now that all the networks and machines are up and running, we can start creating reports and syncing with the Server.

After running ./start.sh on a client, to see the available commands run help, you should see:

help_menu

We can start by creating a report, run create-report and fill the fields:

create_report

We can now list the reports we created but are not yet synced with the Server, run list-reports:

list_reports

Next we will sync with the Server, run sync. A playload is sent containing the reports we created:

Example:

{
  "reports": [
    {
      "id": "00bde5e2-736d-48c2-9592-4c0686990d1f",
      "version": 1,
      "timestamp": "2025-12-20T23:15:02.587246887Z",
      "dek": "np0/NqKxaA72VLEAFMqWXnVaCpBZtgXw52S39x+pqvQ=",
      "historyHash": "l2BMXuiB2VGVM+eRRFaiiYGVxx4SindPt59qMAr6ooYHjw7rKoGPKhNHpF1gK5SfjEh492lAOnuzNGgjZEpDdw==",
      "prevHash": "LVEEdSQPuNleihLT4l5yDxJ64Fh1DYem9ryq+pJJejPgveWy61X3dv582eHg7wlACcgaz52LXkhXI8W1Ax+ZSA==",
      "inner": "ZSQV9fPi+rmK0aGqx9TsrCqKmTkAdhEsYzMjZDqSAoFZDq7zRNX8Zyl50tMev/LafxNcnao9AM2AA9+pf2whxfJ2PiuzNlpGCLyOl8Lr9L7+6X84SpBiIKfP23wNkMHiwxQhBK0hn/MpH903Aq9LKtMShq9Ctpg6F1+G5D9S4GAkKchAJz98DeDTHfu3eUTv8UauPkAP1cs0AH3o7I0wV0xlAbJwY5NguCmApRwOJGyhUJ1un7jKKPtZ15mVpyxK18P74LXI1p3Ob6RS5ecKZPm7iRGiHuqvU8z1NafArqfyUGo6+PLOjCfJjvA1qp4OGXiPf0MzoafuaQYv61nD4OomSbYlsWfiBoesf4BTrhb3T6PF1Rr2sbot7FTEloun88QfcSRYUnde0w2JeBnKpg==",
      "signature": "lhwhP8oaDw7HvD4rnHWTofa5d32SwulQCMI5YNDMDs08Iz6qznvvD9kzODCZ4Sflv/C+lT5XPsB0QqjT+pJNkV/a12ZaCcP/uT68pg0SZR1qIktcCgMrjpEfl9/Qi1Y9pzbqkiH9TrDzDKylY2Q7zKf1jTvKas5gzPnmQkp9yVJWgP1AGSqwH9uXBBPX0j5tn82TVmV4+ZvThnNpG1ZQ+H886Dui/ufYkzWpKFMLNFzHb4ajZYZMjR8svuDAyKXo00zJewxNytDSXFPoBSSZZ6V+hcpNzoYPeHI0ojL60uWOB8y85d4joIdbrMMQ7MF2JgSJ9AtRuxaSthgg9nQcFFalBkmsRsnFuYE3GFBGntJkTQVz3rTmwObKFXXsXpjovXRBEmtP2xDZyZoM67EbxvuGkqDicm4ux7m0zQhH7whbnLYx/3Pe8QgW+QXSm1ObpRUANbZ/2NBjHu0vTGv9Nrc9AcApu6ep07KsttSoAah8yoEAPSssZ9G2cSjD3szJ5LFbImnnN9UfSKmOcCMCERviSx+SDuWaeE+Sr4KPBMpJILH7kemHF10Put02T/4rC+GSCrKd/ulealai0N2I+gwrRMU/2oNto6KX4h3+rGr0yOsSbyoenePdZtMorLFAtv7E3f4Tl4kLWPuzjuOL+31xek0c32vMaAqY6V5lCjI="
    }
  ]
}

sync

database_report_1

We can also verify that our report has been moved to the already synced reports:

already_synced_1

On another client, running sync will make it pull all the not synced reports from the Server:

Example payload:

[
  {
    "id": "1cfa3cbc-c68c-4539-8e88-0b61a5f9d850",
    "version": 1,
    "timestamp": "2025-12-20T22:15:20.407189Z",
    "dek": "0XCQnqlw8PIdKN2pQbwCy6/QpsrH0jB1DvsmpzuLRYs=",
    "historyHash": "PPQSEohVmKLur/wQV39pZ0pFj/tvAPUD2tEPIz/sKZXMFtfxZavMflfGpYk+7vGXAGvluPRjgvbOwP8ZLW/Lsg==",
    "prevHash": "bgrOEsoknsdNguILeRQ1shFr1R1BJHnVBoNezuqfflxhxDpEJoGSDeRMfF0zvhcfynSr1GNWSyfIcfRPi+nhfQ==",
    "inner": "A1NFF0l3YcdyO6l2WckAi8SKg8d/7qfpj1xUETREAHWpQBtNskKWTlE2eNqcVgEfBkGTIbp3/hZfnVDgMYDWDVeN4aQpn3QjI+tukWaXXkDbneU+VyIq3yxhTzeEjR+HRa5b7FODULlzjeYHitimuEgWo166xnDdYLXMznEtYDtP/yb1L4pxJJrw2fEtC2LvcYDjyFbIiwSIYI9fEDG88/ETjPuU6pEagkM9iyMFhCIVrzw1f4KqraOhj+NxR+kIt9AR7F7mA4rEFX3bn9JcXjLYMCzdpxZW3YXx214+0c7Z8Sskh8se+xKMGo1M4ZGDGgAnXyVEpjhKQrUovpZm1qhyxhs0Zc8AxbXee199XiqyVioc157f4jw+XPPk2eX04sJaLdSu02IVhu78xRhGEg==",
    "signature": "YbSjylBmnFUK3l2O/9gNXRsTmtq3+1Enq3LpGM/OH87SXeHLbSS0xfBRcbGkicf3vtv6KheVXAlOwe0T2AGTnHYizJdocMXT+9TVa0zhXV/nuNXLJhaUKLE1m2mfaERir0F1A8KbVNXjZNS3MadI6XVlYMryo2x2WMOT3E1DWIH4QcXVnHysnVCCtt2QyuilduTn7E7WylrsGtMYSo1GycG98AlTfaOVLnuMl2IpiSfe31xdLA4AAkctg++MsrQa42+MZ/kZiN4Hd1Tw8OBEqhR/qpSU5+DjH2huDAW6exkv12QbLlOr3HswvAAt6I8f1ZN8qlXeBMdtNKxpyudM6dxgYrdTY0uwm3iETKSLfIPrPGTYUbvbWez3xB/yguoja4DDxmMUEjhYKunXoi4tc9IUbTdjDYwAWX0J1YqVF1Q9zFT7X6anVbpl9sIi97RxGw94kQPykE3jhNyjMcIn6fFCx7J4LGIn1NVg0/4adiy3IbtMtACPxrSHGAY0b3Fbz11InIEHTo7DQ4wg1X1VWL2x0/Fk66C5L4JF3oYChDbpfDZL9+2FJP0y+WVwNU2970g7ube8CNgflnjTFgz4oQ32AjeRwKOV9u17gUJXlHJam+N8WXUqG6dg6US5IkY9f0jI23ISU4tgVD2uWLC3JapsthTsdGM8OZUlbk/IEa4="
  }
]

sync_client_2

As can also be seen in the image above, through wireshark, the request and responses are ciphered since we use TLS, an attacker would not be able to read whatever is sent or received by the client over the network regardless of where he stands in our network.

All communication is protected between all components, CLIENTS <-> NGINX, NGINX <-> API|VIGILANT and API|VIGILANT <-> DB.

wireshark.png

Apart from these commands, we can also update an existing report:

updated_report

The report will be added to our reports that are not yet synced with the Server. We can then sync:

updated_synced_client_1

And pull the new version from another client:

updated_synced_client_2

Moreover, we can add a new client whose reports will all be invalid to demonstrate how bans work.

To do so, the simplest way is to remove this client's certificate from the certificates the clients have of one another. These are present in the clientX/certs/ directory. Do it for the two clients that are not the "bad" clients.

After that we can create some reports for this new client and sync them:

bad_client_reports

After they are synced, other clients can pull them. They will try to validate the reports but fail.

Posteriorly, the validating clients report the invalid reports back to the Server (using the /reports endpoints).

Example Payload:

[
    {"id": "7c69094c-2859-447e-85f9-70d915873b73", "version": 4},
    {"id": "ee1d1a26-ca93-44a1-9c51-d7538c138448", "version": 23}
]

good_client_sync_invalid.png

The report table then tracks how many times the report has been invalidated.

invalidations_per_report.png

(We make sure clients can't report the same report twice, by tracking which client reported which reports in an extra table called peer_report_invalidations. To know which client called the /reports endpoint, we added a custom header, X-SSL-Client-Cert , to the request by configuring NGINX. It contains the certificate used by the client during the TLS handshake.)

If a majority of clients have invalidated a report, it will be tagged as invalid.

When it is tagged as invalid, there is a new table, ban (in which we store how many invalid reports each client has sent) that gets updated. We increment the report's client invalid_report_count.

bans_per_client.png

When this number is between (and including) 2 to 5, the client is banned once temporarily, for 1 minute.

During this time he can only pull reports from the server with sync, he can't push or report reports from other clients as invalid.

client_sync_forbidden.png

If more than 5 reports have been tagged as invalid, the client is banned permanently.

This authorization mechanism was achieved by configuring NGINX to make an additional request to our Vigilant API.

This API accesses the database and queries the ban table, in which we track the number of invalid reports per client (also using the TLS Handshake certificate to identify the client).

This concludes the demonstration.

Additional Information

Links to Used Tools and Libraries

Versioning

We use GIT for versioning.

License

This project is licensed under the MIT License - see the LICENSE.txt for details.

Releases

No releases published

Packages

 
 
 

Contributors