poc4 wit rust
This commit is contained in:
@@ -1,6 +1,37 @@
|
||||
# MikroSuricata
|
||||
|
||||
Project version: `0.9.1`
|
||||
Project version: `0.11.2`
|
||||
|
||||
|
||||
## What changed in 0.11.2
|
||||
|
||||
- Replaced full-traffic RouterOS Packet Sniffer capture with a **hybrid TZSP path**: routed IPv4 uses `/ip firewall mangle action=sniff-tzsp`, while Packet Sniffer streams only `filter-mac-protocol=!ip` traffic such as ARP, IPv6, LLDP and PPPoE.
|
||||
- The capture deploy no longer assumes a production bridge, VLAN or interface name. `TZSP_L2_INTERFACE=` defaults to RouterOS `filter-interface=all`; set it only when a site intentionally wants to constrain the low-volume non-IPv4 stream.
|
||||
- Packet Sniffer TZSP destination now tries the current `streaming-server=IP:PORT` syntax first and automatically falls back to legacy split `streaming-server`/`streaming-port` syntax.
|
||||
- The project-owned IPv4 mangle rule is identified by the exact comment `MikroSuricata TZSP IPv4`; deployment replaces only that rule and leaves unrelated mangle rules untouched. RouterOS `sniff-tzsp` continues processing subsequent mangle rules after cloning the packet.
|
||||
- Added `scripts/configure-routeros-tzsp-hybrid.sh` to migrate an existing installation without rebuilding the container, including `DRY_RUN=true` preview mode.
|
||||
- Replaced the VLAN-specific RouterOS template with `routeros/02-tzsp-hybrid.rsc` and added detailed capture coverage, limitations and migration documentation in `docs/routeros-tzsp-capture.md`.
|
||||
|
||||
## What changed in 0.11.0
|
||||
|
||||
- Reworked the Rust TZSP data-plane for RouterOS containers that cannot raise kernel `rmem_max`: UDP receive and TZSP decode/TAP injection now run on **separate threads**.
|
||||
- The RX thread does only blocking `recvmmsg(MSG_WAITFORONE)` into reusable preallocated batches. It never waits for TAP, Suricata, Python, Redis or the UI.
|
||||
- Added a bounded **userspace packet queue** (`TZSP_QUEUE_MB`, default 64 MiB). With the default 256-packet batch and 12 KiB slots this provides roughly half a second of burst absorption at 1 Gbit/s even when the kernel socket is limited to ~416 KiB.
|
||||
- If downstream processing falls behind after the userspace queue fills, the RX thread keeps draining UDP into a scratch batch and records explicit `queue_dropped_datagrams` instead of silently turning all loss into kernel UDP drops.
|
||||
- Increased the default receive batch from 64 to **256** and made the per-datagram buffer configurable (`TZSP_DATAGRAM_BYTES`, default 12288; up to 65535). Oversized/truncated datagrams are counted explicitly.
|
||||
- Added live capture diagnostics: TZSP ingress rate, inspected rate, capture efficiency, userspace queue fill, kernel drops/s, queue drops/s, truncated packets and RX/TAP worker health.
|
||||
- The overview now labels the main value as **Inspected throughput** and separately shows TZSP ingress, so a RouterOS mirror limitation can be distinguished from a Suricata/TAP bottleneck immediately.
|
||||
- Prometheus exports the new Rust queue/capture-health gauges and drop counters.
|
||||
|
||||
## What changed in 0.10.0
|
||||
|
||||
- Moved the complete TZSP packet hot-path out of Python into a dedicated **Rust data-plane process**. Rust now owns batched UDP receive (`recvmmsg`), TZSP decoding, direction accounting and TAP injection; packet bytes no longer cross into Python.
|
||||
- Added configurable UDP receive buffering and batched receive. In 0.11.0 the hot path was further split into dedicated RX/TAP threads with a userspace queue.
|
||||
- Added 1 Hz Unix-socket telemetry from Rust to the Python control plane. The dashboard now receives live throughput independently from retained Redis history, so **Throughput now** is current instead of reusing a stale historical payload.
|
||||
- Added receiver observability to the UI/health/Prometheus path: actual receive-buffer size, Rust PID, telemetry age and kernel UDP drop counter.
|
||||
- Fixed the overview refresh path so throughput history is refreshed during periodic UI refreshes, not only at initial load/range changes.
|
||||
- The old `FORENSIC_PCAP_MODE=blocks` pre-event ring required Python to process every frame. With the isolated Rust data-plane it is treated as a legacy setting and safely falls back to Suricata alert PCAP capture. The new default is `alerts`.
|
||||
- Docker now compiles and tests the Rust receiver in a dedicated build stage and copies only the stripped receiver binary into the runtime image. The existing RouterOS amd64/arm64/armv7 image workflow is preserved.
|
||||
|
||||
|
||||
## What changed in 0.9.1
|
||||
@@ -70,7 +101,7 @@ A lightweight IDS stack designed to run as a **single container on MikroTik Rout
|
||||
- Security analytics add anomalies, NXDOMAIN counts, encrypted/cleartext session counts, local/remote endpoint inventory, passive IP/MAC asset observations, top signatures/severities and JA4/JA3/HASSH fingerprint inventory.
|
||||
- The built-in local ruleset adds NXDOMAIN/DNS-rate signals, outbound SMB/SMTP/FTP policy detections, exposed database-service probes and an internal administrative/lateral-movement burst detector.
|
||||
|
||||
RouterOS mirrors selected traffic with TZSP, the container decodes the frames into a TAP interface, Suricata analyzes them, and the Python service stores EVE alerts in SQLite and exposes a small web dashboard.
|
||||
RouterOS mirrors traffic with a hybrid TZSP path: routed IPv4 is cloned by an IPv4 mangle `sniff-tzsp` rule, while Packet Sniffer handles non-IPv4 Ethernet traffic. A dedicated Rust process decodes and injects the frames into TAP, Suricata analyzes them, and the Python control plane handles EVE/SQLite/NDR/API/UI without sitting in the packet hot-path.
|
||||
|
||||
## NDR / MikroTik-specific options
|
||||
|
||||
@@ -93,28 +124,39 @@ All NDR state, IOC data, Redis persistence, Suricata logs/rules and forensic PCA
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
VLAN / RouterOS traffic
|
||||
|
|
||||
v
|
||||
RouterOS Packet Sniffer
|
||||
|
|
||||
| TZSP UDP/37008
|
||||
v
|
||||
RouterOS routed IPv4 ----> mangle sniff-tzsp ---------+
|
||||
| TZSP UDP/37008
|
||||
RouterOS non-IPv4 -----> Packet Sniffer (!ip) --------+
|
||||
v
|
||||
single RouterOS container
|
||||
Debian slim
|
||||
+ Python TZSP receiver
|
||||
+ Rust TZSP data-plane (recvmmsg -> TZSP -> TAP)
|
||||
+ TAP suritap0
|
||||
+ Suricata IDS
|
||||
+ EVE JSON watcher
|
||||
+ Python control plane / EVE JSON watcher
|
||||
+ SQLite alerts / assets / NDR incidents / sessions
|
||||
+ MikroSuricata behavior + correlation engine
|
||||
+ local IOC datasets (IP/domain/SHA256/JA3/JA4/HASSH)
|
||||
+ Redis/RAM bounded traffic history
|
||||
+ WebSocket live stream
|
||||
+ Redis traffic history
|
||||
+ Rust -> Python 1 Hz Unix telemetry
|
||||
+ WebSocket live throughput / event stream
|
||||
+ Web UI :8080
|
||||
+ optional RouterOS REST blocking
|
||||
```
|
||||
|
||||
The capture path is intentionally independent from the UI/control plane:
|
||||
|
||||
```text
|
||||
RouterOS TZSP UDP
|
||||
|
|
||||
v
|
||||
Rust receiver -- recvmmsg() --> TZSP decode --> TAP write --> Suricata
|
||||
|
|
||||
+-- 1 Hz counters only --> Unix datagram --> Python --> Redis / WebSocket / Prometheus
|
||||
```
|
||||
|
||||
If Redis, the browser or an analytics request is slow, it cannot block UDP receive/TAP injection.
|
||||
|
||||
The RouterOS deployment workflow is:
|
||||
|
||||
```text
|
||||
@@ -342,7 +384,7 @@ Every feed update is transactional at the merged-rules level: the existing `suri
|
||||
```dotenv
|
||||
UPDATE_RULES_ON_START=false
|
||||
RULE_UPDATE_INTERVAL_HOURS=24
|
||||
FORENSIC_PCAP_MODE=blocks
|
||||
FORENSIC_PCAP_MODE=alerts
|
||||
FORENSIC_PCAP_WINDOW_SECONDS=60
|
||||
FORENSIC_PCAP_MEMORY_MB=64
|
||||
FORENSIC_PCAP_MAX_FILES=32
|
||||
@@ -584,15 +626,15 @@ Then deploy by giving the **RouterOS-side TAR path** directly:
|
||||
./scripts/deploy-routeros.sh routeros-suricata-tzsp-arm64.tar
|
||||
```
|
||||
|
||||
The deployer no longer builds, detects image architecture, renames, or re-uploads the image. For project version `0.9.0` it creates:
|
||||
The deployer no longer builds, detects image architecture, renames, or re-uploads the image. For project version `0.11.2` it creates:
|
||||
|
||||
```text
|
||||
name=suricata_0.9.0
|
||||
name=suricata_0.11.2
|
||||
file=routeros-suricata-tzsp-arm64.tar
|
||||
root-dir=/containers/suricata_0.9.0/root
|
||||
root-dir=/containers/suricata_0.11.2/root
|
||||
```
|
||||
|
||||
The remaining deployment work is unchanged: bridge/VETH/NAT, environment, persistent mounts, optional RouterOS REST user/firewall integration, TZSP sniffer configuration, image extraction wait, container start, and final status. Existing containers are not removed. Re-running deployment for the same version stops with `Container suricata_0.9.0 already exists`.
|
||||
The remaining deployment work is unchanged: private container bridge/VETH/NAT, environment, persistent mounts, optional RouterOS REST user/firewall integration, hybrid TZSP capture configuration, image extraction wait, container start, and final status. Existing containers are not removed. Re-running deployment for the same version stops with `Container suricata_0.11.2 already exists`.
|
||||
|
||||
For SSH key authentication set:
|
||||
|
||||
@@ -644,7 +686,7 @@ RouterOS templates are located in `routeros/`:
|
||||
|
||||
```text
|
||||
01-container-network.rsc
|
||||
02-sniffer-vlan100.rsc
|
||||
02-tzsp-hybrid.rsc
|
||||
03-rest-and-firewall.rsc
|
||||
04-container-import-amd64.rsc
|
||||
04-container-import-arm64.rsc
|
||||
@@ -686,26 +728,50 @@ Replacing or restarting the application container does not remove any of these f
|
||||
|
||||
## TZSP capture
|
||||
|
||||
The default deployment configuration can configure RouterOS Packet Sniffer to stream VLAN traffic to the container address on UDP port `37008`.
|
||||
The default RouterOS deployment uses a hybrid capture model:
|
||||
|
||||
Relevant settings in `deploy-routeros.env`:
|
||||
|
||||
```dotenv
|
||||
CONFIGURE_SNIFFER=true
|
||||
START_SNIFFER=true
|
||||
VLAN_ID=100
|
||||
```text
|
||||
routed IPv4 -> /ip firewall mangle action=sniff-tzsp
|
||||
non-IPv4 L2 -> /tool sniffer filter-mac-protocol=!ip
|
||||
\_______________________________/
|
||||
TZSP UDP/37008
|
||||
```
|
||||
|
||||
If the router already uses Packet Sniffer for another purpose, disable automatic sniffer configuration:
|
||||
Relevant `deploy-routeros.env` settings:
|
||||
|
||||
```dotenv
|
||||
CONFIGURE_SNIFFER=false
|
||||
START_SNIFFER=false
|
||||
TZSP_PORT=37008
|
||||
CONFIGURE_TZSP_CAPTURE=true
|
||||
CONFIGURE_IPV4_MANGLE=true
|
||||
CONFIGURE_L2_SNIFFER=true
|
||||
START_L2_SNIFFER=true
|
||||
TZSP_L2_INTERFACE=
|
||||
TZSP_L2_MAC_PROTOCOL=!ip
|
||||
```
|
||||
|
||||
Then configure the capture manually.
|
||||
No production bridge/VLAN name is assumed. Empty `TZSP_L2_INTERFACE` maps to RouterOS `filter-interface=all`. To intentionally constrain non-IPv4 capture, set an exact interface name, for example `TZSP_L2_INTERFACE=bridge-core`.
|
||||
|
||||
Hardware-offloaded bridge traffic may require additional verification on the specific RouterOS device because some switched traffic can bypass software capture paths.
|
||||
The IPv4 rule is added to `chain=forward` and is owned by the comment `MikroSuricata TZSP IPv4`. Only that project-owned rule is replaced; unrelated mangle rules are left intact. `sniff-tzsp` clones the packet and then continues to subsequent mangle rules.
|
||||
|
||||
The Packet Sniffer complement uses `filter-mac-protocol=!ip`. RouterOS defines `ip` as IPv4 EtherType `0x0800` and `ipv6` separately as `0x86DD`, so this stream includes ARP, IPv6, LLDP, PPPoE and other non-IPv4 Ethernet protocols. `filter-stream=yes` prevents the sniffer's own TZSP stream from being recaptured.
|
||||
|
||||
Current RouterOS builds may expose the target as `streaming-server=IP:PORT`; older builds/documentation may expose `streaming-server` plus `streaming-port`. Deployment tries the combined form first and falls back automatically.
|
||||
|
||||
To migrate an already running installation without changing the container image:
|
||||
|
||||
```bash
|
||||
./scripts/configure-routeros-tzsp-hybrid.sh
|
||||
```
|
||||
|
||||
Preview without modifying RouterOS:
|
||||
|
||||
```bash
|
||||
DRY_RUN=true ./scripts/configure-routeros-tzsp-hybrid.sh
|
||||
```
|
||||
|
||||
Packet Sniffer is global RouterOS state. If another administrative workflow owns `/tool/sniffer`, use `CONFIGURE_L2_SNIFFER=false` and manage the low-volume L2 complement separately. Hardware-offloaded bridge-only traffic may also be invisible to Packet Sniffer on some devices.
|
||||
|
||||
Full coverage details, caveats, migration behavior and verification commands are in [`docs/routeros-tzsp-capture.md`](docs/routeros-tzsp-capture.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -721,15 +787,29 @@ The pipeline is:
|
||||
|
||||
```text
|
||||
TZSP datagram
|
||||
-> Python decoder
|
||||
-> Rust batched UDP receiver
|
||||
-> Rust TZSP decoder
|
||||
-> Ethernet frame
|
||||
-> TAP suritap0
|
||||
-> Suricata
|
||||
-> eve.json
|
||||
-> Python EVE watcher
|
||||
-> Python control plane / EVE watcher
|
||||
-> SQLite / Web UI
|
||||
```
|
||||
|
||||
The Rust receiver emits only a compact 1 Hz telemetry sample to Python. That sample drives the live throughput card and is persisted asynchronously for historical throughput charts.
|
||||
|
||||
Useful receive-path tuning:
|
||||
|
||||
```dotenv
|
||||
TZSP_RCVBUF_BYTES=33554432
|
||||
TZSP_BATCH_SIZE=256
|
||||
TZSP_QUEUE_MB=64
|
||||
TZSP_DATAGRAM_BYTES=12288
|
||||
```
|
||||
|
||||
On RouterOS the kernel may cap the actual socket buffer around a few hundred KiB regardless of `TZSP_RCVBUF_BYTES`. The 0.11.0 receiver therefore does not rely on sysctl: a dedicated RX thread drains UDP into the preallocated userspace queue. The UI exposes both kernel drops and userspace queue drops, plus TZSP ingress versus traffic actually injected into Suricata.
|
||||
|
||||
If the RouterOS container cannot create the TAP interface, the application will fail early with an error related to `/dev/net/tun`, `TUNSETIFF`, or permissions. This is the main platform-specific capability to validate on the target router.
|
||||
|
||||
---
|
||||
@@ -778,11 +858,14 @@ Full RouterOS/container application entry point
|
||||
app/dev_web.py
|
||||
Local web-only entry point
|
||||
|
||||
app/tzsp.py
|
||||
TZSP receiver and decoder
|
||||
rust/tzsp-receiver/
|
||||
Production Rust TZSP receiver: batched UDP receive, decoder, counters and TAP injection
|
||||
|
||||
app/tap.py
|
||||
TAP interface handling
|
||||
app/tzsp_rust.py
|
||||
Rust process supervision plus 1 Hz telemetry bridge; no packet bytes enter Python
|
||||
|
||||
app/tzsp.py / app/tap.py
|
||||
Legacy decoder/TAP helpers retained for focused tests; not used by the production packet path
|
||||
|
||||
app/eve.py
|
||||
Suricata EVE JSON watcher
|
||||
@@ -862,7 +945,7 @@ The default configuration is observation-oriented:
|
||||
AUTO_BLOCK=false
|
||||
ALERT_MAX_SEVERITY=2
|
||||
UPDATE_RULES_ON_START=false
|
||||
FORENSIC_PCAP_MODE=blocks
|
||||
FORENSIC_PCAP_MODE=alerts
|
||||
ROUTEROS_PASSWORD=CHANGE_ME
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=
|
||||
@@ -875,7 +958,7 @@ Keep automatic firewall actions disabled until the capture path and alert qualit
|
||||
|
||||
## Image-only upgrade on an already configured RouterOS
|
||||
|
||||
After the first deployment, when `veth-ids`, bridge/NAT, TZSP sniffer, `IDS_ENV` and `IDS_MOUNTS` already exist, do not run the full deploy just to change the image.
|
||||
After the first deployment, when the VETH/private bridge/NAT, hybrid TZSP capture, `IDS_ENV` and `IDS_MOUNTS` already exist, do not run the full deploy just to change the image.
|
||||
|
||||
1. Upload the ready TAR only:
|
||||
|
||||
@@ -889,18 +972,18 @@ After the first deployment, when `veth-ids`, bridge/NAT, TZSP sniffer, `IDS_ENV`
|
||||
./scripts/upgrade-routeros-container.sh routeros-suricata-tzsp-arm64.tar
|
||||
```
|
||||
|
||||
For version `0.9.0` the second command creates:
|
||||
For version `0.11.2` the second command creates:
|
||||
|
||||
```text
|
||||
name=suricata_0.9.0
|
||||
name=suricata_0.11.2
|
||||
file=routeros-suricata-tzsp-arm64.tar
|
||||
root-dir=/containers/suricata_0.9.0/root
|
||||
root-dir=/containers/suricata_0.11.2/root
|
||||
interface=veth-ids
|
||||
envlist=IDS_ENV
|
||||
mountlists=IDS_MOUNTS
|
||||
```
|
||||
|
||||
The upgrade helper does not modify the bridge, IP addresses, NAT, veth, TZSP/sniffer, firewall, REST user or envlist definitions. It stops older `suricata_*` containers, normalizes `IDS_MOUNTS` to the single `<disk>/containers/suricata-data -> /data` mapping, creates the new versioned container, waits for image extraction and starts it. Older containers are kept stopped for rollback.
|
||||
The upgrade helper does not modify the bridge, IP addresses, NAT, VETH, hybrid TZSP capture, firewall, REST user or envlist definitions. It stops older `suricata_*` containers, normalizes `IDS_MOUNTS` to the single `<disk>/containers/suricata-data -> /data` mapping, creates the new versioned container, waits for image extraction and starts it. Older containers are kept stopped for rollback.
|
||||
|
||||
All new mutable state is written below `/data`, so subsequent image upgrades need only that one persistent mount.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user