2026-08-15 23:43:58 +02:00
2026-08-15 23:43:58 +02:00
2026-08-13 15:58:52 +02:00
2026-08-13 15:58:52 +02:00
2026-08-13 15:58:52 +02:00
2026-08-15 23:43:58 +02:00
2026-08-15 23:43:58 +02:00
2026-08-15 23:43:58 +02:00
2026-08-15 23:43:58 +02:00
2026-08-13 15:58:52 +02:00
2026-08-13 15:58:52 +02:00
2026-08-13 15:58:52 +02:00
2026-08-15 23:43:58 +02:00
2026-08-14 11:33:01 +02:00
2026-08-13 15:58:52 +02:00
2026-08-15 18:29:36 +02:00
2026-08-15 23:43:58 +02:00
2026-08-14 11:33:01 +02:00
2026-08-14 11:33:01 +02:00
2026-08-15 18:29:36 +02:00
2026-08-15 23:43:58 +02:00
2026-08-13 15:58:52 +02:00
2026-08-15 23:43:58 +02:00

MikroSuricata

Project version: 0.9.1

What changed in 0.9.1

  • Fixed a startup regression where main.py passed backup_manager to EVEWatcher even though the watcher has no such constructor argument. Backup management remains correctly attached to the web/admin layer.
  • Added a regression test that statically validates keyword arguments used to construct EVEWatcher against its Python constructor signature.
  • Made the local cleartext-FTP SYN policy rule explicitly flow:to_server,stateless and bumped SID 1000113 to revision 2, eliminating the Suricata 8 direction warning seen during validation/startup.

What changed in 0.9.0

  • Added conservative MITRE ATT&CK annotations to NDR evidence and correlated incidents. Mapping is limited to techniques supported by network-visible evidence and is backfilled for existing NDR event history during the schema migration.
  • Expanded autonomous behavioral analytics with established-asset domain/fingerprint baselines, NXDOMAIN bursts, repeated high-entropy DNS tunnel candidates and outbound transfer-volume detection.
  • Added Adaptive Rule Intelligence based on observed alert volume, duplication/concentration and incident correlation. It never disables signatures automatically; operators can explicitly apply a standard global Suricata limit threshold to high-noise candidates.
  • Added persistent ruleset snapshots and rollback. Local rules, threshold configuration, merged vendor rules and enabled suricata-update source state are captured before rule changes/vendor updates, with bounded retention.
  • Added persistent IDS-state backups under /data/backups using SQLite online backup. Backups include the database, custom Suricata state, enabled source definitions and merged rules while excluding Redis runtime data, EVE logs and the forensic PCAP ring.
  • Added an SQLite administrative audit trail for login attempts and IDS-management operations including IOC changes, incident state changes, threshold/rule operations, backups and maintenance actions.
  • Extended the Intelligence, Rules and System views with ATT&CK evidence, noisy-rule analysis, one-click threshold proposals, ruleset rollback, backup download/delete and audit history.
  • The new 0.9.0 functionality is self-contained in the IDS container and persistent /data; it does not add any new RouterOS configuration/firewall modification path. Existing optional RouterOS integrations are unchanged.

What changed in 0.8.1

  • Fixed RouterOS/container rebuild persistence for signature-feed selections. All suricata-update source-management commands (list-sources, update-sources, enable-source, disable-source) now use the same persistent -D /data/lib/suricata directory as rule downloads, so enabled source definitions survive image replacement together with the single /data mount.
  • Added bulk signature-source selection and a sequential download queue in Signature Feeds. Operators can select visible sources or all parameter-free free sources, queue them together, and the IDS enables each source before performing one merged download, suricata -T validation and live reload.
  • Queue status and per-source progress/errors are visible in the feed table. A failed source does not prevent the remaining selected sources from being processed; the merged ruleset still keeps the previous known-good file if final download or validation fails.

A lightweight IDS stack designed to run as a single container on MikroTik RouterOS.

