Skip to content
Bolt3zPublic

About

Isolated hardware lab for real-time DDoS detection & inline mitigation: FortiGate + Suricata + edge-AI (Jetson/k3s) closed loop

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

DDoS Detection & Mitigation Lab

🇬🇧 English · 🇮🇹 Italiano

An isolated hardware lab for researching real-time detection and inline mitigation of DoS/DDoS attacks. A FortiGate NGFW segments the network and enforces blocks, a Suricata sensor turns traffic into per-flow features, an edge-AI cluster (NVIDIA Jetson + Turing Pi running k3s) classifies those flows, and a closed-loop orchestrator pushes block decisions back to the firewall over its REST API — a full sensor → AI → enforcement loop on real hardware.

Status: decommissioned. The physical lab no longer exists, so the tests here can't be re-run. This repository is the as-built documentation, the exact configuration commands, and the pipeline/ML code, kept as a reference and portfolio piece. All real credentials have been replaced with <PLACEHOLDER> tokens (see secrets.example.md), and the private IPs/MACs below are historical.


Architecture

   WiFi (uplink for the control laptop only)
     │
 [LAPTOP "parrot"] ── eno1 ──┐   control host + traffic generator
   10.10.40.104 (VLAN40)     │
                             ▼
                     [MikroTik RB5009] ── ether1 (802.1q trunk) ──> [FortiGate 50G] lan3
                      L2 switch core / VLANs                          router + DHCP + inspection
                       │  │  │  │  │                                  lan1 = mgmt 192.168.1.99
                    ether2..ether8 (access ports)                    wan  = 10.99.0.0/24 (lab-only)
                       │  │  │  │  │
        ┌──────────────┘  │  │  │  └───────────────┐
   ether4 = Futro     ether3 = Turing Pi      ether5-8 = Jetson ×3 / firefly
   (VLAN10, target)   (VLAN40, k3s)           (VLAN40, analysis)

