# RouterOS TZSP + Suricata IDS Project version: `0.3.2` A lightweight IDS stack designed to run as a **single container on MikroTik RouterOS**. 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. ## Architecture ```text 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 + Web UI :8080 + optional RouterOS REST blocking ``` The RouterOS deployment workflow is: ```text 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. --- ## 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: ```bash ./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: ```text 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: ```text GET /api/status ``` Example: ```bash curl http://127.0.0.1:8080/api/status ``` The response includes: - overall application status and uptime, - Web UI/API status, - TZSP receiver status, - TAP interface status, - Suricata process status and PID, - EVE JSON watcher status, - RouterOS REST integration status, - listening/outbound port information, - runtime packet, alert, and block 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: ```bash DEV_SEED_DATA=true ./dev.sh ``` Development database: ```text data/dev/ids.db ``` To use another port: ```bash WEB_PORT=8090 ./dev.sh ``` To listen on all local interfaces: ```bash 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: ```bash ./dev.sh --test ``` Or run tests before starting the dashboard: ```bash 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: ```bash cp .env.example .env ``` Build and start: ```bash docker compose up -d --build ``` Dashboard: ```text http://127.0.0.1:8080 ``` Send the built-in TZSP test packet: ```bash ./scripts/selftest.sh ``` The test rule should generate: ```text LOCAL TZSP PIPELINE TEST SID 1000001 ``` Stop the stack: ```bash 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: ```routeros /system/resource/print /system/package/print /system/device-mode/print /disk/print /container/print ``` --- ## Automated RouterOS deployment Create the deployment configuration: ```bash cp deploy-routeros.env.example deploy-routeros.env ``` Edit at least: ```dotenv ROUTER_HOST=192.168.88.1 ROUTER_USER=admin ROUTER_ARCH=auto ROUTER_DISK=disk1 ROUTER_SCP_DIR=disk1 VLAN_ID=100 MONITORED_NETWORKS=192.168.100.0/24 AUTO_BLOCK=false ``` Deploy: ```bash ./scripts/deploy-routeros.sh ``` The deployer performs the following sequence: ```text SSH architecture detection -> build image for the detected CPU -> docker save / podman save to build/*.tar -> calculate SHA256 -> generate deployment .rsc -> SCP TAR to RouterOS -> SCP .rsc to RouterOS -> create bridge/VETH/NAT/mounts/environment -> /container/add file= -> wait for extraction -> start the container -> optionally configure/start TZSP sniffer -> print container status and logs ``` For SSH key authentication set: ```dotenv ROUTER_IDENTITY_FILE=/home/user/.ssh/id_ed25519 ``` --- ## Build RouterOS image without deployment ARM64: ```bash ./scripts/build-routeros.sh arm64 ``` AMD64/x86_64: ```bash ./scripts/build-routeros.sh amd64 ``` ARMv7/armhf: ```bash ./scripts/build-routeros.sh arm ``` Generated files are written to `build/`, for example: ```text build/routeros-suricata-tzsp-arm64.tar build/routeros-suricata-tzsp-arm64.tar.sha256 ``` --- ## Manual SCP and RouterOS import Example: ```bash ./scripts/build-routeros.sh arm64 scp build/routeros-suricata-tzsp-arm64.tar admin@192.168.88.1:disk1/ ``` RouterOS templates are located in `routeros/`: ```text 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: ```routeros /container/print detail /container/start suricata-ids /log/print where message~"suricata|TZSP|IDS" ``` --- ## Persistent data on RouterOS The deployment keeps application state outside the image root directory: ```text /containers/suricata-ids-data -> /data /containers/suricata-ids-logs -> /var/log/suricata /containers/suricata-ids-rules -> /var/lib/suricata ``` This preserves SQLite data, logs, and downloaded Suricata rules when the application image is replaced. --- ## 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`: ```dotenv CONFIGURE_SNIFFER=true START_SNIFFER=true VLAN_ID=100 ``` If the router already uses Packet Sniffer for another purpose, disable automatic sniffer configuration: ```dotenv 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: ```text suritap0 ``` The pipeline is: ```text 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: ```dotenv 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: ```dotenv 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 ```text 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 app/policy.py Alert/blocking policy 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: ```dotenv AUTO_BLOCK=false UPDATE_RULES_ON_START=false ROUTEROS_PASSWORD=CHANGE_ME ``` Keep automatic firewall actions disabled until the capture path and alert quality are validated on the real network.