What changed in 0.8.0

  • Added a persistent MikroSuricata NDR correlation engine on top of Suricata: multi-stage incidents, risk score 0-100, bounded evidence timelines and incident triage.
  • Added asset intelligence persisted in SQLite and enriched from RouterOS ARP/DHCP through the RouterOS v7 REST API. Baselines track applications, outbound ports, identities, domains and fingerprints.
  • Added behavioral detections for new services on established assets, periodic beaconing, DGA/high-entropy DNS bursts, internal lateral fan-out, outbound scans of sensitive services, unusually large outbound transfers and repeated IP/MAC identity changes consistent with ARP spoofing or address conflicts.
  • Added persistent local threat intelligence for IP, domain, SHA-256, JA3, JA4 and HASSH. IP/domain/JA3/JA4/HASSH are materialized as Suricata datasets; malicious SHA-256 lists are matched natively on supported file protocols.
  • Added Suricata 8 xbits correlation for scan -> administrative access and internal probe -> SMB/RDP/SSH/WinRM/WinBox sequences.
  • Forensic PCAP now defaults to FORENSIC_PCAP_MODE=blocks: packets stay in a bounded RAM pre-event ring and a PCAP is persisted only after a successful RouterOS block. alerts, all and off modes remain selectable by environment variables.
  • Added MikroTik-specific detection for repeated RouterOS API/API-SSL access on TCP 8728/8729, in addition to WinBox/SSH/RDP and existing edge rules.
  • Added sensor-quality health monitoring for capture drops, Suricata alert-queue overflow and TZSP/TAP injection errors.
  • Managed Redis now uses AOF everysec + RDB persistence under the same /data volume.
  • Added optional asynchronous high-risk incident webhooks (NOTIFY_WEBHOOK_URL, default disabled) with risk threshold and anti-spam escalation logic.
  • Expanded the Intelligence UI with incident status actions, evidence, asset inventory, IOC management and the forensic PCAP ring.
  • RouterOS deployment now forwards the NDR, Redis AOF, inventory and optional notification settings from deploy-routeros.env into IDS_ENV, so the same controls work without manual container edits.

What changed in 0.7.2

  • Fixed the Suricata 8.0.6 EVE profile: removed unsupported llmnr and ftp-data logger entries. FTP_DATA events remain supported through the ftp EVE logger.
  • Normalized the IKE EVE entry to the Suricata 8.0.6 logger syntax.
  • Removed the visual M logo mark from both the sidebar header and authentication modal.
  • Removed the non-existent LLMNR EVE event filter from Live Sessions.

What changed in 0.7.1

  • All mutable state now uses a single persistent /data mount. SQLite, Redis, EVE logs, Suricata-update state/vendor rules, custom rules, sessions and analytics snapshots are kept below /data.
  • Docker Compose now creates only routeros-suricata-data; the migration helper can merge the old 0.7.0 log/rule volumes into /data/logs/suricata and /data/lib/suricata without overwriting initialized targets.
  • RouterOS deploy and upgrade helpers normalize IDS_MOUNTS to one mapping: <disk>/containers/suricata-data -> /data.
  • suricata-update uses its -D /data/lib/suricata data directory and runtime Suricata uses that persistent rule path directly.

What changed in 0.7.0

  • Dashboard authentication now uses a normal username/password modal. The browser keeps only an HttpOnly session cookie; sessions are stored in SQLite and survive container restarts.
  • Traffic analytics are materialized into SQLite for 15m / 1h / 6h / 24h and refreshed periodically, so charts can render immediately while Redis is starting or reconnecting.
  • Redis, SQLite, sessions and chart snapshots live under persistent /data; Docker Compose uses named volumes and RouterOS keeps the existing IDS_MOUNTS across image-only upgrades.
  • Chart rendering is visibility-aware and re-runs on tab/view changes, resize, visibility changes and layout observation.
  • Mobile mode now uses an off-canvas navigation drawer, responsive metrics/panels/forms and horizontally scrollable investigation tables.
  • The System icon no longer depends on a font glyph; it is an inline SVG.
  • The image now installs Suricata 8 from Debian trixie-backports and loads a project-owned EVE profile so package updates do not silently remove required telemetry.
  • EVE telemetry explicitly enables DNS v3, community_id, Ethernet metadata, SHA-256 file hashes, JA3/JA4, SSH HASSH, ARP/DHCP, plus Windows/AD and application protocols such as SMB, RDP, Kerberos, DCERPC, LDAP, QUIC and HTTP/2/DoH2.
  • 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.

NDR / MikroTik-specific options

The defaults are conservative: NDR analysis is enabled, but automatic NDR blocking and outbound webhooks are disabled until explicitly configured. Useful environment variables:

NDR_ENABLED=true
NDR_CORRELATION_WINDOW_SECONDS=1800
BEHAVIOR_MIN_OBSERVATIONS=50
NDR_AUTO_BLOCK=false
NDR_AUTO_BLOCK_RISK=92
ROUTEROS_INVENTORY_INTERVAL_SECONDS=300
NOTIFY_WEBHOOK_URL=
NOTIFY_MIN_RISK=80
REDIS_AOF=true

All NDR state, IOC data, Redis persistence, Suricata logs/rules and forensic PCAP rotation remain below the single persistent /data mount.

Architecture

VLAN / RouterOS traffic
        |
        v
RouterOS Packet Sniffer
        |
        | TZSP UDP/37008
        v
single RouterOS container
  Debian slim
  + Python TZSP receiver
  + TAP suritap0
  + Suricata IDS
  + 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
  + Web UI :8080
  + optional RouterOS REST blocking

The RouterOS deployment workflow is:

Docker/Podman build host
        |
        | build for RouterOS CPU architecture
        v
container image
        |
        | docker save / podman save
        v
*.tar
        |
        | SCP
        v
RouterOS external disk
        |
        | /container/add file=...
        v
running IDS container

Docker Compose is only provided for Linux integration testing. RouterOS receives one saved container image as a TAR archive.


Quick start - Docker Compose on Linux

Use these steps for the local full-stack test with TZSP, TAP and Suricata.

1. Check prerequisites

You need:

  • Linux,
  • Docker Engine,
  • Docker Compose v2 (docker compose),
  • /dev/net/tun available on the host.

Check them:

docker --version
docker compose version
test -c /dev/net/tun && echo "TUN/TAP: OK" || echo "TUN/TAP: MISSING"

If /dev/net/tun is missing on Linux, try:

sudo modprobe tun

Then check /dev/net/tun again.

2. Create the runtime configuration

From the project directory:

cp .env.example .env

For a first test the defaults can be used. Before monitoring a real network, review at least:

SURICATA_HOME_NET=[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]
MONITORED_NETWORKS=192.168.0.0/16,10.0.0.0/8,172.16.0.0/12
ALERT_MAX_SEVERITY=2
ALERT_DEDUP_WINDOW_SECONDS=300
ADMIN_USERNAME=admin
ADMIN_PASSWORD=<long-unique-password>
SESSION_HOURS=168
ANALYTICS_SNAPSHOT_INTERVAL_SECONDS=60
AUTO_BLOCK=false

Keep AUTO_BLOCK=false until alerts are verified.

3. Build and start

docker compose up -d --build

Compose uses one named volume, routeros-suricata-data, mounted at /data. SQLite, Redis, chart snapshots, sessions, Suricata logs, local configuration and downloaded vendor rules all live below that mount. RouterOS uses the same single-mount layout: disk1/containers/suricata-data -> /data through IDS_MOUNTS. Rebuilding or replacing the container therefore leaves all mutable IDS data outside the image root.

When upgrading a Docker installation from 0.6.1 or older, migrate the old bind-mounted ./data, ./logs and ./data/vendor-rules before the first 0.8.1 start:

docker compose build
./scripts/migrate-docker-volumes.sh
docker compose up -d

The migration keeps the existing routeros-suricata-data volume and folds legacy log/rule volumes into /data/logs/suricata and /data/lib/suricata only when those target directories are empty. ./scripts/first-run.sh performs this step automatically. If you start Compose manually, set a strong ADMIN_PASSWORD in .env; first-run.sh generates one automatically for a new .env.

Check container status:

docker compose ps

Follow logs:

docker compose logs -f ids

You can also use the container name directly:

docker logs -f routeros-suricata-tzsp

Do not run plain docker logs -f - Docker requires a container name.

4. Run the end-to-end self-test

./scripts/selftest.sh

Expected result:

SELFTEST OK: Suricata emitted marked TZSP pipeline test alert(s); UI filtering remains enabled

The self-test uses reserved SID 1000001 and a unique payload marker. Normal ICMP/ping traffic cannot match it, and SID 1000001 is filtered from SQLite/UI by default.

5. Open the dashboard

http://127.0.0.1:8080

Status API:

curl http://127.0.0.1:8080/api/status

6. Stop the stack

docker compose down

One-command first start

The helper script checks Docker, Compose and /dev/net/tun, creates .env if needed, starts the stack and runs the self-test:

./scripts/first-run.sh

If scripts are not executable after unpacking an archive:

chmod +x dev.sh scripts/*.sh
./scripts/first-run.sh

Rebuild after updating older images

Version 0.3.2 could restart continuously with:

chown: invalid user: 'suricata:suricata'

Version 0.3.3 added the suricata system account. Version 0.3.4 also fixes a second startup issue where the suricata -T configuration check could create root-owned eve.json, fast.log, and stats.log, causing the real Suricata process to fail with Permission denied. It also relocates the Unix command socket into /run/suricata/. Rebuild the image completely:

docker compose down
docker compose build --no-cache
docker compose up -d
docker compose ps
docker compose logs -f ids

Alert tuning and false-positive control

Version 0.5.2 uses two tuning layers. Raw Suricata EVE stays on disk, while the incident database/UI defaults to severity 1-2 and collapses repeated identical SID/source/destination tuples for five minutes. This prevents informational events from flooding the dashboard without changing the raw sensor log.

ALERT_MAX_SEVERITY=2
ALERT_DEDUP_WINDOW_SECONDS=300
ALERT_IGNORE_SIDS=
ALERT_IGNORE_CATEGORIES=

For a known false positive, prefer sensor-level tuning in /data/suricata/threshold.config. The dashboard can add a full SID suppression with Suppress SID, or the file can be edited directly from the rule-management panel. Examples:

suppress gen_id 1, sig_id 1234567
threshold gen_id 1, sig_id 1234567, type limit, track by_src, count 1, seconds 300

Do not globally suppress a rule just because it fired once. First verify the SID, endpoint, direction and expected application behavior.


Adding new Suricata detections

Suricata is signature/rule driven; it does not need model training to learn a new network detection. Add environment-specific signatures to /data/suricata/custom.rules, or use Custom Suricata signatures in the dashboard. Save performs a suricata -T validation first and only then writes the file and requests a live rule reload. Runtime loads persisted /data/suricata/*.rules, so additional rule files can be placed beside custom.rules without rebuilding the image.

Example local rule:

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"LOCAL suspicious URI marker"; flow:established,to_server; http.uri; content:"/admin/export"; nocase; classtype:web-application-activity; priority:2; sid:1000100; rev:1;)

Use unique local SIDs. SID 1000001 is reserved for the marked pipeline self-test, built-in production detections use 1000101-1000115, and site-specific rules should use 1001000+.

Vendor rules are managed with suricata-update. A baseline ET/Open ruleset and a current OISF source index are baked into the image. Runtime rule state is written with suricata-update -D /data/lib/suricata, so downloaded feeds, source definitions and caches are inside the single persistent /data mount. An empty first-run data directory is seeded from the image baseline. scripts/update-rules.sh applies persisted /data/suricata/disable.conf, enable.conf, and modify.conf.

The dedicated Signature Feeds page has a provider table backed by the official OISF suricata-update catalog. It also shows the current active merged-rule count and the configured automatic-update interval. It can also add/remove a signature source directly by HTTP(S) URL when that feed is not present in the public catalog. The UI lists sources, shows vendor/license/tags/status, refreshes the OISF index, enables or disables parameter-free feeds, and downloads all active feeds on demand. Multiple parameter-free sources can be selected and queued together; they are enabled sequentially and then rebuilt/validated once. All source-management commands and rule downloads use -D /data/lib/suricata, so source definitions survive RouterOS container rebuilds with the same /data mount. ET/Open remains the default source and cannot be accidentally disabled from the panel. The Rules page also exposes a read-only, searchable, paginated view of the merged /data/lib/suricata/rules/suricata.rules file. The merged-rule browser is collapsed by default to keep the page compact while leaving the active-rule counter visible in its header.

Every feed update is transactional at the merged-rules level: the existing suricata.rules is backed up, new signatures are downloaded, the complete Suricata configuration is tested with suricata -T, and only a validated ruleset is kept. If download or validation fails, the previous known-good rules are restored. The periodic updater uses the same active-source set and runs every RULE_UPDATE_INTERVAL_HOURS when the interval is greater than zero. The default is 24; set RULE_UPDATE_INTERVAL_HOURS=0 to disable automatic updates.

UPDATE_RULES_ON_START=false
RULE_UPDATE_INTERVAL_HOURS=24
FORENSIC_PCAP_MODE=blocks
FORENSIC_PCAP_WINDOW_SECONDS=60
FORENSIC_PCAP_MEMORY_MB=64
FORENSIC_PCAP_MAX_FILES=32
FORENSIC_PCAP_MAX_TOTAL_MB=512

Built-in production detections

The image ships with a conservative local baseline in addition to the ET/Open snapshot baked by suricata-update. Local rules are rate-limited so ordinary single packets do not become incidents. The baseline covers repeated SSH/RDP/WinBox attempts, SYN scans, ICMP sweeps, inbound and outbound SMB policy violations, unusually long or high-rate DNS activity, NXDOMAIN bursts, outbound Telnet/FTP/direct SMTP, external database-service probes and an internal RDP/SMB lateral-movement burst signal.

The production baseline uses SIDs 1000101-1000116, with NDR state rules 1000120-1000123 and managed threat-intelligence rules starting at 1000201. The deterministic pipeline self-test remains SID 1000001, but it only matches the exact payload marker generated by scripts/send_test_tzsp.py and is ignored by the incident database. Environment-specific exceptions should be handled with threshold.config or suppression rather than weakening the entire sensor.

ET/Open is still the main vendor signature source. suricata-update is the supported manager for refreshing it; the image seeds /data/lib/suricata inside the persistent data volume on first start.


Upload a ready RouterOS image without deploying it

The upload helper requires an already-built TAR and does exactly one job:

cp deploy-routeros.env.example deploy-routeros.env
./scripts/upload-routeros-image.sh build/routeros-suricata-tzsp-arm64.tar

It performs SCP upload plus a read-only file-list verification. It does not detect architecture, build an image, run /container/add, import an .rsc, change RouterOS configuration, or start a container.


Database, storage and maintenance

The dashboard detects SQLite and persistent storage separately. It shows DB path, schema version, row count, DB/WAL size, filesystem usage and Suricata log size. SQLite uses WAL mode and performs a small schema migration automatically when upgrading from older project versions.

Administrative actions require a dashboard session. Configure ADMIN_USERNAME and a long unique ADMIN_PASSWORD; the server issues an HttpOnly, SameSite=Strict cookie and stores only a hash of the random session token in SQLite. Set SESSION_COOKIE_SECURE=true when the dashboard itself is served over HTTPS. ADMIN_TOKEN is accepted only as a migration fallback and is no longer stored by the browser.

Authenticated maintenance includes clearing incident/history data, SQLite VACUUM, runtime counter reset, RouterOS block-list actions, validated custom-rule/threshold edits, live Suricata rule reloads and managed signature-feed updates. Keep port 8080 on a trusted management network or place the dashboard behind HTTPS.

SQLite also stores the four rolling chart summaries (900, 3600, 21600, 86400 seconds). They refresh every ANALYTICS_SNAPSHOT_INTERVAL_SECONDS and are served immediately after UI entry/restart when newer live history is temporarily unavailable.


Extended statistics

The dashboard reports alert hits vs deduplicated incidents, selected-window activity, top signatures, severity distribution, protocol anomalies, NXDOMAINs, encrypted vs cleartext sessions, local clients vs remote peers, JA4/JA3/HASSH fingerprints, passive IP/MAC assets, filter/dedup/error counters, block attempts/results and Suricata EVE stats counters such as decoder/capture/drop values when emitted by the installed configuration. Live-history records remain searchable by IP/port, signature, flow_id, community_id and transaction ID.


Dashboard sections

The web UI sections are Overview, Live Sessions, Security, Intelligence, Blocks, Reports, Signature Feeds, Rules and System. Security is split into Incidents / Analytics / Telemetry subtabs, while Intelligence is split into Incidents / Assets / Threat intel / Forensics so large inventories stay one click away instead of far down the page. System includes a dedicated Redis status card with connection/runtime, persistence, retained event and writer-health information. Incident timestamps are stored in UTC and rendered in the browser's local timezone. Repeated alerts are aggregated by SID, source, destination, protocol and destination port within the configured deduplication window.


Local Web UI development without Docker

The web dashboard can be started locally without Docker, Suricata, TAP, /dev/net/tun, or root privileges.

Requirements:

  • Linux or macOS
  • Python 3
  • Python venv support

Start the development dashboard:

./dev.sh

The script will:

  1. create .venv/ if needed,
  2. install dependencies from requirements.txt,
  3. create data/dev/,
  4. start the web-only application.

Open:

http://127.0.0.1:8080

This mode intentionally does not start Suricata, TZSP capture, TAP, or RouterOS integration.

Status API

The dashboard and external monitoring systems can use:

GET /api/status

Example:

curl http://127.0.0.1:8080/api/status

The response includes:

  • overall application status and uptime,
  • Web UI/API, TZSP, TAP, Suricata and EVE watcher state,
  • SQLite existence/path/size/WAL/schema/row count,
  • persistent filesystem usage and Suricata log size,
  • managed custom-rule and threshold/suppression status,
  • RouterOS REST integration status and ports,
  • runtime packet/error/filter/dedup/block counters,
  • the latest numeric Suricata EVE stats counters.

GET /api/health is kept as a compatibility alias and returns the same status payload.

Add a sample alert

To start the dashboard with one demo alert in an empty development database:

DEV_SEED_DATA=true ./dev.sh

Development database:

data/dev/ids.db

To use another port:

WEB_PORT=8090 ./dev.sh

To listen on all local interfaces:

WEB_BIND=0.0.0.0 ./dev.sh

Do not expose the development server directly to an untrusted network.


Local tests

Install dependencies and run tests:

./dev.sh --test

Or run tests before starting the dashboard:

RUN_TESTS=true ./dev.sh

The project currently uses only the Python standard library, including unittest for tests. requirements.txt is kept as the canonical dependency file so future packages can be installed automatically by dev.sh.


Docker integration test

Prepare local environment configuration:

cp .env.example .env

Build and start:

docker compose up -d --build

Dashboard:

http://127.0.0.1:8080

Send the built-in TZSP test packet:

./scripts/selftest.sh

The test rule should generate raw EVE alert SID 1000001 with signature LOCAL TEST TZSP PIPELINE MARKER. It is intentionally filtered from the incident database/UI.

Stop the stack:

docker compose down

RouterOS requirements

Before deployment, verify that the router has:

  • a RouterOS version and package set that supports containers,
  • the container package installed,
  • container and sniffer functionality enabled in device mode,
  • SSH access from the build machine,
  • enough storage for the image, container root directory, Suricata rules, logs, and SQLite database,
  • preferably an external disk instead of small internal flash storage.

Useful RouterOS checks:

/system/resource/print
/system/package/print
/system/device-mode/print
/disk/print
/container/print

Automated RouterOS deployment

Create the deployment configuration:

cp deploy-routeros.env.example deploy-routeros.env

Upload a ready image first:

./scripts/upload-routeros-image.sh build/routeros-suricata-tzsp-arm64.tar

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:

name=suricata_0.9.0
file=routeros-suricata-tzsp-arm64.tar
root-dir=/containers/suricata_0.9.0/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.

For SSH key authentication set:

ROUTER_IDENTITY_FILE=/home/user/.ssh/id_ed25519

Build RouterOS image without deployment

ARM64:

./scripts/build-routeros.sh arm64

AMD64/x86_64:

./scripts/build-routeros.sh amd64

ARMv7/armhf:

./scripts/build-routeros.sh arm

Generated files are written to build/, for example:

build/routeros-suricata-tzsp-arm64.tar
build/routeros-suricata-tzsp-arm64.tar.sha256

Manual SCP and RouterOS import

Example:

./scripts/build-routeros.sh arm64
scp build/routeros-suricata-tzsp-arm64.tar admin@192.168.88.1:/

RouterOS templates are located in routeros/:

01-container-network.rsc
02-sniffer-vlan100.rsc
03-rest-and-firewall.rsc
04-container-import-amd64.rsc
04-container-import-arm64.rsc
04-container-import-arm.rsc
rollback.rsc

After extraction, inspect and start the container:

/container/print detail
/container/start suricata-ids
/log/print where message~"suricata|TZSP|IDS"

Persistent data on RouterOS

The deployment uses one persistent directory outside the image root:

<disk>/containers/suricata-data -> /data

Inside it the application keeps:

/data/ids.db                 SQLite, sessions and analytics snapshots
/data/redis/                 Redis persistence
/data/logs/suricata/         EVE/raw Suricata logs
/data/lib/suricata/          suricata-update feeds, cache and vendor rules
/data/suricata/              custom rules, thresholds and update filters

Replacing or restarting the application container does not remove any of these files.


TZSP capture

The default deployment configuration can configure RouterOS Packet Sniffer to stream VLAN traffic to the container address on UDP port 37008.

Relevant settings in deploy-routeros.env:

CONFIGURE_SNIFFER=true
START_SNIFFER=true
VLAN_ID=100

If the router already uses Packet Sniffer for another purpose, disable automatic sniffer configuration:

CONFIGURE_SNIFFER=false
START_SNIFFER=false

Then configure the capture manually.

Hardware-offloaded bridge traffic may require additional verification on the specific RouterOS device because some switched traffic can bypass software capture paths.


TAP and Suricata

Inside the container the application creates:

suritap0

The pipeline is:

TZSP datagram
  -> Python decoder
  -> Ethernet frame
  -> TAP suritap0
  -> Suricata
  -> eve.json
  -> Python EVE watcher
  -> SQLite / Web UI

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.


RouterOS REST reaction

Automatic blocking is intentionally disabled by default:

AUTO_BLOCK=false

Observation mode should be used first. After validating alerts and false positives, the optional reaction engine can add selected addresses to a RouterOS firewall address list through REST.

Relevant settings:

AUTO_BLOCK=true
AUTO_BLOCK_MAX_SEVERITY=1
ROUTEROS_URL=https://172.31.255.1
ROUTEROS_USER=suricata-api
ROUTEROS_PASSWORD=CHANGE_ME
ROUTEROS_ADDRESS_LIST=IDS-BLOCK
BLOCK_TIMEOUT=1h

Do not enable automatic blocking until the monitored networks, exclusion list, REST credentials, firewall rule placement, and alert policy have been reviewed.


Important files

Dockerfile
Dockerfile / Debian slim runtime image

dev.sh
Local web-only development launcher

requirements.txt
Local Python development/test dependencies

app/main.py
Full RouterOS/container application entry point

app/dev_web.py
Local web-only entry point

app/tzsp.py
TZSP receiver and decoder

app/tap.py
TAP interface handling

app/eve.py
Suricata EVE JSON watcher

suricata/ids-output.yaml
Project-owned Suricata 8 EVE telemetry profile

app/policy.py
Alert/blocking policy

app/tuning.py
Second-stage alert severity/category/SID filter

app/rules.py
Validated custom rules, suppressions and vendor-rule updates

app/maintenance.py
Storage detection and safe maintenance helpers

app/routeros.py
RouterOS REST client

app/store.py
SQLite alert storage

app/webui.py
Dashboard and JSON API

scripts/build-routeros.sh
Build and save image to TAR

scripts/deploy-routeros.sh
Build, SCP, import, and start on RouterOS

routeros/
RouterOS configuration templates

Safety defaults

The default configuration is observation-oriented:

AUTO_BLOCK=false
ALERT_MAX_SEVERITY=2
UPDATE_RULES_ON_START=false
FORENSIC_PCAP_MODE=blocks
ROUTEROS_PASSWORD=CHANGE_ME
ADMIN_USERNAME=admin
ADMIN_PASSWORD=
SESSION_COOKIE_SECURE=false

Keep automatic firewall actions disabled until the capture path and alert quality are validated on the real network. Set a strong ADMIN_PASSWORD before enabling dashboard maintenance/rule-management actions, and restrict the Web UI to a trusted management network.


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.

  1. Upload the ready TAR only:
./scripts/upload-routeros-image.sh build/routeros-suricata-tzsp-arm64.tar
  1. Swap the Suricata container only:
./scripts/upgrade-routeros-container.sh routeros-suricata-tzsp-arm64.tar

For version 0.9.0 the second command creates:

name=suricata_0.9.0
file=routeros-suricata-tzsp-arm64.tar
root-dir=/containers/suricata_0.9.0/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.

All new mutable state is written below /data, so subsequent image upgrades need only that one persistent mount.

0.9.3 HTTP disconnect hardening

  • Treat browser/client disconnects during JSON responses and file downloads as normal connection termination.
  • Suppress BrokenPipeError, ConnectionResetError, and equivalent socket disconnect tracebacks from the threaded HTTP server.
  • No change to Redis-only traffic history semantics from 0.9.2.

0.9.4 Throughput visibility

  • Traffic throughput now always renders the raw TZSP Total series, even when inbound/outbound classification is unavailable.
  • The dashboard exposes unclassified throughput as OTHER instead of silently drawing an empty IN/OUT chart.
  • Default MONITORED_NETWORKS now covers RFC1918 private LAN ranges (192.168/16, 10/8, 172.16/12) so common RouterOS LANs classify correctly without editing the image.
  • Throughput remains persisted in Redis; no RAM history fallback is reintroduced.

0.9.5 Traffic accuracy and CPU reduction

  • Observed traffic is calculated from raw TZSP packet-byte samples for the selected time range. EVE transaction records are no longer summed as traffic volume, avoiding repeated cumulative flow counters (for example impossible hundreds of GB in a 15-minute view).
  • Traffic throughput has a lightweight /api/traffic/throughput path, so the speed graph can render without waiting for full EVE analytics.
  • failed, unknown, none and similar Suricata application classifications are excluded from Top applications. Application counts are deduplicated by flow.
  • SURICATA IPv4 truncated packet / IPv6 equivalents are hidden from dashboard history and analytics. Alerts rejected by the configured alert tuner are no longer persisted into dashboard Redis history; raw EVE remains on disk.
  • Analytics snapshots are demand-driven: only time windows used by a browser/API are refreshed. The old unconditional full 24h Redis scan every minute was removed. Default refresh cadence is 60s (15m), 120s (1h), 300s (6h), 900s (24h), with current throughput read separately in constant time.
  • The TZSP session tracker keeps packet throughput active but skips per-flow OrderedDict/hash work while Live Sessions streaming is off.
  • LAN membership lookups are cached, reducing repeated ipaddress parsing for packet direction classification.
  • Redis event persistence batches up to 128 EVE records into one multi-member ZADD, reducing Python socket and Redis command overhead while retaining every accepted dashboard event.
  • Duplicate fast.log and standalone stats.log outputs are disabled because the application consumes EVE alerts/stats already.
  • Analytics cache namespace is bumped to v3 so incorrect pre-0.9.5 traffic-volume snapshots are not reused after upgrade.
S
Description
No description provided
Readme
1 MiB
Languages
Python 71.3%
JavaScript 10.5%
Shell 6.2%
Rust 4.8%
HTML 4.3%
Other 2.9%