The FortiGate sits inline between segments: attacker, benign, target and analysis traffic live in separate VLANs so the firewall can route and inspect between them. The MikroTik (formerly the site's internet router, repurposed) is a pure L2 VLAN switch and also SPAN-mirrors the target port to the Suricata sensor. The laptop reaches every device through the FortiGate's routing while keeping its own internet on WiFi.

Network segmentation

VLAN Name (FortiGate) Subnet / gateway Role
10 lab-target 10.10.10.0/24 — gw .1 Victim server (Futro, nginx)
20 lab-attacker 10.10.20.0/24 — gw .1 Malicious traffic (from laptop)
30 lab-benign 10.10.30.0/24 — gw .1 Benign traffic (from laptop)
40 lab-analyzer 10.10.40.0/24 — gw .1 Analysis / AI stack + management

Inter-VLAN traffic in the lab is routed only, never NATed — so the firewall (and the AI pipeline) sees real source IPs and can block them.


Hardware inventory

Device Board / OS Role
FortiGate 50G FortiOS 7.0.15 Router, DHCP, inter-VLAN inspection, enforcement
MikroTik RB5009 RouterOS 7.18.2 L2 VLAN switch core + hardware port mirror
firefly Firefly ROC-RK3588S, Ubuntu 20.04 DPI sensor (Suricata) + telemetry shipper
jetson-1 Jetson Orin Nano #1, JetPack 6.x AI: RandomForest flow classifier
jetson-2 Jetson Orin Nano #2, JetPack 6.x AI: autoencoder anomaly detector
nano-nanissimo Jetson Orin NX, JetPack R36.3 Data spine: VictoriaMetrics + Grafana + MinIO
Turing Pi 2 4× RK1 modules, Ubuntu 24.04 k3s cluster: NATS bus + orchestrator
Futro x86 thin client Target/victim host (nginx)
laptop "parrot" Control host + traffic generation

⚠️ Naming gotcha: there are two Jetson Orin Nano boards (jetson-1, jetson-2). The node nicknamed "nano" / "nano-nanissimo" is a different board — a Jetson Orin NX — used as the data-spine node, not a third Orin Nano. Three Jetson boards total: 2× Orin Nano + 1× Orin NX.


Detection & mitigation pipeline

The core of the lab is a closed loop from packet capture to firewall enforcement:

flowchart LR
    A[Target traffic] -->|SPAN mirror| B[Suricata<br/>firefly]
    B -->|eve.json flows| C[shipper.py<br/>async tail]
    C -->|lab.features| D[(NATS bus<br/>k3s)]
    D --> E[classifier.py<br/>RandomForest · jetson-1]
    D --> F[autoencoder.py<br/>MLP · jetson-2]
    E -->|lab.verdicts| G[orchestrator.py<br/>k3s]
    F -->|lab.anomalies| G
    G -->|sustained evidence| H[FortiGate REST API<br/>add IP to ddos_blocklist]
    H --> I[Top deny-policy<br/>inline block]
    C -.metrics.-> J[(VictoriaMetrics)]
    J --> K[Grafana dashboard]
Loading
  1. Sense — Suricata on firefly reads a hardware port mirror and emits per-flow records to eve.json. Flow timeouts are tuned low (new=5s / established=30s / closed=5s) so flows surface in near real time instead of minutes later.
  2. Ship — shipper.py tails eve.json asynchronously (asyncio.create_subprocess_exec + await readline) and publishes flow features to the lab.features NATS subject, plus Suricata stats to VictoriaMetrics.
  3. Classify — classifier.py (RandomForest) scores each flow and emits malicious verdicts to lab.verdicts; autoencoder.py flags reconstruction-error outliers to lab.anomalies.
  4. Decide & enforce — orchestrator.py blocks a source IP only after sustained evidence (≥ N malicious verdicts within a time window), then calls the FortiGate REST API to add it to the ddos_blocklist address group, which a top-priority deny policy references — an inline block. A NEVER_BLOCK whitelist protects the target and all gateways.
  5. Observe — packets/s, bytes/s, alerts and total blocks stream to Grafana (grafana-dashboard.json).

The models

Both models use 8 sensor-agnostic flow features — duration, tot_pkts, tot_bytes, pkts_toserver, pkts_toclient, bytes_toserver, bytes_toclient, bytes_per_pkt — chosen so the same model works on both the training data and live Suricata flows (rate/is_tcp features were dropped because their units/precision differ between sources).

  • Training data: CIC-IDS2017 (benign + DoS/DDoS) combined with flows captured from this lab's own floods, giving realistic benign traffic instead of "small flow = attack".
  • Models: RandomForest classifier + MLPRegressor autoencoder + a StandardScaler, trained by gen/train2.py (→ ~99% precision/recall on a realistic test split). Artifacts are read from meta.json at runtime, so re-training and swapping the model needs no code change on the Jetsons — just replace the files and docker restart.

Key findings

  • Per-flow AI vs. volumetric floods. The per-flow path (Suricata → shipper → NATS → classifier → orchestrator) has a throughput ceiling: high-rate floods (thousands of pps) congest the downstream consumers, so blocks arrive late or not at all. Moderate floods (≲ a few hundred pps, e.g. hping3 -i u6000) are detected and blocked in real time (verified: ~80 verdicts → block). For true volumetric DDoS the right tool is the FortiGate DoS-policy (hardware fast-path); the AI path covers moderate-rate and L7 attacks.
  • NAT hides the attacker. With wan→lab NAT enabled, the source collapses to the gateway IP and can't be blocked individually — real tests require NAT off so the source stays visible.
  • Subnet collisions bite. The lab WAN must avoid 10.42.0.0/16 (the k3s/flannel pod CIDR); 10.99.0.0/24 was used instead.
  • Safety first. Block only on sustained evidence (never a single verdict), and keep the target + gateways permanently whitelisted.

Repository layout

Path What it is
docs/en/as-built.md Detailed as-built configuration
docs/en/commands.md Every command used to build the lab, in order
docs/en/runbook.md Original bring-up runbook / plan
docs/en/guide.html Standalone illustrated guide (design decisions & why)
docs/it/ Same four documents in Italian 🇮🇹
services/ On-node agents: shipper, classifier, autoencoder, orchestrator
gen/ Traffic generators (locust/hping3) + model training + flow capture
fgt.sh mt.sh nssh.sh zy_cli.py Non-interactive SSH/CLI wrappers (FortiGate / MikroTik / nodes / Zyxel)
reset-block.sh Clears the FortiGate blocklist (unblock all)
k8s-nats.yaml k8s-orchestrator.yaml k3s manifests for the NATS bus and the orchestrator
nano-compose.yml Data-spine stack (VictoriaMetrics + Grafana + MinIO)
grafana-dashboard.json Grafana dashboard definition
secrets.example.md · .env.example Credential/config templates (fill in locally)

Using the code

The lab is hardware-specific, but the code is reusable. Roughly:

# 1) Control-host tooling (traffic gen + model training)
python3 -m venv genv && genv/bin/pip install -r requirements.txt

# 2) Secrets: export as env vars (see secrets.example.md) — nothing is hardcoded
export FGTPW='...' MTPW='...' FGT_KEY='...'

# 3) Dataset for training: download CIC-IDS2017 "MachineLearningCVE" CSVs into cic/
#    https://www.unb.ca/cic/datasets/ids-2017.html   (gitignored, not shipped here)

# 4) Data-spine stack
cp .env.example .env    # then edit
docker compose -f nano-compose.yml up -d

Full step-by-step (VLANs, DHCP reservations, DoS-policy, IPS, NetFlow, k3s, deployment, and the end-to-end validation loop) is in docs/en/commands.md.


Security note

This is defensive security research run inside an isolated lab the author owns. The traffic generators (hping3, locust, slowloris) are for that closed environment only — don't point them at hosts you don't control. All credentials in this repo are placeholders; the real lab is offline.

License

MIT © 2026 Edoardo Besana

About

Isolated hardware lab for real-time DDoS detection & inline mitigation: FortiGate + Suricata + edge-AI (Jetson/k3s) closed loop

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages