Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6d656ce14 | ||
|
|
17dc79372e | ||
|
|
4118d60d6d | ||
|
|
6ce60ed5d3 | ||
|
|
fe113d5518 | ||
|
|
9104c67f97 | ||
|
|
d01eca2cf0 | ||
|
|
b7abd340bc | ||
|
|
0bd84ed2ed | ||
|
|
74eb423a92 | ||
|
|
3c8c03bb98 | ||
|
|
2f76875f2b | ||
|
|
f9e0e1a98e | ||
|
|
544b7cfa49 | ||
|
|
1fc63c657a | ||
|
|
b45b810eb9 | ||
|
|
64c491f245 | ||
|
|
88b0dd7472 | ||
|
|
b18e0a51e1 | ||
|
|
a7967260b1 | ||
|
|
59416f0fe3 | ||
|
|
85db2b1483 | ||
|
|
cc7a0f6f0f | ||
|
|
44be5e232e | ||
|
|
34d830b275 | ||
|
|
3e0bd64b14 | ||
|
|
6ebab9e23e | ||
|
|
7498527667 | ||
|
|
d901f264cd | ||
|
|
14645c520b | ||
|
|
c62b41d624 | ||
|
|
c8efe5c942 | ||
|
|
40f8f05e87 | ||
|
|
3faddb3f5f | ||
|
|
20bdf72f81 |
@ -0,0 +1,136 @@
|
|||||||
|
./deployment/update_from_git.sh --db
|
||||||
|
|
||||||
|
╔═══════════════════════════════════════════════╗
|
||||||
|
║ AGGIORNAMENTO SISTEMA IDS DA GIT ║
|
||||||
|
╚═══════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
Verifica configurazione git...
|
||||||
|
|
||||||
|
Backup configurazione locale...
|
||||||
|
✅ .env salvato in .env.backup
|
||||||
|
|
||||||
|
Verifica modifiche locali...
|
||||||
|
⚠ Ci sono modifiche locali non committate
|
||||||
|
Esegui 'git status' per vedere i dettagli
|
||||||
|
Vuoi procedere comunque? (y/n) y
|
||||||
|
Salvo modifiche locali temporaneamente...
|
||||||
|
No local changes to save
|
||||||
|
|
||||||
|
Download aggiornamenti da git.alfacom.it...
|
||||||
|
remote: Enumerating objects: 27, done.
|
||||||
|
remote: Counting objects: 100% (27/27), done.
|
||||||
|
remote: Compressing objects: 100% (16/16), done.
|
||||||
|
remote: Total 16 (delta 13), reused 0 (delta 0), pack-reused 0 (from 0)
|
||||||
|
Unpacking objects: 100% (16/16), 4.03 KiB | 295.00 KiB/s, done.
|
||||||
|
From https://git.alfacom.it/marco/ids.alfacom.it
|
||||||
|
40f8f05..14645c5 main -> origin/main
|
||||||
|
* [new tag] v1.0.113 -> v1.0.113
|
||||||
|
From https://git.alfacom.it/marco/ids.alfacom.it
|
||||||
|
* branch main -> FETCH_HEAD
|
||||||
|
Updating 40f8f05..14645c5
|
||||||
|
Fast-forward
|
||||||
|
client/src/pages/Training.tsx | 65 +++++++++++++++++++++++++++++++++++++++++++++++++----------------
|
||||||
|
database-schema/schema.sql | 4 ++--
|
||||||
|
replit.md | 2 +-
|
||||||
|
server/routes.ts | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------
|
||||||
|
version.json | 16 ++++++++--------
|
||||||
|
5 files changed, 149 insertions(+), 76 deletions(-)
|
||||||
|
✅ Aggiornamenti scaricati con successo
|
||||||
|
|
||||||
|
Ripristino configurazione locale...
|
||||||
|
✅ .env ripristinato
|
||||||
|
|
||||||
|
Aggiornamento dipendenze Node.js...
|
||||||
|
|
||||||
|
up to date, audited 492 packages in 5s
|
||||||
|
|
||||||
|
65 packages are looking for funding
|
||||||
|
run `npm fund` for details
|
||||||
|
|
||||||
|
13 vulnerabilities (3 low, 6 moderate, 4 high)
|
||||||
|
|
||||||
|
To address issues that do not require attention, run:
|
||||||
|
npm audit fix
|
||||||
|
|
||||||
|
To address all issues (including breaking changes), run:
|
||||||
|
npm audit fix --force
|
||||||
|
|
||||||
|
Run `npm audit` for details.
|
||||||
|
✅ Dipendenze Node.js aggiornate
|
||||||
|
|
||||||
|
Aggiornamento dipendenze Python...
|
||||||
|
Defaulting to user installation because normal site-packages is not writeable
|
||||||
|
Requirement already satisfied: fastapi==0.104.1 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 1)) (0.104.1)
|
||||||
|
Requirement already satisfied: uvicorn==0.24.0 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 2)) (0.24.0)
|
||||||
|
Requirement already satisfied: pandas==2.1.3 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 3)) (2.1.3)
|
||||||
|
Requirement already satisfied: numpy==1.26.2 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 4)) (1.26.2)
|
||||||
|
Requirement already satisfied: scikit-learn==1.3.2 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 5)) (1.3.2)
|
||||||
|
Requirement already satisfied: psycopg2-binary==2.9.9 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 6)) (2.9.9)
|
||||||
|
Requirement already satisfied: python-dotenv==1.0.0 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 7)) (1.0.0)
|
||||||
|
Requirement already satisfied: pydantic==2.5.0 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 8)) (2.5.0)
|
||||||
|
Requirement already satisfied: httpx==0.25.1 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 9)) (0.25.1)
|
||||||
|
Requirement already satisfied: xgboost==2.0.3 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 10)) (2.0.3)
|
||||||
|
Requirement already satisfied: joblib==1.3.2 in /home/ids/.local/lib/python3.11/site-packages (from -r requirements.txt (line 11)) (1.3.2)
|
||||||
|
Requirement already satisfied: anyio<4.0.0,>=3.7.1 in /home/ids/.local/lib/python3.11/site-packages (from fastapi==0.104.1->-r requirements.txt (line 1)) (3.7.1)
|
||||||
|
Requirement already satisfied: starlette<0.28.0,>=0.27.0 in /home/ids/.local/lib/python3.11/site-packages (from fastapi==0.104.1->-r requirements.txt (line 1)) (0.27.0)
|
||||||
|
Requirement already satisfied: typing-extensions>=4.8.0 in /home/ids/.local/lib/python3.11/site-packages (from fastapi==0.104.1->-r requirements.txt (line 1)) (4.15.0)
|
||||||
|
Requirement already satisfied: click>=7.0 in /home/ids/.local/lib/python3.11/site-packages (from uvicorn==0.24.0->-r requirements.txt (line 2)) (8.3.1)
|
||||||
|
Requirement already satisfied: h11>=0.8 in /home/ids/.local/lib/python3.11/site-packages (from uvicorn==0.24.0->-r requirements.txt (line 2)) (0.16.0)
|
||||||
|
Requirement already satisfied: python-dateutil>=2.8.2 in /home/ids/.local/lib/python3.11/site-packages (from pandas==2.1.3->-r requirements.txt (line 3)) (2.9.0.post0)
|
||||||
|
Requirement already satisfied: pytz>=2020.1 in /home/ids/.local/lib/python3.11/site-packages (from pandas==2.1.3->-r requirements.txt (line 3)) (2025.2)
|
||||||
|
Requirement already satisfied: tzdata>=2022.1 in /home/ids/.local/lib/python3.11/site-packages (from pandas==2.1.3->-r requirements.txt (line 3)) (2025.2)
|
||||||
|
Requirement already satisfied: scipy>=1.5.0 in /home/ids/.local/lib/python3.11/site-packages (from scikit-learn==1.3.2->-r requirements.txt (line 5)) (1.16.3)
|
||||||
|
Requirement already satisfied: threadpoolctl>=2.0.0 in /home/ids/.local/lib/python3.11/site-packages (from scikit-learn==1.3.2->-r requirements.txt (line 5)) (3.6.0)
|
||||||
|
Requirement already satisfied: annotated-types>=0.4.0 in /home/ids/.local/lib/python3.11/site-packages (from pydantic==2.5.0->-r requirements.txt (line 8)) (0.7.0)
|
||||||
|
Requirement already satisfied: pydantic-core==2.14.1 in /home/ids/.local/lib/python3.11/site-packages (from pydantic==2.5.0->-r requirements.txt (line 8)) (2.14.1)
|
||||||
|
Requirement already satisfied: certifi in /home/ids/.local/lib/python3.11/site-packages (from httpx==0.25.1->-r requirements.txt (line 9)) (2025.11.12)
|
||||||
|
Requirement already satisfied: httpcore in /home/ids/.local/lib/python3.11/site-packages (from httpx==0.25.1->-r requirements.txt (line 9)) (1.0.9)
|
||||||
|
Requirement already satisfied: idna in /home/ids/.local/lib/python3.11/site-packages (from httpx==0.25.1->-r requirements.txt (line 9)) (3.11)
|
||||||
|
Requirement already satisfied: sniffio in /home/ids/.local/lib/python3.11/site-packages (from httpx==0.25.1->-r requirements.txt (line 9)) (1.3.1)
|
||||||
|
Requirement already satisfied: six>=1.5 in /home/ids/.local/lib/python3.11/site-packages (from python-dateutil>=2.8.2->pandas==2.1.3->-r requirements.txt (line 3)) (1.17.0)
|
||||||
|
✅ Dipendenze Python aggiornate
|
||||||
|
|
||||||
|
🗄 Aggiornamento schema database...
|
||||||
|
Applicando migrazioni SQL...
|
||||||
|
🗄 Sistema Migrazioni Database (Versioned)
|
||||||
|
📋 Verifica sistema versioning...
|
||||||
|
psql:/opt/ids/database-schema/migrations/000_init_schema_version.sql:14: NOTICE: relation "schema_version" already exists, skipping
|
||||||
|
✅ Sistema versioning attivo
|
||||||
|
📊 Versione database corrente: 9
|
||||||
|
✅ Database già aggiornato (nessuna migrazione da applicare)
|
||||||
|
✅ Migrazioni SQL applicate
|
||||||
|
Sincronizzando schema Drizzle...
|
||||||
|
|
||||||
|
> rest-express@1.0.0 db:push
|
||||||
|
> drizzle-kit push
|
||||||
|
|
||||||
|
No config path provided, using default 'drizzle.config.ts'
|
||||||
|
Reading config file '/opt/ids/drizzle.config.ts'
|
||||||
|
Using 'pg' driver for database querying
|
||||||
|
[✓] Pulling schema from database...
|
||||||
|
[✓] Changes applied
|
||||||
|
✅ Schema database completamente sincronizzato
|
||||||
|
|
||||||
|
📡 Configurazione RSyslog (log MikroTik)...
|
||||||
|
✅ RSyslog già configurato
|
||||||
|
|
||||||
|
📋 Verifica servizio list-fetcher...
|
||||||
|
✅ Servizio ids-list-fetcher già installato
|
||||||
|
|
||||||
|
🔄 Restart servizi...
|
||||||
|
✅ Servizi riavviati
|
||||||
|
|
||||||
|
╔═══════════════════════════════════════════════╗
|
||||||
|
║ ✅ AGGIORNAMENTO COMPLETATO ║
|
||||||
|
╚═══════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
📋 VERIFICA SISTEMA:
|
||||||
|
• Log backend: tail -f /var/log/ids/backend.log
|
||||||
|
• Log frontend: tail -f /var/log/ids/frontend.log
|
||||||
|
• API backend: curl http://localhost:8000/health
|
||||||
|
• Frontend: curl http://localhost:5000
|
||||||
|
|
||||||
|
📊 STATO SERVIZI:
|
||||||
|
ids 1034 1.1 2.3 2939568 381944 ? Ssl 12:18 0:09 /opt/ids/python_ml/venv/bin/python3 main.py
|
||||||
|
ids 1069 18.3 0.1 52452 26240 ? Ss 12:18 2:40 /opt/ids/python_ml/venv/bin/python3 syslog_parser.py
|
||||||
|
root 2447 0.0 0.2 731344 32324 pts/0 Rl+ 12:33 0:00 /usr/bin/node /usr/bin/npm run dev
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
echo "=== TEST PORTA 5000 ===" && curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:5000/api/health && echo "=== TEST AUTO-BLOCK MANUALE ===" && sudo -u ids /opt/ids/python_ml/venv/bin/python3 /opt/ids/python_ml/auto_block.py 2>&1 && echo "=== STATO TUTTI I SERVIZI ===" && systemctl status ids-backend ids-ml-backend ids-syslog-parser ids-auto-block.timer --no-pager -l
|
||||||
|
=== TEST PORTA 5000 ===
|
||||||
|
HTTP 200
|
||||||
|
=== TEST AUTO-BLOCK MANUALE ===
|
||||||
|
[2026-02-17 08:51:22] Starting auto-block cycle...
|
||||||
|
[2026-02-17 08:51:22] Step 1: Detection ML...
|
||||||
|
[2026-02-17 08:51:22] Detection completata: 0 anomalie rilevate
|
||||||
|
[2026-02-17 08:51:22] Step 2: Blocco IP critici sui router...
|
||||||
|
[2026-02-17 08:51:22] 24 IP bloccati sui router, 0 falliti, 0 gia' bloccati
|
||||||
|
=== STATO TUTTI I SERVIZI ===
|
||||||
|
● ids-backend.service - IDS Node.js Backend (Express API + Frontend)
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-backend.service; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Tue 2026-02-17 08:51:09 CET; 57s ago
|
||||||
|
Process: 31307 ExecStartPre=/bin/bash -c test -f /opt/ids/dist/index.js || (echo "ERRORE: dist/index.js non trovato - eseguire npm run build" && exit 1) (code=exited, status=0/SUCCESS)
|
||||||
|
Main PID: 31308 (node)
|
||||||
|
Tasks: 11 (limit: 100409)
|
||||||
|
Memory: 59.1M (max: 1.0G available: 964.8M)
|
||||||
|
CPU: 1.669s
|
||||||
|
CGroup: /system.slice/ids-backend.service
|
||||||
|
└─31308 node dist/index.js
|
||||||
|
|
||||||
|
Feb 17 08:51:09 ids.alfacom.it systemd[1]: Starting IDS Node.js Backend (Express API + Frontend)...
|
||||||
|
Feb 17 08:51:09 ids.alfacom.it systemd[1]: Started IDS Node.js Backend (Express API + Frontend).
|
||||||
|
|
||||||
|
● ids-ml-backend.service - IDS ML Backend (FastAPI)
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-ml-backend.service; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Tue 2026-02-17 08:50:14 CET; 1min 51s ago
|
||||||
|
Main PID: 31127 (python3)
|
||||||
|
Tasks: 26 (limit: 100409)
|
||||||
|
Memory: 256.8M (max: 2.0G available: 1.7G)
|
||||||
|
CPU: 4.073s
|
||||||
|
CGroup: /system.slice/ids-ml-backend.service
|
||||||
|
└─31127 /opt/ids/python_ml/venv/bin/python3 main.py
|
||||||
|
|
||||||
|
Feb 17 08:50:14 ids.alfacom.it systemd[1]: Started IDS ML Backend (FastAPI).
|
||||||
|
|
||||||
|
● ids-syslog-parser.service - IDS Syslog Parser (Network Logs Processor)
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-syslog-parser.service; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Mon 2026-02-16 12:18:52 CET; 20h ago
|
||||||
|
Main PID: 1069 (python3)
|
||||||
|
Tasks: 1 (limit: 100409)
|
||||||
|
Memory: 9.7M (max: 1.0G available: 1014.2M)
|
||||||
|
CPU: 1h 59min 34.854s
|
||||||
|
CGroup: /system.slice/ids-syslog-parser.service
|
||||||
|
└─1069 /opt/ids/python_ml/venv/bin/python3 syslog_parser.py
|
||||||
|
|
||||||
|
Feb 16 12:18:52 ids.alfacom.it systemd[1]: Started IDS Syslog Parser (Network Logs Processor).
|
||||||
|
|
||||||
|
● ids-auto-block.timer - IDS Auto-Blocking Timer - Run every 5 minutes
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-auto-block.timer; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Mon 2026-02-16 19:24:04 CET; 13h ago
|
||||||
|
Until: Mon 2026-02-16 19:24:04 CET; 13h ago
|
||||||
|
Trigger: n/a
|
||||||
|
Triggers: ● ids-auto-block.service
|
||||||
|
Docs: https://github.com/yourusername/ids
|
||||||
|
|
||||||
|
Feb 16 19:24:04 ids.alfacom.it systemd[1]: Started IDS Auto-Blocking Timer - Run every 5 minutes.
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
tail -f /var/log/ids/backend.log
|
||||||
|
9:21:00 AM [express] GET /api/detections 304 in 19ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:21:01 AM [express] GET /api/services/status 200 in 29ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:21:02 AM [express] GET /api/routers 200 in 3ms :: [{"id":"c6904896-59dc-4060-952f-0e55df7dee4a","n…
|
||||||
|
9:21:12 AM [express] PUT /api/routers/c6904896-59dc-4060-952f-0e55df7dee4a 200 in 8ms :: {"id":"c690…
|
||||||
|
9:21:12 AM [express] GET /api/routers 304 in 2ms :: [{"id":"c6904896-59dc-4060-952f-0e55df7dee4a","n…
|
||||||
|
9:22:16 AM [express] POST /api/ml/block-all-critical 200 in 99ms :: {"message":"Nessun IP critico da…
|
||||||
|
9:24:17 AM [express] POST /api/ml/block-all-critical 200 in 75ms :: {"message":"Nessun IP critico da…
|
||||||
|
Using standard PostgreSQL database
|
||||||
|
9:26:18 AM [express] serving on port 5000
|
||||||
|
✅ Database connection successful
|
||||||
|
9:28:19 AM [express] POST /api/ml/block-all-critical 200 in 93ms :: {"message":"Nessun IP critico da…
|
||||||
|
9:30:19 AM [express] POST /api/ml/block-all-critical 200 in 82ms :: {"message":"Nessun IP critico da…
|
||||||
|
9:30:25 AM [express] GET /api/training-history 200 in 6ms :: [{"id":"7570df54-8169-4fb2-abdc-e9c1bfa…
|
||||||
|
9:30:29 AM [express] GET /api/detections 200 in 24ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:30:29 AM [express] GET /api/whitelist 200 in 259ms :: {"items":[{"id":"49b5b9a9-4683-452c-a784-fc5…
|
||||||
|
9:30:37 AM [express] GET /api/services/status 200 in 22ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:30:39 AM [express] POST /api/services/ids-syslog-parser/start 500 in 22ms :: {"error":"Service con…
|
||||||
|
9:30:42 AM [express] GET /api/services/status 200 in 21ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:30:47 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:30:52 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:30:57 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:02 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:07 AM [express] GET /api/services/status 200 in 23ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:12 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:16 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:21 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:26 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:27 AM [express] GET /api/detections 304 in 11ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:31:27 AM [express] GET /api/whitelist 200 in 229ms :: {"items":[{"id":"91108765-7ef6-4e49-87d8-319…
|
||||||
|
9:31:30 AM [express] GET /api/public-lists 200 in 5ms :: [{"id":"2a835798-02a7-4129-a528-d39a026ad15…
|
||||||
|
9:31:32 AM [express] GET /api/services/status 200 in 19ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:37 AM [express] GET /api/services/status 200 in 20ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:42 AM [express] GET /api/services/status 200 in 19ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:47 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:52 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:31:57 AM [express] GET /api/services/status 200 in 21ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:32:02 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:32:07 AM [express] GET /api/services/status 200 in 24ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:32:12 AM [express] GET /api/services/status 200 in 24ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:32:17 AM [express] GET /api/services/status 200 in 23ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:32:25 AM [express] GET /api/services/status 200 in 2875ms :: {"services":{"mlBackend":{"name":"ML …
|
||||||
|
[BLOCK-ALL] Avvio blocco massivo: 11/11 IP con score >= 80 su 2 router
|
||||||
|
[BULK-BLOCK] Starting: 11 IPs on 2 routers (10.20.30.100, 185.203.24.2)
|
||||||
|
[BULK-BLOCK] Router 185.203.24.2: 124 IPs already in list (50ms)
|
||||||
|
9:32:30 AM [express] GET /api/services/status 200 in 31ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:32:35 AM [express] GET /api/services/status 200 in 31ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:32:40 AM [express] GET /api/services/status 200 in 46ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:32:46 AM [express] GET /api/services/status 200 in 75ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
[MIKROTIK] Failed to get address-list from 10.20.30.100: This operation was aborted
|
||||||
|
[BULK-BLOCK] Router 10.20.30.100: 0 IPs already in list (20004ms)
|
||||||
|
[BULK-BLOCK] 0 already blocked, 11 new to block
|
||||||
|
9:32:51 AM [express] GET /api/services/status 200 in 40ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 87.17.182.32: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 185.98.164.31: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 109.115.163.146: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 2.118.225.238: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 74.125.99.38: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 192.178.112.96: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 74.125.111.102: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 212.14.142.214: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8000ms for IP 89.96.215.146: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.39.230: This operation was aborted
|
||||||
|
9:32:56 AM [express] GET /api/services/status 200 in 44ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:01 AM [express] GET /api/services/status 200 in 46ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 104.19.148.8: This operation was aborted
|
||||||
|
[BULK-BLOCK] Progress: 11/11
|
||||||
|
[BULK-BLOCK] Router 10.20.30.100: 0 blocked, 11 failed, 0 skipped
|
||||||
|
[BULK-BLOCK] Router 185.203.24.2: 11 blocked, 0 failed, 0 skipped
|
||||||
|
[BULK-BLOCK] Done: 11 blocked, 0 failed, 0 skipped
|
||||||
|
[BLOCK-ALL] Database aggiornato: 11 IP marcati come bloccati
|
||||||
|
9:33:03 AM [express] POST /api/ml/block-all-critical 200 in 36137ms :: {"message":"Blocco massivo co…
|
||||||
|
9:33:07 AM [express] GET /api/services/status 200 in 1125ms :: {"services":{"mlBackend":{"name":"ML …
|
||||||
|
9:33:12 AM [express] GET /api/services/status 200 in 40ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:17 AM [express] GET /api/services/status 200 in 56ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:22 AM [express] GET /api/services/status 200 in 58ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:27 AM [express] GET /api/services/status 200 in 57ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:32 AM [express] GET /api/services/status 200 in 57ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:37 AM [express] GET /api/services/status 200 in 52ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:42 AM [express] GET /api/services/status 200 in 77ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:48 AM [express] GET /api/services/status 200 in 50ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:53 AM [express] GET /api/services/status 200 in 72ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:33:58 AM [express] GET /api/services/status 200 in 69ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:34:03 AM [express] GET /api/services/status 200 in 64ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:34:08 AM [express] GET /api/services/status 200 in 79ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
@ -0,0 +1,224 @@
|
|||||||
|
tail -f /var/log/ids/backend.log
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8000ms for IP 188.8.66.34: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.101.193.229: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 109.205.211.40: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.59.33.110: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 151.73.209.26: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 79.8.248.29: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 217.202.28.18: This operation was aborted
|
||||||
|
Using standard PostgreSQL database
|
||||||
|
9:13:09 AM [express] serving on port 5000
|
||||||
|
✅ Database connection successful
|
||||||
|
[BLOCK-ALL] Avvio blocco massivo: 100/100 IP con score >= 80 su 2 router
|
||||||
|
[BULK-BLOCK] Starting: 100 IPs on 2 routers (10.20.30.100, 185.203.24.2)
|
||||||
|
[BULK-BLOCK] Router 185.203.24.2: 74 IPs already in list (38ms)
|
||||||
|
9:14:15 AM [express] GET /api/detections 200 in 17ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:14:15 AM [express] GET /api/services/status 200 in 42ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:14:20 AM [express] GET /api/detections 200 in 19ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:14:20 AM [express] GET /api/services/status 200 in 20ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:14:25 AM [express] GET /api/detections 200 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:14:25 AM [express] GET /api/whitelist 200 in 222ms :: {"items":[{"id":"4b7a60b4-b47c-431e-8efa-864…
|
||||||
|
[MIKROTIK] Failed to get address-list from 10.20.30.100: This operation was aborted
|
||||||
|
[BULK-BLOCK] Router 10.20.30.100: 0 IPs already in list (20004ms)
|
||||||
|
[BULK-BLOCK] 0 already blocked, 100 new to block
|
||||||
|
9:14:35 AM [express] GET /api/detections 200 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:14:39 AM [express] GET /api/training-history 200 in 5ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.73.24.49: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 104.16.248.249: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 77.39.130.185: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 104.16.249.249: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 104.18.36.146: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 193.205.185.20: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 50.93.53.165: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8004ms for IP 91.208.175.82: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8004ms for IP 74.0.42.209: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8005ms for IP 79.6.115.203: This operation was aborted
|
||||||
|
9:14:50 AM [express] GET /api/detections 200 in 31ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:14:50 AM [express] GET /api/whitelist 200 in 246ms :: {"items":[{"id":"49b5b9a9-4683-452c-a784-fc5…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 142.250.181.168: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 195.32.127.72: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 93.150.41.42: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 212.183.171.12: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 78.134.17.204: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 62.94.77.251: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.73.24.107: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 74.125.45.108: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 2.42.47.211: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8004ms for IP 89.97.31.234: This operation was aborted
|
||||||
|
9:14:52 AM [express] GET /api/dashboard/live 200 in 12ms :: {"totalPackets":75520962,"attackPackets"…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 31.197.212.130: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 44.197.141.31: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 146.75.61.140: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 93.150.200.81: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 101.56.92.15: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 2.33.192.82: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.41.104.237: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 77.39.220.111: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 185.96.131.245: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8004ms for IP 142.250.181.164: This operation was aborted
|
||||||
|
9:15:00 AM [express] GET /api/routers 200 in 16ms :: [{"id":"c6904896-59dc-4060-952f-0e55df7dee4a","…
|
||||||
|
9:15:00 AM [express] GET /api/detections 200 in 24ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:15:00 AM [express] GET /api/services/status 200 in 28ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:15:05 AM [express] GET /api/detections 200 in 11ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:15:05 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:15:06 AM [express] GET /api/stats 200 in 5385ms :: {"routers":{"total":2,"enabled":2},"detections"…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 45.146.216.240: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 62.112.11.234: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 51.159.85.76: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 188.8.66.34: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 151.101.193.229: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 109.205.211.40: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.59.33.110: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.73.209.26: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 79.8.248.29: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 217.202.28.18: This operation was aborted
|
||||||
|
[ANALYTICS QUERY] {
|
||||||
|
startDate: '2026-01-18T08:15:10.899Z',
|
||||||
|
endDate: '2026-02-17T08:15:10.899Z',
|
||||||
|
hourly: true,
|
||||||
|
hourCondition: 'NOT NULL'
|
||||||
|
}
|
||||||
|
[ANALYTICS RESULTS] 41 records found
|
||||||
|
[ANALYTICS SAMPLE] {
|
||||||
|
id: 'b3216c05-7f95-4ba9-a5fb-b37461441401',
|
||||||
|
date: 2026-02-16T00:00:00.000Z,
|
||||||
|
hour: 23,
|
||||||
|
totalPackets: 10463767,
|
||||||
|
totalBytes: 13102201398,
|
||||||
|
uniqueIps: 25472,
|
||||||
|
normalPackets: 10463767,
|
||||||
|
normalBytes: 13102201398,
|
||||||
|
normalUniqueIps: 25472,
|
||||||
|
topNormalIps: '[{"ip": "88.39.149.52", "packets": 3744014, "bytes": 5747025096, "country": null}, {"ip": "79.11.175.156", "packets": 1279527, "bytes": 1892212998, "country": null}, {"ip": "95.229.133.69", "packets": 1188856, "bytes": 1758976078, "country": null}, {"ip": "188.12.75.242", "packets": 1168922, "bytes": 1729133469, "country": null}, {"ip": "95.229.85.134", "packets": 1151191, "bytes": 1703382127, "country": null}, {"ip": "74.125.45.108", "packets": 108382, "bytes": 13501983, "country": "United States"}, {"ip": "185.243.5.22", "packets": 87376, "bytes": 38384352, "country": null}, {"ip": "104.16.248.249", "packets": 72117, "bytes": 40864635, "country": "Canada"}, {"ip": "8.8.8.8", "packets": 66005, "bytes": 18862021, "country": "United States"}, {"ip": "95.110.183.67", "packets": 51456, "bytes": 2281666, "country": "Italy"}]',
|
||||||
|
attackPackets: 0,
|
||||||
|
attackBytes: 0,
|
||||||
|
attackUniqueIps: 0,
|
||||||
|
attacksByCountry: '{}',
|
||||||
|
attacksByType: '{}',
|
||||||
|
topAttackers: '[]',
|
||||||
|
trafficByCountry: '{"Canada": {"normal": 81941, "attacks": 0}, "The Netherlands": {"normal": 49409, "attacks": 0}, "Singapore": {"normal": 61, "attacks": 0}, "Germany": {"normal": 339, "attacks": 0}, "United States":
|
||||||
|
{"normal": 385230, "attacks": 0}, "Italy": {"normal": 73801, "attacks": 0}, "Netherlands": {"normal": 19928, "attacks": 0}}',
|
||||||
|
createdAt: 2026-02-17T00:05:00.185Z
|
||||||
|
}
|
||||||
|
9:15:10 AM [express] GET /api/analytics/recent 200 in 16ms :: [{"id":"b3216c05-7f95-4ba9-a5fb-b37461…
|
||||||
|
9:15:12 AM [express] GET /api/training-history 200 in 3ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7968ms for IP 151.73.139.162: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7969ms for IP 217.28.70.122: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7971ms for IP 79.9.120.141: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7971ms for IP 52.123.129.14: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7970ms for IP 157.240.231.35: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7975ms for IP 46.229.84.162: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7973ms for IP 178.248.182.171: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7974ms for IP 93.43.107.86: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7974ms for IP 178.248.51.104: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7977ms for IP 93.51.52.251: HTTP 401: {"error":401,"message":"Unauthorized"}
|
||||||
|
[BULK-BLOCK] Progress: 50/100
|
||||||
|
9:15:19 AM [express] GET /api/ml/stats 200 in 6466ms :: {"logs":{"total":126216346,"last_hour":0},"d…
|
||||||
|
9:15:22 AM [express] POST /api/ml/train 200 in 10ms :: {"message":"Training avviato in background","…
|
||||||
|
9:15:22 AM [express] GET /api/training-history 304 in 3ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 85.44.118.45: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 2.228.8.122: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8000ms for IP 188.217.110.96: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.84.198.239: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 93.54.65.87: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 213.82.166.186: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 23.216.150.137: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 185.110.20.185: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 95.100.171.8: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 79.11.222.236: This operation was aborted
|
||||||
|
9:15:28 AM [express] GET /api/ml/stats 304 in 6490ms :: {"logs":{"total":126216346,"last_hour":0},"d…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 85.35.59.252: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 79.10.32.9: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 79.13.197.84: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 94.85.21.211: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 213.215.214.82: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 185.82.114.4: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 5.158.71.206: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 94.34.87.184: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 93.67.251.46: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 31.7.147.21: This operation was aborted
|
||||||
|
9:15:32 AM [express] GET /api/training-history 304 in 15ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 91.187.197.104: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.38.150: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 217.141.0.110: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.38.157: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 140.82.121.3: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 95.255.202.79: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.58.132.84: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 82.55.154.58: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 62.98.165.6: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 95.110.183.67: This operation was aborted
|
||||||
|
9:15:42 AM [express] GET /api/training-history 304 in 16ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
|
||||||
|
9:15:45 AM [express] GET /api/ml/stats 304 in 7198ms :: {"logs":{"total":126216346,"last_hour":0},"d…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 93.40.225.146: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 216.128.11.53: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 95.230.242.4: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 2.118.179.69: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 188.8.204.7: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 185.168.176.197: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 93.64.198.214: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 217.202.56.187: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 5.63.174.25: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.64.151.157: This operation was aborted
|
||||||
|
9:15:52 AM [express] GET /api/training-history 304 in 17ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 79.2.176.5: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 82.192.139.100: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 93.40.226.33: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 93.146.168.160: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 94.101.59.182: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 185.104.127.31: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 31.197.212.236: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8000ms for IP 79.3.132.252: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.38.148: This operation was aborted
|
||||||
|
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.38.151: This operation was aborted
|
||||||
|
[BULK-BLOCK] Progress: 100/100
|
||||||
|
[BULK-BLOCK] Router 10.20.30.100: 0 blocked, 100 failed, 0 skipped
|
||||||
|
[BULK-BLOCK] Router 185.203.24.2: 50 blocked, 0 failed, 50 skipped
|
||||||
|
[BULK-BLOCK] Done: 100 blocked, 0 failed, 0 skipped
|
||||||
|
[BLOCK-ALL] Database aggiornato: 100 IP marcati come bloccati
|
||||||
|
9:15:55 AM [express] POST /api/ml/block-all-critical 200 in 100172ms :: {"message":"Blocco massivo c…
|
||||||
|
9:16:02 AM [express] GET /api/training-history 304 in 2ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
|
||||||
|
9:16:02 AM [express] GET /api/ml/stats 200 in 6527ms :: {"logs":{"total":126216346,"last_hour":0},"d…
|
||||||
|
9:16:12 AM [express] GET /api/training-history 304 in 13ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
|
||||||
|
9:16:19 AM [express] GET /api/ml/stats 304 in 6558ms :: {"logs":{"total":126216346,"last_hour":0},"d…
|
||||||
|
9:16:19 AM [express] POST /api/ml/block-all-critical 200 in 76ms :: {"message":"Nessun IP critico da…
|
||||||
|
9:16:22 AM [express] GET /api/training-history 304 in 3ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
|
||||||
|
9:16:29 AM [express] GET /api/training-history 304 in 3ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
|
||||||
|
9:16:35 AM [express] GET /api/ml/stats 304 in 6623ms :: {"logs":{"total":126216346,"last_hour":0},"d…
|
||||||
|
9:16:39 AM [express] GET /api/training-history 304 in 13ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
|
||||||
|
9:16:49 AM [express] GET /api/training-history 304 in 14ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
|
||||||
|
9:16:59 AM [express] GET /api/training-history 304 in 15ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
|
||||||
|
[ML Stats] Fallback to database - ML Backend error: This operation was aborted
|
||||||
|
9:17:06 AM [express] GET /api/ml/stats 200 in 20345ms :: {"source":"database_fallback","ml_backend_s…
|
||||||
|
9:17:08 AM [express] GET /api/detections 200 in 21ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:17:08 AM [express] GET /api/routers 200 in 21ms :: [{"id":"c6904896-59dc-4060-952f-0e55df7dee4a","…
|
||||||
|
9:17:13 AM [express] GET /api/services/status 200 in 5007ms :: {"services":{"mlBackend":{"name":"ML …
|
||||||
|
9:17:13 AM [express] GET /api/detections 304 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:17:14 AM [express] GET /api/stats 200 in 5393ms :: {"routers":{"total":2,"enabled":2},"detections"…
|
||||||
|
9:17:18 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:17:23 AM [express] GET /api/services/status 304 in 5006ms :: {"services":{"mlBackend":{"name":"ML …
|
||||||
|
9:17:23 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:17:28 AM [express] GET /api/services/status 200 in 85ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:17:28 AM [express] GET /api/detections 304 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:17:29 AM [express] GET /api/stats 304 in 5314ms :: {"routers":{"total":2,"enabled":2},"detections"…
|
||||||
|
9:17:33 AM [express] GET /api/services/status 200 in 29ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:17:33 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:17:38 AM [express] GET /api/services/status 200 in 53ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:17:39 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:17:43 AM [express] GET /api/services/status 200 in 70ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:17:44 AM [express] GET /api/detections 304 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:17:44 AM [express] GET /api/stats 304 in 5395ms :: {"routers":{"total":2,"enabled":2},"detections"…
|
||||||
|
9:17:49 AM [express] GET /api/services/status 200 in 47ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:17:49 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:17:54 AM [express] GET /api/detections 304 in 12ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:17:54 AM [express] GET /api/services/status 200 in 61ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:17:59 AM [express] GET /api/detections 304 in 11ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:17:59 AM [express] GET /api/services/status 200 in 30ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:18:00 AM [express] GET /api/stats 304 in 5405ms :: {"routers":{"total":2,"enabled":2},"detections"…
|
||||||
|
9:18:04 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:18:04 AM [express] GET /api/services/status 200 in 64ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:18:09 AM [express] GET /api/detections 304 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
|
||||||
|
9:18:09 AM [express] GET /api/services/status 200 in 21ms :: {"services":{"mlBackend":{"name":"ML Ba…
|
||||||
|
9:18:14 AM [express] GET /api/detections 304 in 21ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
|
||||||
|
9:18:15 AM [express] GET /api/stats 304 in 5344ms :: {"routers":{"total":2,"enabled":2},"detections"…
|
||||||
|
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
Cerca il watchdog che riavvia il backend
|
||||||
|
grep -r "Backend Python NON attivo" /opt/ids/ --include="*.sh"
|
||||||
|
grep -r "Backend Python NON attivo" /etc/cron* /var/spool/cron/
|
||||||
|
|
||||||
|
# Verifica cron jobs attivi
|
||||||
|
crontab -l
|
||||||
|
crontab -l -u ids
|
||||||
|
|
||||||
|
# Verifica timer systemd
|
||||||
|
systemctl list-timers --all | grep ids
|
||||||
|
/opt/ids/deployment/check_backend.sh: echo "[$(date)] Backend Python NON attivo, riavvio..." >> "$LOG_FILE"
|
||||||
|
# ============================================
|
||||||
|
# SISTEMA IDS - CONFIGURAZIONE AUTOMATICA
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Training ML ogni 12 ore (alle 00:00 e 12:00)
|
||||||
|
0 */12 * * * /opt/ids/deployment/cron_train.sh
|
||||||
|
|
||||||
|
# Detection automatica ogni 5 minuti
|
||||||
|
*/3 * * * * /opt/ids/deployment/cron_detect.sh
|
||||||
|
|
||||||
|
# Verifica processo backend Python ogni 5 minuti (riavvia se non attivo)
|
||||||
|
*/5 * * * * /opt/ids/deployment/check_backend.sh >> /var/log/ids/cron.log 2>&1
|
||||||
|
|
||||||
|
# Verifica processo frontend ogni 5 minuti (riavvia se non attivo)
|
||||||
|
*/5 * * * * /opt/ids/deployment/check_frontend.sh >> /var/log/ids/cron.log 2>&1
|
||||||
|
|
||||||
|
# Pulizia log settimanale (ogni domenica alle 02:00)
|
||||||
|
0 2 * * 0 find /var/log/ids -name "*.log" -size +100M -exec truncate -s 50M {} \; >> /var/log/ids/cron.log 2>&1
|
||||||
|
|
||||||
|
# Restart completo del sistema ogni settimana (domenica alle 03:00)
|
||||||
|
0 3 * * 0 /opt/ids/deployment/restart_all.sh >> /var/log/ids/cron.log 2>&1
|
||||||
|
|
||||||
|
# Backup database giornaliero (alle 04:00)
|
||||||
|
0 4 * * * /opt/ids/deployment/backup_db.sh >> /var/log/ids/cron.log 2>&1
|
||||||
|
0 3 * * * /opt/ids/deployment/cleanup_database.sh >> /var/log/ids/cleanup.log 2>&1
|
||||||
|
Mon 2026-02-16 13:05:00 CET 4min 9s left Mon 2026-02-16 12:05:00 CET 55min ago ids-analytics-aggregator.timer ids-analytics-aggregator.service
|
||||||
|
Mon 2026-02-16 13:14:33 CET 13min left Mon 2026-02-16 12:13:57 CET 46min ago ids-cleanup.timer ids-cleanup.service
|
||||||
|
Mon 2026-02-23 03:00:00 CET 6 days left Mon 2026-02-16 03:00:00 CET 10h ago ids-ml-training.timer ids-ml-training.service
|
||||||
|
- - Mon 2026-02-16 12:48:47 CET 12min ago ids-auto-block.timer ids-auto-block.service
|
||||||
|
- - Mon 2026-02-16 13:00:01 CET 48s ago ids-list-fetcher.timer ids-list-fetcher.service
|
||||||
@ -0,0 +1,158 @@
|
|||||||
|
echo "=== 1. STATO SERVIZI ===" && systemctl status ids-backend ids-ml-backend ids-syslog-parser ids-analytics ids-auto-block.timer ids-auto-block.service --no-pager -l 2>&1 | tail -80 && echo "=== 2. LOG
|
||||||
|
NODE.JS ===" && journalctl -u ids-backend --no-pager -n 50 && echo "=== 3. LOG ML ===" && journalctl -u ids-ml-backend --no-pager -n 50 && echo "=== 4. LOG AUTO-BLOCK ===" && journalctl -u ids-auto-block --no-pager -n 50 && echo "=== 5. LOG SYSLOG ===" && journalctl -u ids-syslog-parser --no-pager -n 30 && echo "=== 6. PORTE ===" && ss -tlnp | grep -E '3001|5001|514' && echo "=== 7. PROCESSI ===" && ps aux | grep -E 'node|python|uvicorn' | grep -v grep && echo "=== 8. DISCO/MEMORIA ===" && df -h / && free -h && echo "=== 9. TEST CONNESSIONE ===" && curl -s -o /dev/null -w "%{http_code} - Node.js backend\n" http://localhost:3001/api/health && curl -s -o /dev/null -w "%{http_code} - ML backend\n" http://localhost:5001/health && echo "=== 10. LOG DB ===" && sudo -u ids psql -d ids_db -c "SELECT COUNT(*) as logs_last_30min FROM network_logs WHERE timestamp > NOW() - INTERVAL '30 minutes';"
|
||||||
|
=== 1. STATO SERVIZI ===
|
||||||
|
Unit ids-backend.service could not be found.
|
||||||
|
Unit ids-analytics.service could not be found.
|
||||||
|
● ids-ml-backend.service - IDS ML Backend (FastAPI)
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-ml-backend.service; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Mon 2026-02-16 19:29:06 CET; 13h ago
|
||||||
|
Main PID: 17629 (python3)
|
||||||
|
Tasks: 26 (limit: 100409)
|
||||||
|
Memory: 75.8M (max: 2.0G available: 1.9G)
|
||||||
|
CPU: 40.396s
|
||||||
|
CGroup: /system.slice/ids-ml-backend.service
|
||||||
|
└─17629 /opt/ids/python_ml/venv/bin/python3 main.py
|
||||||
|
|
||||||
|
Feb 16 19:29:06 ids.alfacom.it systemd[1]: Started IDS ML Backend (FastAPI).
|
||||||
|
|
||||||
|
● ids-syslog-parser.service - IDS Syslog Parser (Network Logs Processor)
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-syslog-parser.service; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Mon 2026-02-16 12:18:52 CET; 20h ago
|
||||||
|
Main PID: 1069 (python3)
|
||||||
|
Tasks: 1 (limit: 100409)
|
||||||
|
Memory: 9.7M (max: 1.0G available: 1014.2M)
|
||||||
|
CPU: 1h 59min 34.173s
|
||||||
|
CGroup: /system.slice/ids-syslog-parser.service
|
||||||
|
└─1069 /opt/ids/python_ml/venv/bin/python3 syslog_parser.py
|
||||||
|
|
||||||
|
Feb 16 12:18:52 ids.alfacom.it systemd[1]: Started IDS Syslog Parser (Network Logs Processor).
|
||||||
|
|
||||||
|
● ids-auto-block.timer - IDS Auto-Blocking Timer - Run every 5 minutes
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-auto-block.timer; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Mon 2026-02-16 19:24:04 CET; 13h ago
|
||||||
|
Until: Mon 2026-02-16 19:24:04 CET; 13h ago
|
||||||
|
Trigger: n/a
|
||||||
|
Triggers: ● ids-auto-block.service
|
||||||
|
Docs: https://github.com/yourusername/ids
|
||||||
|
|
||||||
|
Feb 16 19:24:04 ids.alfacom.it systemd[1]: Started IDS Auto-Blocking Timer - Run every 5 minutes.
|
||||||
|
|
||||||
|
● ids-auto-block.service - IDS Auto-Blocking Service - Detect and Block Malicious IPs
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-auto-block.service; disabled; preset: disabled)
|
||||||
|
Active: activating (start) since Tue 2026-02-17 08:33:33 CET; 3min 14s ago
|
||||||
|
TriggeredBy: ● ids-auto-block.timer
|
||||||
|
Main PID: 30644 (python3)
|
||||||
|
Tasks: 1 (limit: 100409)
|
||||||
|
Memory: 14.7M
|
||||||
|
CPU: 148ms
|
||||||
|
CGroup: /system.slice/ids-auto-block.service
|
||||||
|
└─30644 /opt/ids/python_ml/venv/bin/python3 /opt/ids/python_ml/auto_block.py
|
||||||
|
|
||||||
|
Feb 17 08:33:33 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
=== 2. LOG NODE.JS ===
|
||||||
|
-- No entries --
|
||||||
|
=== 3. LOG ML ===
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 12676 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 12677 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 12681 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 12682 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 12684 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: ids-ml-backend.service: Main process exited, code=killed, status=9/KILL
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: ids-ml-backend.service: Failed with result 'timeout'.
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: Stopped IDS ML Backend (FastAPI).
|
||||||
|
Feb 16 15:51:21 ids.alfacom.it systemd[1]: ids-ml-backend.service: Consumed 9.526s CPU time.
|
||||||
|
Feb 16 15:51:26 ids.alfacom.it systemd[1]: Started IDS ML Backend (FastAPI).
|
||||||
|
Feb 16 16:50:11 ids.alfacom.it systemd[1]: Stopping IDS ML Backend (FastAPI)...
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: State 'stop-sigterm' timed out. Killing.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13099 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13102 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13103 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13110 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13112 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13116 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13117 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13122 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 13125 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Main process exited, code=killed, status=9/KILL
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Failed with result 'timeout'.
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: Stopped IDS ML Backend (FastAPI).
|
||||||
|
Feb 16 16:51:41 ids.alfacom.it systemd[1]: ids-ml-backend.service: Consumed 15.919s CPU time.
|
||||||
|
Feb 16 16:51:46 ids.alfacom.it systemd[1]: Started IDS ML Backend (FastAPI).
|
||||||
|
Feb 16 19:27:20 ids.alfacom.it systemd[1]: Stopping IDS ML Backend (FastAPI)...
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: State 'stop-sigterm' timed out. Killing.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14614 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14619 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14626 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14675 (n/a) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14676 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14677 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14678 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14679 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14680 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14681 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14682 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Killing process 14683 (python3) with signal SIGKILL.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Main process exited, code=killed, status=9/KILL
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Failed with result 'timeout'.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: Stopped IDS ML Backend (FastAPI).
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: ids-ml-backend.service: Consumed 15.247s CPU time.
|
||||||
|
Feb 16 19:28:50 ids.alfacom.it systemd[1]: Started IDS ML Backend (FastAPI).
|
||||||
|
Feb 16 19:29:00 ids.alfacom.it systemd[1]: Stopping IDS ML Backend (FastAPI)...
|
||||||
|
Feb 16 19:29:01 ids.alfacom.it systemd[1]: ids-ml-backend.service: Deactivated successfully.
|
||||||
|
Feb 16 19:29:01 ids.alfacom.it systemd[1]: Stopped IDS ML Backend (FastAPI).
|
||||||
|
Feb 16 19:29:01 ids.alfacom.it systemd[1]: ids-ml-backend.service: Consumed 4.113s CPU time.
|
||||||
|
Feb 16 19:29:06 ids.alfacom.it systemd[1]: Started IDS ML Backend (FastAPI).
|
||||||
|
=== 4. LOG AUTO-BLOCK ===
|
||||||
|
Feb 17 07:45:29 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 07:45:29 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 07:49:30 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 07:49:30 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 07:49:30 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 07:49:30 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 07:53:30 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 07:53:30 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 07:53:30 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 07:53:30 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 07:57:30 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 07:57:30 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 07:57:30 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 07:57:30 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:01:31 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:01:31 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:01:31 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:01:31 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:05:31 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:05:31 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:05:31 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:05:31 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:09:31 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:09:31 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:09:31 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:09:31 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:13:32 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:13:32 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:13:32 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:13:32 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:17:32 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:17:32 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:17:32 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:17:32 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:21:32 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:21:32 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:21:32 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:21:32 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:25:33 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:25:33 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:25:33 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:25:33 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:29:33 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:29:33 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:29:33 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:29:33 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 17 08:33:33 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 17 08:33:33 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 17 08:33:33 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 17 08:33:33 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
=== 5. LOG SYSLOG ===
|
||||||
|
Feb 16 12:18:52 ids.alfacom.it systemd[1]: Started IDS Syslog Parser (Network Logs Processor).
|
||||||
|
=== 6. PORTE ===
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
echo "=== VERIFICA BACKEND NODE.JS ===" && ls -la /etc/systemd/system/ids-*.service /etc/systemd/system/ids-*.timer && echo "=== FILE SERVICE DISPONIBILI ===" && cat /etc/systemd/system/ids-backend.service 2>&1 || echo "FILE NON TROVATO" && echo "=== NGINX/REVERSE PROXY ===" && ss -tlnp | grep -E '80|443|3001|5001' && echo "=== TEST PORTA 3001 ===" && curl -v --connect-timeout 5 http://localhost:3001/api/health 2>&1 && echo "=== COME VIENE AVVIATO NODE.JS ===" && ps aux | grep -i node | grep -v grep && echo "=== PM2 STATUS ===" && pm2 list 2>&1 || echo "PM2 non installato" && echo "=== CONTENUTO /opt/ids/ ===" && ls -la /opt/ids/ && echo "=== PACKAGE.JSON ===" && cat /opt/ids/package.json 2>&1 | head -30 && echo "=== AUTO_BLOCK OUTPUT DETTAGLIATO ===" && sudo -u ids /opt/ids/python_ml/venv/bin/python3 /opt/ids/python_ml/auto_block.py 2>&1
|
||||||
|
=== VERIFICA BACKEND NODE.JS ===
|
||||||
|
-rw-r--r--. 1 root root 473 Feb 16 15:52 /etc/systemd/system/ids-analytics-aggregator.service
|
||||||
|
-rw-r--r--. 1 root root 339 Feb 16 15:52 /etc/systemd/system/ids-analytics-aggregator.timer
|
||||||
|
-rw-r--r--. 1 root root 674 Feb 16 19:23 /etc/systemd/system/ids-auto-block.service
|
||||||
|
-rw-r--r--. 1 root root 457 Feb 14 11:42 /etc/systemd/system/ids-auto-block.timer
|
||||||
|
-rw-r--r--. 1 root root 550 Nov 25 11:47 /etc/systemd/system/ids-cleanup.service
|
||||||
|
-rw-r--r--. 1 root root 440 Nov 25 11:47 /etc/systemd/system/ids-cleanup.timer
|
||||||
|
-rw-r--r--. 1 root root 623 Nov 27 19:29 /etc/systemd/system/ids-list-fetcher.service
|
||||||
|
-rw-r--r--. 1 root root 246 Nov 27 19:29 /etc/systemd/system/ids-list-fetcher.timer
|
||||||
|
-rw-r--r--. 1 root root 675 Nov 24 12:12 /etc/systemd/system/ids-ml-backend.service
|
||||||
|
-rw-r--r--. 1 root root 620 Nov 24 19:19 /etc/systemd/system/ids-ml-training.service
|
||||||
|
-rw-r--r--. 1 root root 398 Nov 24 19:19 /etc/systemd/system/ids-ml-training.timer
|
||||||
|
-rw-r--r--. 1 root root 727 Nov 24 12:12 /etc/systemd/system/ids-syslog-parser.service
|
||||||
|
=== FILE SERVICE DISPONIBILI ===
|
||||||
|
cat: /etc/systemd/system/ids-backend.service: No such file or directory
|
||||||
|
FILE NON TROVATO
|
||||||
|
=== NGINX/REVERSE PROXY ===
|
||||||
|
LISTEN 1107 2048 0.0.0.0:8000 0.0.0.0:* users:(("python3",pid=17629,fd=12))
|
||||||
|
=== TEST PORTA 3001 ===
|
||||||
|
* Trying ::1:3001...
|
||||||
|
* connect to ::1 port 3001 failed: Connection refused
|
||||||
|
* Trying 127.0.0.1:3001...
|
||||||
|
* connect to 127.0.0.1 port 3001 failed: Connection refused
|
||||||
|
* Failed to connect to localhost port 3001: Connection refused
|
||||||
|
* Closing connection 0
|
||||||
|
curl: (7) Failed to connect to localhost port 3001: Connection refused
|
||||||
|
PM2 non installato
|
||||||
|
=== CONTENUTO /opt/ids/ ===
|
||||||
|
total 608
|
||||||
|
drwxr-xr-x. 14 ids ids 4096 Feb 16 19:28 .
|
||||||
|
drwxr-xr-x. 3 root root 43 Nov 17 18:20 ..
|
||||||
|
-rw-------. 1 ids ids 508 Feb 16 19:28 .env
|
||||||
|
-rw-r-----. 1 root root 508 Feb 16 19:28 .env.backup
|
||||||
|
-rw-r--r--. 1 ids ids 446 Nov 17 18:23 .env.example
|
||||||
|
drwxr-xr-x. 8 ids ids 4096 Feb 16 19:28 .git
|
||||||
|
-rw-r--r--. 1 ids ids 686 Nov 17 18:23 .gitignore
|
||||||
|
-rw-r--r--. 1 ids ids 801 Jan 2 12:50 .replit
|
||||||
|
-rw-r--r--. 1 ids ids 6264 Nov 17 17:08 GUIDA_INSTALLAZIONE.md
|
||||||
|
-rw-r--r--. 1 ids ids 44765 Feb 16 08:50 IDS_Conformita_ISO27001.docx
|
||||||
|
-rw-r--r--. 1 ids ids 7595 Nov 25 19:14 MIKROTIK_API_FIX.md
|
||||||
|
-rw-r--r--. 1 ids ids 8452 Nov 17 16:40 README.md
|
||||||
|
-rw-r--r--. 1 ids ids 9092 Nov 17 16:40 RISPOSTA_DEPLOYMENT.md
|
||||||
|
drwxr-xr-x. 2 ids ids 12288 Feb 16 16:49 attached_assets
|
||||||
|
drwxr-xr-x. 2 ids ids 4096 Feb 17 04:00 backups
|
||||||
|
drwxr-xr-x. 4 ids ids 49 Nov 17 16:40 client
|
||||||
|
-rw-r--r--. 1 ids ids 459 Nov 17 16:40 components.json
|
||||||
|
drwxr-xr-x. 3 ids ids 4096 Feb 16 19:28 database-schema
|
||||||
|
-rwxr-xr-x. 1 ids ids 10264 Nov 17 18:23 deploy-to-gitlab.sh
|
||||||
|
drwxr-xr-x. 7 ids ids 4096 Feb 16 19:28 deployment
|
||||||
|
-rw-r--r--. 1 ids ids 3165 Nov 17 16:40 design_guidelines.md
|
||||||
|
drwxr-xr-x. 3 root root 36 Nov 24 11:05 dist
|
||||||
|
-rw-r--r--. 1 ids ids 325 Nov 17 16:40 drizzle.config.ts
|
||||||
|
drwxr-xr-x. 4 ids ids 4096 Nov 17 16:40 extracted_idf
|
||||||
|
-rw-r--r--. 1 ids ids 28609 Feb 16 08:50 generate_iso27001_doc.py
|
||||||
|
-rw-r--r--. 1 ids ids 1033 Nov 17 17:08 git.env.example
|
||||||
|
-rw-r--r--. 1 ids ids 96 Nov 26 11:14 main.py
|
||||||
|
drwxr-xr-x. 328 ids ids 12288 Feb 16 19:28 node_modules
|
||||||
|
-rw-r--r--. 1 ids ids 299523 Feb 16 19:28 package-lock.json
|
||||||
|
-rw-r--r--. 1 ids ids 3696 Nov 17 16:40 package.json
|
||||||
|
-rw-r--r--. 1 ids ids 80 Nov 17 16:40 postcss.config.js
|
||||||
|
-rwxr-xr-x. 1 ids ids 2496 Nov 17 16:40 push-gitlab.sh
|
||||||
|
-rw-r--r--. 1 ids ids 191 Feb 16 08:50 pyproject.toml
|
||||||
|
drwxr-xr-x. 7 ids ids 4096 Feb 16 16:49 python_ml
|
||||||
|
-rw-r--r--. 1 ids ids 5796 Feb 16 12:33 replit.md
|
||||||
|
drwxr-xr-x. 2 ids ids 104 Feb 16 12:55 server
|
||||||
|
drwxr-xr-x. 2 ids ids 23 Jan 2 15:50 shared
|
||||||
|
-rw-r--r--. 1 ids ids 4050 Nov 17 16:40 tailwind.config.ts
|
||||||
|
-rw-r--r--. 1 ids ids 657 Nov 17 16:40 tsconfig.json
|
||||||
|
-rw-r--r--. 1 ids ids 37505 Feb 16 08:50 uv.lock
|
||||||
|
-rw-r--r--. 1 ids ids 7329 Feb 16 19:28 version.json
|
||||||
|
-rw-r--r--. 1 ids ids 1080 Nov 17 16:40 vite.config.ts
|
||||||
|
=== PACKAGE.JSON ===
|
||||||
|
{
|
||||||
|
"name": "rest-express",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "NODE_ENV=development tsx server/index.ts",
|
||||||
|
"build": "vite build && esbuild server/index.ts --platform=node --packages=external --bundle --format=esm --outdir=dist",
|
||||||
|
"start": "NODE_ENV=production node dist/index.js",
|
||||||
|
"check": "tsc",
|
||||||
|
"db:push": "drizzle-kit push"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hookform/resolvers": "^3.10.0",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.25",
|
||||||
|
"@neondatabase/serverless": "^0.10.4",
|
||||||
|
"@radix-ui/react-accordion": "^1.2.4",
|
||||||
|
"@radix-ui/react-alert-dialog": "^1.1.7",
|
||||||
|
"@radix-ui/react-aspect-ratio": "^1.1.3",
|
||||||
|
"@radix-ui/react-avatar": "^1.1.4",
|
||||||
|
"@radix-ui/react-checkbox": "^1.1.5",
|
||||||
|
"@radix-ui/react-collapsible": "^1.1.4",
|
||||||
|
"@radix-ui/react-context-menu": "^2.2.7",
|
||||||
|
"@radix-ui/react-dialog": "^1.1.7",
|
||||||
|
"@radix-ui/react-dropdown-menu": "^2.1.7",
|
||||||
|
"@radix-ui/react-hover-card": "^1.1.7",
|
||||||
|
"@radix-ui/react-label": "^2.1.3",
|
||||||
|
"@radix-ui/react-menubar": "^1.1.7",
|
||||||
|
"@radix-ui/react-navigation-menu": "^1.2.6",
|
||||||
|
"@radix-ui/react-popover": "^1.1.7",
|
||||||
|
=== AUTO_BLOCK OUTPUT DETTAGLIATO ===
|
||||||
|
[2026-02-17 08:38:05] Starting auto-block cycle...
|
||||||
|
[2026-02-17 08:38:05] Step 1: Detection ML...
|
||||||
|
[2026-02-17 08:38:05] ML Detection timeout, skip (blocco IP esistenti continua)
|
||||||
|
[2026-02-17 08:38:05] Step 2: Blocco IP critici sui router...
|
||||||
|
[2026-02-17 08:38:05] ERRORE: Timeout blocco IP (120s)
|
||||||
|
[root@ids ~]#
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
journalctl -u ids-analytics-aggregator.timer -f
|
||||||
|
Feb 16 12:18:50 ids.alfacom.it systemd[1]: Started IDS Analytics Aggregation Timer - Runs every hour.
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: ids-analytics-aggregator.timer: Deactivated successfully.
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Stopped IDS Analytics Aggregation Timer - Runs every hour.
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Stopping IDS Analytics Aggregation Timer - Runs every hour...
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Started IDS Analytics Aggregation Timer - Runs every hour.
|
||||||
|
^C
|
||||||
|
[root@ids ids]# systemctl status ids-ml-backend
|
||||||
|
● ids-ml-backend.service - IDS ML Backend (FastAPI)
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-ml-backend.service; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Mon 2026-02-16 15:51:26 CET; 9min ago
|
||||||
|
Main PID: 13099 (python3)
|
||||||
|
Tasks: 26 (limit: 100409)
|
||||||
|
Memory: 402.9M (max: 2.0G available: 1.6G)
|
||||||
|
CPU: 15.905s
|
||||||
|
CGroup: /system.slice/ids-ml-backend.service
|
||||||
|
└─13099 /opt/ids/python_ml/venv/bin/python3 main.py
|
||||||
|
|
||||||
|
Feb 16 15:51:26 ids.alfacom.it systemd[1]: Started IDS ML Backend (FastAPI).
|
||||||
|
[root@ids ids]# cat /var/log/ids/backend.log | tail -20
|
||||||
|
[Mon Feb 16 15:40:04 CET 2026] Backend riavviato con PID: 12165
|
||||||
|
INFO: Started server process [12165]
|
||||||
|
INFO: Waiting for application startup.
|
||||||
|
INFO: Application startup complete.
|
||||||
|
ERROR: [Errno 98] error while attempting to bind on address ('0.0.0.0', 8000): address already in use
|
||||||
|
INFO: Waiting for application shutdown.
|
||||||
|
INFO: Application shutdown complete.
|
||||||
|
[WARNING] Extended Isolation Forest not available, using standard IF
|
||||||
|
[ML] Using Hybrid ML Detector (Extended Isolation Forest + Feature Selection)
|
||||||
|
[HYBRID] Ensemble classifier loaded
|
||||||
|
[HYBRID] Models loaded (version: latest)
|
||||||
|
[HYBRID] Selected features: 18/25
|
||||||
|
[HYBRID] Mode: Hybrid (IF + Ensemble)
|
||||||
|
[ML] ✓ Hybrid detector models loaded and ready
|
||||||
|
Starting IDS API on http://0.0.0.0:8000
|
||||||
|
Docs available at http://0.0.0.0:8000/docs
|
||||||
|
[Mon Feb 16 15:45:01 CET 2026] Backend Python NON attivo, riavvio via systemctl...
|
||||||
|
[Mon Feb 16 15:45:04 CET 2026] ERRORE: Backend non si è avviato. Controlla: journalctl -u ids-ml-backend
|
||||||
|
[Mon Feb 16 15:50:01 CET 2026] Backend Python NON attivo, riavvio via systemctl...
|
||||||
|
[Mon Feb 16 15:50:04 CET 2026] ERRORE: Backend non si è avviato. Controlla: journalctl -u ids-ml-backend
|
||||||
|
[root@ids ids]# systemctl status ids-auto-block
|
||||||
|
journalctl -u ids-auto-block --no-pager | tail -20
|
||||||
|
× ids-auto-block.service - IDS Auto-Blocking Service - Detect and Block Malicious IPs
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-auto-block.service; disabled; preset: disabled)
|
||||||
|
Active: failed (Result: signal) since Mon 2026-02-16 12:47:58 CET; 3h 13min ago
|
||||||
|
TriggeredBy: ○ ids-auto-block.timer
|
||||||
|
Docs: https://github.com/yourusername/ids
|
||||||
|
Main PID: 2896 (code=killed, signal=TERM)
|
||||||
|
CPU: 155ms
|
||||||
|
|
||||||
|
Feb 16 12:46:47 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 16 12:47:58 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=killed, status=15/TERM
|
||||||
|
Feb 16 12:47:58 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'signal'.
|
||||||
|
Feb 16 12:47:58 ids.alfacom.it systemd[1]: Stopped IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 16 12:38:46 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 16 12:40:46 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 16 12:40:46 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 16 12:40:46 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 16 12:40:46 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 16 12:42:46 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 16 12:42:46 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 16 12:42:46 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 16 12:42:46 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 16 12:44:47 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 16 12:44:47 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 16 12:44:47 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 16 12:44:47 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 16 12:46:47 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=exited, status=1/FAILURE
|
||||||
|
Feb 16 12:46:47 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'exit-code'.
|
||||||
|
Feb 16 12:46:47 ids.alfacom.it systemd[1]: Failed to start IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
Feb 16 12:46:47 ids.alfacom.it systemd[1]: Starting IDS Auto-Blocking Service - Detect and Block Malicious IPs...
|
||||||
|
Feb 16 12:47:58 ids.alfacom.it systemd[1]: ids-auto-block.service: Main process exited, code=killed, status=15/TERM
|
||||||
|
Feb 16 12:47:58 ids.alfacom.it systemd[1]: ids-auto-block.service: Failed with result 'signal'.
|
||||||
|
Feb 16 12:47:58 ids.alfacom.it systemd[1]: Stopped IDS Auto-Blocking Service - Detect and Block Malicious IPs.
|
||||||
|
[root@ids ids]# curl -X POST http://localhost:5000/api/ml/block-all-critical \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"min_score": 80, "limit": 200}'
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
sudo /opt/ids/deployment/setup_analytics_timer.sh
|
||||||
|
╔═══════════════════════════════════════════════╗
|
||||||
|
║ IDS Analytics Timer Setup ║
|
||||||
|
╚═══════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
Copia file systemd...
|
||||||
|
Reload systemd daemon...
|
||||||
|
⚙ Enable e start timer...
|
||||||
|
|
||||||
|
Stato timer:
|
||||||
|
● ids-analytics-aggregator.timer - IDS Analytics Aggregation Timer - Runs every hour
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-analytics-aggregator.timer; enabled; preset: disabled)
|
||||||
|
Active: active (waiting) since Mon 2026-02-16 12:40:08 CET; 3h 12min ago
|
||||||
|
Until: Mon 2026-02-16 12:40:08 CET; 3h 12min ago
|
||||||
|
Trigger: Mon 2026-02-16 16:05:00 CET; 12min left
|
||||||
|
Triggers: ● ids-analytics-aggregator.service
|
||||||
|
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Stopped IDS Analytics Aggregation Timer - Runs every hour.
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Stopping IDS Analytics Aggregation Timer - Runs every hour...
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Started IDS Analytics Aggregation Timer - Runs every hour.
|
||||||
|
|
||||||
|
Prossime esecuzioni:
|
||||||
|
NEXT LEFT LAST PASSED UNIT ACTIVATES
|
||||||
|
Mon 2026-02-16 16:05:00 CET 12min left Mon 2026-02-16 15:05:00 CET 47min ago ids-analytics-aggregator.timer ids-analytics-aggregator.service
|
||||||
|
|
||||||
|
1 timers listed.
|
||||||
|
Pass --all to see loaded but inactive timers, too.
|
||||||
|
|
||||||
|
╔═══════════════════════════════════════════════╗
|
||||||
|
║ ✅ ANALYTICS TIMER CONFIGURATO ║
|
||||||
|
╚═══════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
📝 Comandi utili:
|
||||||
|
Stato timer: sudo systemctl status ids-analytics-aggregator.timer
|
||||||
|
Prossime run: sudo systemctl list-timers
|
||||||
|
Log aggregazione: sudo journalctl -u ids-analytics-aggregator -f
|
||||||
|
Test manuale: sudo systemctl start ids-analytics-aggregator
|
||||||
|
|
||||||
|
[root@ids ids]# systemctl status ids-analytics-aggregator.timer
|
||||||
|
● ids-analytics-aggregator.timer - IDS Analytics Aggregation Timer - Runs every hour
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-analytics-aggregator.timer; enabled; preset: disabled)
|
||||||
|
Active: active (waiting) since Mon 2026-02-16 12:40:08 CET; 3h 12min ago
|
||||||
|
Until: Mon 2026-02-16 12:40:08 CET; 3h 12min ago
|
||||||
|
Trigger: Mon 2026-02-16 16:05:00 CET; 11min left
|
||||||
|
Triggers: ● ids-analytics-aggregator.service
|
||||||
|
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Stopped IDS Analytics Aggregation Timer - Runs every hour.
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Stopping IDS Analytics Aggregation Timer - Runs every hour...
|
||||||
|
Feb 16 12:40:08 ids.alfacom.it systemd[1]: Started IDS Analytics Aggregation Timer - Runs every hour.
|
||||||
|
[root@ids ids]# cd /opt/ids && ./deployment/run_analytics.sh
|
||||||
|
Usage: ./deployment/run_analytics.sh {hourly|daily}
|
||||||
|
[root@ids ids]# cd /opt/ids && ./deployment/run_analytics.sh {1}
|
||||||
|
Errore: modo deve essere 'hourly' o 'daily'
|
||||||
|
[root@ids ids]# cd /opt/ids && ./deployment/run_analytics.sh {hourly}
|
||||||
|
Errore: modo deve essere 'hourly' o 'daily'
|
||||||
|
[root@ids ids]# cd /opt/ids && ./deployment/run_analytics.sh {hourly=1}
|
||||||
|
Errore: modo deve essere 'hourly' o 'daily'
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
systemctl stop ids-ml-backend
|
||||||
|
[root@ids ~]# systemctl start ids-ml-backend
|
||||||
|
[root@ids ~]# systemctl status ids-ml-backend
|
||||||
|
● ids-ml-backend.service - IDS ML Backend (FastAPI)
|
||||||
|
Loaded: loaded (/etc/systemd/system/ids-ml-backend.service; enabled; preset: disabled)
|
||||||
|
Active: active (running) since Mon 2026-02-16 12:59:19 CET; 4s ago
|
||||||
|
Main PID: 3600 (python3)
|
||||||
|
Tasks: 26 (limit: 100409)
|
||||||
|
Memory: 157.6M (max: 2.0G available: 1.8G)
|
||||||
|
CPU: 3.936s
|
||||||
|
CGroup: /system.slice/ids-ml-backend.service
|
||||||
|
└─3600 /opt/ids/python_ml/venv/bin/python3 main.py
|
||||||
|
|
||||||
|
Feb 16 12:59:19 ids.alfacom.it systemd[1]: Started IDS ML Backend (FastAPI).
|
||||||
|
[root@ids ~]# cat /etc/systemd/system/ids-ml-backend.service
|
||||||
|
[Unit]
|
||||||
|
Description=IDS ML Backend (FastAPI)
|
||||||
|
After=network.target postgresql-16.service
|
||||||
|
Wants=postgresql-16.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=ids
|
||||||
|
Group=ids
|
||||||
|
WorkingDirectory=/opt/ids/python_ml
|
||||||
|
EnvironmentFile=/opt/ids/.env
|
||||||
|
|
||||||
|
# Comando esecuzione (usa virtual environment)
|
||||||
|
ExecStart=/opt/ids/python_ml/venv/bin/python3 main.py
|
||||||
|
|
||||||
|
# Restart automatico sempre (non solo on-failure)
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
StartLimitInterval=300
|
||||||
|
StartLimitBurst=5
|
||||||
|
|
||||||
|
# Limiti risorse
|
||||||
|
LimitNOFILE=65536
|
||||||
|
MemoryMax=2G
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
StandardOutput=append:/var/log/ids/ml_backend.log
|
||||||
|
StandardError=append:/var/log/ids/ml_backend.log
|
||||||
|
SyslogIdentifier=ids-ml-backend
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
[root@ids ~]# tail -f /var/log/ids/backend.log
|
||||||
|
🚀 Starting IDS API on http://0.0.0.0:8000
|
||||||
|
📚 Docs available at http://0.0.0.0:8000/docs
|
||||||
|
[Mon Feb 16 12:56:12 CET 2026] Backend Python NON attivo, riavvio...
|
||||||
|
[Mon Feb 16 12:56:14 CET 2026] Backend riavviato con PID: 3453
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/opt/ids/python_ml/main.py", line 21, in <module>
|
||||||
|
from ml_hybrid_detector import MLHybridDetector
|
||||||
|
File "/opt/ids/python_ml/ml_hybrid_detector.py", line 13, in <module>
|
||||||
|
from xgboost import XGBClassifier
|
||||||
|
ModuleNotFoundError: No module named 'xgboost'
|
||||||
|
|
||||||
@ -2,25 +2,21 @@ import { useQuery, useMutation } from "@tanstack/react-query";
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Activity, Brain, Database, FileText, Terminal, RefreshCw, AlertCircle, Play, Square, RotateCw } from "lucide-react";
|
import { Activity, Brain, Database, FileText, Terminal, RefreshCw, Play, Square, RotateCw, Shield, Trash2, ListChecks, GraduationCap, Server, Clock, Timer } from "lucide-react";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { queryClient, apiRequest } from "@/lib/queryClient";
|
import { queryClient, apiRequest } from "@/lib/queryClient";
|
||||||
|
|
||||||
interface ServiceStatus {
|
interface ServiceStatus {
|
||||||
name: string;
|
name: string;
|
||||||
status: "running" | "idle" | "offline" | "error" | "unknown";
|
status: string;
|
||||||
healthy: boolean;
|
healthy: boolean;
|
||||||
details: any;
|
details: any;
|
||||||
|
systemdUnit: string;
|
||||||
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServicesStatusResponse {
|
interface ServicesStatusResponse {
|
||||||
services: {
|
services: Record<string, ServiceStatus>;
|
||||||
mlBackend: ServiceStatus;
|
|
||||||
database: ServiceStatus;
|
|
||||||
syslogParser: ServiceStatus;
|
|
||||||
analyticsAggregator: ServiceStatus;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ServicesPage() {
|
export default function ServicesPage() {
|
||||||
@ -28,10 +24,9 @@ export default function ServicesPage() {
|
|||||||
|
|
||||||
const { data: servicesStatus, isLoading, refetch } = useQuery<ServicesStatusResponse>({
|
const { data: servicesStatus, isLoading, refetch } = useQuery<ServicesStatusResponse>({
|
||||||
queryKey: ["/api/services/status"],
|
queryKey: ["/api/services/status"],
|
||||||
refetchInterval: 5000, // Refresh every 5s
|
refetchInterval: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mutation for service control
|
|
||||||
const serviceControlMutation = useMutation({
|
const serviceControlMutation = useMutation({
|
||||||
mutationFn: async ({ service, action }: { service: string; action: string }) => {
|
mutationFn: async ({ service, action }: { service: string; action: string }) => {
|
||||||
return apiRequest("POST", `/api/services/${service}/${action}`);
|
return apiRequest("POST", `/api/services/${service}/${action}`);
|
||||||
@ -39,9 +34,8 @@ export default function ServicesPage() {
|
|||||||
onSuccess: (data, variables) => {
|
onSuccess: (data, variables) => {
|
||||||
toast({
|
toast({
|
||||||
title: "Operazione completata",
|
title: "Operazione completata",
|
||||||
description: `Servizio ${variables.service}: ${variables.action} eseguito con successo`,
|
description: `Servizio ${variables.service}: ${variables.action} eseguito`,
|
||||||
});
|
});
|
||||||
// Refresh status after 2 seconds
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["/api/services/status"] });
|
queryClient.invalidateQueries({ queryKey: ["/api/services/status"] });
|
||||||
}, 2000);
|
}, 2000);
|
||||||
@ -59,39 +53,260 @@ export default function ServicesPage() {
|
|||||||
serviceControlMutation.mutate({ service, action });
|
serviceControlMutation.mutate({ service, action });
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusBadge = (service: ServiceStatus) => {
|
const getStatusBadge = (service: ServiceStatus, key: string) => {
|
||||||
if (service.healthy) {
|
if (service.healthy) {
|
||||||
return <Badge variant="default" className="bg-green-600" data-testid={`badge-status-healthy`}>Online</Badge>;
|
return <Badge variant="default" className="bg-green-600" data-testid={`badge-status-${key}-healthy`}>Online</Badge>;
|
||||||
}
|
}
|
||||||
if (service.status === 'idle') {
|
if (service.status === 'idle') {
|
||||||
return <Badge variant="secondary" data-testid={`badge-status-idle`}>In Attesa</Badge>;
|
return <Badge variant="secondary" data-testid={`badge-status-${key}-idle`}>In Attesa</Badge>;
|
||||||
}
|
}
|
||||||
if (service.status === 'offline') {
|
if (service.status === 'offline') {
|
||||||
return <Badge variant="destructive" data-testid={`badge-status-offline`}>Offline</Badge>;
|
return <Badge variant="destructive" data-testid={`badge-status-${key}-offline`}>Offline</Badge>;
|
||||||
}
|
}
|
||||||
if (service.status === 'error') {
|
if (service.status === 'error') {
|
||||||
return <Badge variant="destructive" data-testid={`badge-status-error`}>Errore</Badge>;
|
return <Badge variant="destructive" data-testid={`badge-status-${key}-error`}>Errore</Badge>;
|
||||||
}
|
}
|
||||||
return <Badge variant="outline" data-testid={`badge-status-unknown`}>Sconosciuto</Badge>;
|
return <Badge variant="outline" data-testid={`badge-status-${key}-unknown`}>Sconosciuto</Badge>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusIndicator = (service: ServiceStatus) => {
|
const getStatusIndicator = (service: ServiceStatus) => {
|
||||||
if (service.healthy) {
|
if (service.healthy) {
|
||||||
return <div className="h-3 w-3 rounded-full bg-green-500" />;
|
return <div className="h-3 w-3 rounded-full bg-green-500 shrink-0" />;
|
||||||
}
|
}
|
||||||
if (service.status === 'idle') {
|
if (service.status === 'idle') {
|
||||||
return <div className="h-3 w-3 rounded-full bg-yellow-500" />;
|
return <div className="h-3 w-3 rounded-full bg-yellow-500 shrink-0" />;
|
||||||
}
|
}
|
||||||
return <div className="h-3 w-3 rounded-full bg-red-500" />;
|
return <div className="h-3 w-3 rounded-full bg-red-500 shrink-0" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getServiceIcon = (key: string) => {
|
||||||
|
const icons: Record<string, any> = {
|
||||||
|
nodeBackend: Server,
|
||||||
|
mlBackend: Brain,
|
||||||
|
database: Database,
|
||||||
|
syslogParser: FileText,
|
||||||
|
analyticsAggregator: Activity,
|
||||||
|
autoBlock: Shield,
|
||||||
|
cleanup: Trash2,
|
||||||
|
listFetcher: ListChecks,
|
||||||
|
mlTraining: GraduationCap,
|
||||||
|
};
|
||||||
|
const Icon = icons[key] || Activity;
|
||||||
|
return <Icon className="h-5 w-5" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const controllableServices = [
|
||||||
|
"ids-ml-backend", "ids-syslog-parser", "ids-backend",
|
||||||
|
"ids-analytics-aggregator", "ids-auto-block", "ids-cleanup",
|
||||||
|
"ids-list-fetcher", "ids-ml-training"
|
||||||
|
];
|
||||||
|
|
||||||
|
const getLogCommand = (key: string): string | null => {
|
||||||
|
const logs: Record<string, string> = {
|
||||||
|
nodeBackend: "tail -f /var/log/ids/backend.log",
|
||||||
|
mlBackend: "journalctl -u ids-ml-backend -f",
|
||||||
|
database: "sudo journalctl -u postgresql-16 -f",
|
||||||
|
syslogParser: "tail -f /var/log/ids/syslog_parser.log",
|
||||||
|
analyticsAggregator: "journalctl -u ids-analytics-aggregator -f",
|
||||||
|
autoBlock: "journalctl -u ids-auto-block -f",
|
||||||
|
cleanup: "journalctl -u ids-cleanup -f",
|
||||||
|
listFetcher: "journalctl -u ids-list-fetcher -f",
|
||||||
|
mlTraining: "journalctl -u ids-ml-training -f",
|
||||||
|
};
|
||||||
|
return logs[key] || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderDetailRow = (label: string, value: any, variant?: "default" | "destructive" | "secondary" | "outline") => {
|
||||||
|
if (value === undefined || value === null) return null;
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between gap-2 flex-wrap">
|
||||||
|
<span className="text-sm text-muted-foreground">{label}:</span>
|
||||||
|
{variant ? (
|
||||||
|
<Badge variant={variant} className="text-xs">{String(value)}</Badge>
|
||||||
|
) : (
|
||||||
|
<span className="text-sm font-mono">{String(value)}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderServiceDetails = (key: string, service: ServiceStatus) => {
|
||||||
|
const d = service.details;
|
||||||
|
if (!d) return null;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "nodeBackend":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderDetailRow("Porta", d.port)}
|
||||||
|
{renderDetailRow("Uptime", d.uptime)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "mlBackend":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{d.modelLoaded !== undefined && renderDetailRow("Modello ML", d.modelLoaded ? "Caricato" : "Non Caricato", d.modelLoaded ? "default" : "secondary")}
|
||||||
|
{d.error && renderDetailRow("Errore", d.error, "destructive")}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "database":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{d.connected && renderDetailRow("Connessione", "Attiva", "default")}
|
||||||
|
{d.error && renderDetailRow("Errore", d.error, "destructive")}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "syslogParser":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{d.recentLogs30min !== undefined && renderDetailRow("Log ultimi 30min", d.recentLogs30min.toLocaleString())}
|
||||||
|
{d.lastLog && renderDetailRow("Ultimo log", typeof d.lastLog === 'string' ? d.lastLog : new Date(d.lastLog).toLocaleString('it-IT'))}
|
||||||
|
{d.warning && renderDetailRow("Avviso", d.warning, "destructive")}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "analyticsAggregator":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{d.lastRun && renderDetailRow("Ultima esecuzione", new Date(d.lastRun).toLocaleString('it-IT'))}
|
||||||
|
{d.hoursSinceLastRun && renderDetailRow("Ore dall'ultimo run", d.hoursSinceLastRun + "h", parseFloat(d.hoursSinceLastRun) < 2 ? "default" : "destructive")}
|
||||||
|
{d.warning && renderDetailRow("Avviso", d.warning, "destructive")}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "autoBlock":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderDetailRow("Blocchi ultimi 10min", d.recentBlocks10min)}
|
||||||
|
{renderDetailRow("Totale bloccati", d.totalBlocked)}
|
||||||
|
{d.lastBlock && renderDetailRow("Ultimo blocco", typeof d.lastBlock === 'string' && d.lastBlock !== 'Mai' ? new Date(d.lastBlock).toLocaleString('it-IT') : d.lastBlock)}
|
||||||
|
{renderDetailRow("Intervallo", d.interval)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "cleanup":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderDetailRow("Detection vecchie (>48h)", d.oldDetections48h, d.oldDetections48h > 0 ? "destructive" : "default")}
|
||||||
|
{renderDetailRow("Detection totali", d.totalDetections)}
|
||||||
|
{renderDetailRow("Intervallo", d.interval)}
|
||||||
|
{d.warning && renderDetailRow("Avviso", d.warning, "destructive")}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "listFetcher":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderDetailRow("Liste totali", d.totalLists)}
|
||||||
|
{renderDetailRow("Liste attive", d.enabledLists)}
|
||||||
|
{d.lastFetched && renderDetailRow("Ultimo fetch", typeof d.lastFetched === 'string' && d.lastFetched !== 'Mai' ? new Date(d.lastFetched).toLocaleString('it-IT') : d.lastFetched)}
|
||||||
|
{d.hoursSinceLastFetch && renderDetailRow("Ore dall'ultimo fetch", d.hoursSinceLastFetch + "h", parseFloat(d.hoursSinceLastFetch) < 1 ? "default" : "destructive")}
|
||||||
|
{renderDetailRow("Intervallo", d.interval)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "mlTraining":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{d.lastTraining && renderDetailRow("Ultimo training", typeof d.lastTraining === 'string' && d.lastTraining !== 'Mai' ? new Date(d.lastTraining).toLocaleString('it-IT') : d.lastTraining)}
|
||||||
|
{d.daysSinceLastTraining && renderDetailRow("Giorni dall'ultimo", d.daysSinceLastTraining, parseFloat(d.daysSinceLastTraining) < 8 ? "default" : "destructive")}
|
||||||
|
{d.lastStatus && renderDetailRow("Stato ultimo training", d.lastStatus, d.lastStatus === 'completed' ? "default" : "destructive")}
|
||||||
|
{d.lastModel && renderDetailRow("Modello", d.lastModel)}
|
||||||
|
{d.recordsProcessed && renderDetailRow("Record processati", d.recordsProcessed.toLocaleString())}
|
||||||
|
{renderDetailRow("Intervallo", d.interval)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return d.error ? renderDetailRow("Errore", d.error, "destructive") : null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const coreServices = ["nodeBackend", "mlBackend", "database", "syslogParser"];
|
||||||
|
const timerServices = ["autoBlock", "analyticsAggregator", "cleanup", "listFetcher", "mlTraining"];
|
||||||
|
|
||||||
|
const renderServiceCard = (key: string, service: ServiceStatus) => {
|
||||||
|
const isControllable = controllableServices.includes(service.systemdUnit);
|
||||||
|
const isTimer = service.type === "timer";
|
||||||
|
const logCmd = getLogCommand(key);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card key={key} data-testid={`card-service-${key}`}>
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
|
||||||
|
<CardTitle className="flex items-center gap-2 text-base">
|
||||||
|
{getServiceIcon(key)}
|
||||||
|
<span className="truncate">{service.name}</span>
|
||||||
|
</CardTitle>
|
||||||
|
<div className="flex items-center gap-2 shrink-0">
|
||||||
|
{isTimer && <Timer className="h-4 w-4 text-muted-foreground" />}
|
||||||
|
{getStatusIndicator(service)}
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-3">
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
<span className="text-sm text-muted-foreground">Stato:</span>
|
||||||
|
{getStatusBadge(service, key)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
<span className="text-sm text-muted-foreground">Systemd:</span>
|
||||||
|
<Badge variant="outline" className="text-xs font-mono">
|
||||||
|
{service.systemdUnit}{isTimer ? '.timer' : '.service'}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{renderServiceDetails(key, service)}
|
||||||
|
|
||||||
|
{isControllable && (
|
||||||
|
<div className="space-y-2 pt-2 border-t">
|
||||||
|
<p className="text-xs font-medium text-muted-foreground">Controlli:</p>
|
||||||
|
<div className="flex gap-2 flex-wrap">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => handleServiceAction(service.systemdUnit, "restart")}
|
||||||
|
disabled={serviceControlMutation.isPending}
|
||||||
|
data-testid={`button-restart-${key}`}
|
||||||
|
>
|
||||||
|
<RotateCw className="h-3 w-3 mr-1" />
|
||||||
|
Restart
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => handleServiceAction(service.systemdUnit, "start")}
|
||||||
|
disabled={serviceControlMutation.isPending || service.status === 'running'}
|
||||||
|
data-testid={`button-start-${key}`}
|
||||||
|
>
|
||||||
|
<Play className="h-3 w-3 mr-1" />
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => handleServiceAction(service.systemdUnit, "stop")}
|
||||||
|
disabled={serviceControlMutation.isPending || service.status === 'offline'}
|
||||||
|
data-testid={`button-stop-${key}`}
|
||||||
|
>
|
||||||
|
<Square className="h-3 w-3 mr-1" />
|
||||||
|
Stop
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{logCmd && (
|
||||||
|
<div className="p-2 bg-muted rounded-md">
|
||||||
|
<p className="text-xs font-medium text-muted-foreground mb-1">Log:</p>
|
||||||
|
<code className="text-xs font-mono break-all" data-testid={`code-log-${key}`}>{logCmd}</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 p-6" data-testid="page-services">
|
<div className="flex flex-col gap-6 p-6" data-testid="page-services">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-semibold" data-testid="text-services-title">Gestione Servizi</h1>
|
<h1 className="text-3xl font-semibold" data-testid="text-services-title">Gestione Servizi</h1>
|
||||||
<p className="text-muted-foreground" data-testid="text-services-subtitle">
|
<p className="text-muted-foreground" data-testid="text-services-subtitle">
|
||||||
Monitoraggio e controllo dei servizi IDS
|
Monitoraggio e controllo di tutti i servizi IDS
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={() => refetch()} variant="outline" data-testid="button-refresh">
|
<Button onClick={() => refetch()} variant="outline" data-testid="button-refresh">
|
||||||
@ -100,303 +315,40 @@ export default function ServicesPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Alert data-testid="alert-server-instructions">
|
{isLoading && (
|
||||||
<AlertCircle className="h-4 w-4" />
|
<div className="text-center py-8 text-muted-foreground">Caricamento stato servizi...</div>
|
||||||
<AlertTitle>Gestione Servizi Systemd</AlertTitle>
|
)}
|
||||||
<AlertDescription>
|
|
||||||
I servizi IDS sono gestiti da systemd sul server AlmaLinux.
|
|
||||||
Usa i pulsanti qui sotto per controllarli oppure esegui i comandi systemctl direttamente sul server.
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
|
|
||||||
{/* Services Grid */}
|
{servicesStatus && (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<>
|
||||||
{/* ML Backend Service */}
|
<div>
|
||||||
<Card data-testid="card-ml-backend-service">
|
<h2 className="text-lg font-semibold mb-3 flex items-center gap-2">
|
||||||
<CardHeader>
|
<Server className="h-5 w-5" />
|
||||||
<CardTitle className="flex items-center gap-2 text-lg">
|
Servizi Core
|
||||||
<Brain className="h-5 w-5" />
|
</h2>
|
||||||
ML Backend Python
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
||||||
{servicesStatus && getStatusIndicator(servicesStatus.services.mlBackend)}
|
{coreServices.map((key) => {
|
||||||
</CardTitle>
|
const service = (servicesStatus.services as any)[key];
|
||||||
</CardHeader>
|
return service ? renderServiceCard(key, service) : null;
|
||||||
<CardContent className="space-y-4">
|
})}
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">Stato:</span>
|
|
||||||
{servicesStatus && getStatusBadge(servicesStatus.services.mlBackend)}
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{servicesStatus?.services.mlBackend.details?.modelLoaded !== undefined && (
|
<div>
|
||||||
<div className="flex items-center justify-between">
|
<h2 className="text-lg font-semibold mb-3 flex items-center gap-2">
|
||||||
<span className="text-sm text-muted-foreground">Modello ML:</span>
|
<Clock className="h-5 w-5" />
|
||||||
<Badge variant={servicesStatus.services.mlBackend.details.modelLoaded ? "default" : "secondary"}>
|
Timer Systemd (Attivita Periodiche)
|
||||||
{servicesStatus.services.mlBackend.details.modelLoaded ? "Caricato" : "Non Caricato"}
|
</h2>
|
||||||
</Badge>
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||||
</div>
|
{timerServices.map((key) => {
|
||||||
)}
|
const service = (servicesStatus.services as any)[key];
|
||||||
|
return service ? renderServiceCard(key, service) : null;
|
||||||
{/* Service Controls */}
|
})}
|
||||||
<div className="mt-4 space-y-2">
|
|
||||||
<p className="text-xs font-medium mb-2">Controlli Servizio:</p>
|
|
||||||
<div className="flex gap-2 flex-wrap">
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleServiceAction("ids-ml-backend", "start")}
|
|
||||||
disabled={serviceControlMutation.isPending || servicesStatus?.services.mlBackend.status === 'running'}
|
|
||||||
data-testid="button-start-ml"
|
|
||||||
>
|
|
||||||
<Play className="h-3 w-3 mr-1" />
|
|
||||||
Start
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleServiceAction("ids-ml-backend", "stop")}
|
|
||||||
disabled={serviceControlMutation.isPending || servicesStatus?.services.mlBackend.status === 'offline'}
|
|
||||||
data-testid="button-stop-ml"
|
|
||||||
>
|
|
||||||
<Square className="h-3 w-3 mr-1" />
|
|
||||||
Stop
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleServiceAction("ids-ml-backend", "restart")}
|
|
||||||
disabled={serviceControlMutation.isPending}
|
|
||||||
data-testid="button-restart-ml"
|
|
||||||
>
|
|
||||||
<RotateCw className="h-3 w-3 mr-1" />
|
|
||||||
Restart
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Manual Commands (fallback) */}
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Comando systemctl (sul server):</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-systemctl-ml">
|
|
||||||
sudo systemctl {servicesStatus?.services.mlBackend.status === 'offline' ? 'start' : 'restart'} ids-ml-backend
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Log:</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-log-ml">
|
|
||||||
tail -f /var/log/ids/backend.log
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Database Service */}
|
|
||||||
<Card data-testid="card-database-service">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2 text-lg">
|
|
||||||
<Database className="h-5 w-5" />
|
|
||||||
PostgreSQL Database
|
|
||||||
{servicesStatus && getStatusIndicator(servicesStatus.services.database)}
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">Stato:</span>
|
|
||||||
{servicesStatus && getStatusBadge(servicesStatus.services.database)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{servicesStatus?.services.database.status === 'running' && (
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">Connessione:</span>
|
|
||||||
<Badge variant="default" className="bg-green-600">Connesso</Badge>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Verifica status:</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-status-db">
|
|
||||||
systemctl status postgresql-16
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{servicesStatus?.services.database.status === 'error' && (
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Riavvia database:</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-restart-db">
|
|
||||||
sudo systemctl restart postgresql-16
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Log:</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-log-db">
|
|
||||||
sudo journalctl -u postgresql-16 -f
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Syslog Parser Service */}
|
|
||||||
<Card data-testid="card-syslog-parser-service">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2 text-lg">
|
|
||||||
<FileText className="h-5 w-5" />
|
|
||||||
Syslog Parser
|
|
||||||
{servicesStatus && getStatusIndicator(servicesStatus.services.syslogParser)}
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">Stato:</span>
|
|
||||||
{servicesStatus && getStatusBadge(servicesStatus.services.syslogParser)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{servicesStatus?.services.syslogParser.details?.pid && (
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">PID Processo:</span>
|
|
||||||
<Badge variant="outline" className="font-mono">
|
|
||||||
{servicesStatus.services.syslogParser.details.pid}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{servicesStatus?.services.syslogParser.details?.systemd_unit && (
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">Systemd Unit:</span>
|
|
||||||
<Badge variant="outline" className="font-mono text-xs">
|
|
||||||
{servicesStatus.services.syslogParser.details.systemd_unit}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Service Controls */}
|
|
||||||
<div className="mt-4 space-y-2">
|
|
||||||
<p className="text-xs font-medium mb-2">Controlli Servizio:</p>
|
|
||||||
<div className="flex gap-2 flex-wrap">
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleServiceAction("ids-syslog-parser", "start")}
|
|
||||||
disabled={serviceControlMutation.isPending || servicesStatus?.services.syslogParser.status === 'running'}
|
|
||||||
data-testid="button-start-parser"
|
|
||||||
>
|
|
||||||
<Play className="h-3 w-3 mr-1" />
|
|
||||||
Start
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleServiceAction("ids-syslog-parser", "stop")}
|
|
||||||
disabled={serviceControlMutation.isPending || servicesStatus?.services.syslogParser.status === 'offline'}
|
|
||||||
data-testid="button-stop-parser"
|
|
||||||
>
|
|
||||||
<Square className="h-3 w-3 mr-1" />
|
|
||||||
Stop
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleServiceAction("ids-syslog-parser", "restart")}
|
|
||||||
disabled={serviceControlMutation.isPending}
|
|
||||||
data-testid="button-restart-parser"
|
|
||||||
>
|
|
||||||
<RotateCw className="h-3 w-3 mr-1" />
|
|
||||||
Restart
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Manual Commands (fallback) */}
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Comando systemctl (sul server):</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-systemctl-parser">
|
|
||||||
sudo systemctl {servicesStatus?.services.syslogParser.status === 'offline' ? 'start' : 'restart'} ids-syslog-parser
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Log:</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-log-parser">
|
|
||||||
tail -f /var/log/ids/syslog_parser.log
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Analytics Aggregator Service */}
|
|
||||||
<Card data-testid="card-analytics-aggregator-service">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2 text-lg">
|
|
||||||
<Activity className="h-5 w-5" />
|
|
||||||
Analytics Aggregator
|
|
||||||
{servicesStatus && getStatusIndicator(servicesStatus.services.analyticsAggregator)}
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">Stato:</span>
|
|
||||||
{servicesStatus && getStatusBadge(servicesStatus.services.analyticsAggregator)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{servicesStatus?.services.analyticsAggregator.details?.lastRun && (
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">Ultima Aggregazione:</span>
|
|
||||||
<Badge variant="outline" className="text-xs">
|
|
||||||
{new Date(servicesStatus.services.analyticsAggregator.details.lastRun).toLocaleString('it-IT')}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{servicesStatus?.services.analyticsAggregator.details?.hoursSinceLastRun && (
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm text-muted-foreground">Ore dall'ultimo run:</span>
|
|
||||||
<Badge variant={parseFloat(servicesStatus.services.analyticsAggregator.details.hoursSinceLastRun) < 2 ? "default" : "destructive"}>
|
|
||||||
{servicesStatus.services.analyticsAggregator.details.hoursSinceLastRun}h
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* CRITICAL ALERT: Aggregator idle for too long */}
|
|
||||||
{servicesStatus?.services.analyticsAggregator.details?.hoursSinceLastRun &&
|
|
||||||
parseFloat(servicesStatus.services.analyticsAggregator.details.hoursSinceLastRun) > 2 && (
|
|
||||||
<Alert variant="destructive" className="mt-2" data-testid="alert-aggregator-idle">
|
|
||||||
<AlertCircle className="h-4 w-4" />
|
|
||||||
<AlertTitle className="text-sm font-semibold">⚠️ Timer Systemd Non Attivo</AlertTitle>
|
|
||||||
<AlertDescription className="text-xs mt-1">
|
|
||||||
<p className="mb-2">L'aggregatore non esegue da {servicesStatus.services.analyticsAggregator.details.hoursSinceLastRun}h! Dashboard e Analytics bloccati.</p>
|
|
||||||
<p className="font-semibold">Soluzione Immediata (sul server):</p>
|
|
||||||
<code className="block bg-destructive-foreground/10 p-2 rounded mt-1 font-mono text-xs">
|
|
||||||
sudo /opt/ids/deployment/setup_analytics_timer.sh
|
|
||||||
</code>
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Verifica timer:</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-status-aggregator">
|
|
||||||
systemctl status ids-analytics-aggregator.timer
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Avvia aggregazione manualmente:</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-run-aggregator">
|
|
||||||
cd /opt/ids && ./deployment/run_analytics.sh
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-4 p-3 bg-muted rounded-lg">
|
|
||||||
<p className="text-xs font-medium mb-2">Log:</p>
|
|
||||||
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-log-aggregator">
|
|
||||||
journalctl -u ids-analytics-aggregator.timer -f
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Additional Commands */}
|
|
||||||
<Card data-testid="card-additional-commands">
|
<Card data-testid="card-additional-commands">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
@ -406,30 +358,27 @@ export default function ServicesPage() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium mb-2">Verifica tutti i processi IDS attivi:</p>
|
<p className="text-sm font-medium mb-2">Stato di tutti i servizi IDS:</p>
|
||||||
<code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-check-processes">
|
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-all-services">
|
||||||
ps aux | grep -E "python.*(main|syslog_parser)" | grep -v grep
|
systemctl list-units 'ids-*' --all
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium mb-2">Stato di tutti i timer IDS:</p>
|
||||||
|
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-all-timers">
|
||||||
|
systemctl list-timers 'ids-*' --all
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium mb-2">Verifica log RSyslog (ricezione log MikroTik):</p>
|
<p className="text-sm font-medium mb-2">Verifica log RSyslog (ricezione log MikroTik):</p>
|
||||||
<code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-check-rsyslog">
|
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-check-rsyslog">
|
||||||
tail -f /var/log/mikrotik/raw.log
|
tail -f /var/log/mikrotik/raw.log
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium mb-2">Esegui training manuale ML:</p>
|
<p className="text-sm font-medium mb-2">Verifica processi IDS attivi:</p>
|
||||||
<code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-manual-training">
|
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-check-processes">
|
||||||
curl -X POST http://localhost:8000/train -H "Content-Type: application/json" -d '{"max_records": 10000, "hours_back": 24}'
|
ps aux | grep -E "python.*(main|syslog_parser)" | grep -v grep
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium mb-2">Verifica storico training nel database:</p>
|
|
||||||
<code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-check-training">
|
|
||||||
psql $DATABASE_URL -c "SELECT * FROM training_history ORDER BY trained_at DESC LIMIT 5;"
|
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@ -33,10 +33,13 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
|
||||||
interface MLStatsResponse {
|
interface MLStatsResponse {
|
||||||
|
source?: string;
|
||||||
|
ml_backend_status?: string;
|
||||||
logs?: { total: number; last_hour: number };
|
logs?: { total: number; last_hour: number };
|
||||||
detections?: { total: number; blocked: number };
|
detections?: { total: number; blocked: number; critical?: number; unique_ips?: number };
|
||||||
routers?: { active: number };
|
routers?: { active: number };
|
||||||
latest_training?: any;
|
latest_training?: any;
|
||||||
|
logs_24h?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const trainFormSchema = z.object({
|
const trainFormSchema = z.object({
|
||||||
@ -147,21 +150,43 @@ export default function TrainingPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* ML Backend Status Warning */}
|
||||||
|
{mlStats?.ml_backend_status === "offline" && (
|
||||||
|
<Card className="border-orange-300 dark:border-orange-700" data-testid="card-ml-offline-warning">
|
||||||
|
<CardContent className="flex items-center gap-3 py-3">
|
||||||
|
<XCircle className="h-5 w-5 text-orange-500 shrink-0" />
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-sm">ML Backend Python offline</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Le statistiche mostrate provengono dal database. Training e detection manuali non sono disponibili fino al riavvio del servizio.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* ML Stats */}
|
{/* ML Stats */}
|
||||||
{mlStats && (
|
{mlStats && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<Card data-testid="card-ml-logs">
|
<Card data-testid="card-ml-logs">
|
||||||
<CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
|
||||||
<CardTitle className="text-sm font-medium">Log Totali</CardTitle>
|
<CardTitle className="text-sm font-medium">
|
||||||
|
{mlStats.source === "database_fallback" ? "Log Ultime 24h" : "Log Totali"}
|
||||||
|
</CardTitle>
|
||||||
<Brain className="h-4 w-4 text-muted-foreground" />
|
<Brain className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-semibold" data-testid="text-ml-logs-total">
|
<div className="text-2xl font-semibold" data-testid="text-ml-logs-total">
|
||||||
{mlStats.logs?.total?.toLocaleString() || 0}
|
{(mlStats.source === "database_fallback"
|
||||||
|
? mlStats.logs_24h
|
||||||
|
: mlStats.logs?.total
|
||||||
|
)?.toLocaleString() || 0}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
{mlStats.source !== "database_fallback" && (
|
||||||
Ultima ora: {mlStats.logs?.last_hour?.toLocaleString() || 0}
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
</p>
|
Ultima ora: {mlStats.logs?.last_hour?.toLocaleString() || 0}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@ -172,22 +197,30 @@ export default function TrainingPage() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-semibold" data-testid="text-ml-detections-total">
|
<div className="text-2xl font-semibold" data-testid="text-ml-detections-total">
|
||||||
{mlStats.detections?.total || 0}
|
{mlStats.detections?.total?.toLocaleString() || 0}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
Bloccati: {mlStats.detections?.blocked || 0}
|
Bloccati: {mlStats.detections?.blocked?.toLocaleString() || 0}
|
||||||
|
{mlStats.detections?.critical !== undefined && (
|
||||||
|
<span> | Critici: {mlStats.detections.critical.toLocaleString()}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card data-testid="card-ml-routers">
|
<Card data-testid="card-ml-routers">
|
||||||
<CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
|
||||||
<CardTitle className="text-sm font-medium">Router Attivi</CardTitle>
|
<CardTitle className="text-sm font-medium">
|
||||||
|
{mlStats.source === "database_fallback" ? "IP Unici" : "Router Attivi"}
|
||||||
|
</CardTitle>
|
||||||
<TrendingUp className="h-4 w-4 text-muted-foreground" />
|
<TrendingUp className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-semibold" data-testid="text-ml-routers-active">
|
<div className="text-2xl font-semibold" data-testid="text-ml-routers-active">
|
||||||
{mlStats.routers?.active || 0}
|
{(mlStats.source === "database_fallback"
|
||||||
|
? mlStats.detections?.unique_ips
|
||||||
|
: mlStats.routers?.active
|
||||||
|
)?.toLocaleString() || 0}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@ -214,9 +247,9 @@ export default function TrainingPage() {
|
|||||||
</p>
|
</p>
|
||||||
<Dialog open={isTrainDialogOpen} onOpenChange={setIsTrainDialogOpen}>
|
<Dialog open={isTrainDialogOpen} onOpenChange={setIsTrainDialogOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button className="w-full" data-testid="button-start-training">
|
<Button className="w-full" disabled={mlStats?.ml_backend_status === "offline"} data-testid="button-start-training">
|
||||||
<Play className="h-4 w-4 mr-2" />
|
<Play className="h-4 w-4 mr-2" />
|
||||||
Avvia Training
|
{mlStats?.ml_backend_status === "offline" ? "ML Backend Offline" : "Avvia Training"}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent data-testid="dialog-training">
|
<DialogContent data-testid="dialog-training">
|
||||||
@ -265,7 +298,7 @@ export default function TrainingPage() {
|
|||||||
>
|
>
|
||||||
Annulla
|
Annulla
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" disabled={trainMutation.isPending} data-testid="button-confirm-training">
|
<Button type="submit" disabled={trainMutation.isPending || mlStats?.ml_backend_status === "offline"} data-testid="button-confirm-training">
|
||||||
{trainMutation.isPending ? "Avvio..." : "Avvia Training"}
|
{trainMutation.isPending ? "Avvio..." : "Avvia Training"}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
@ -294,9 +327,9 @@ export default function TrainingPage() {
|
|||||||
</p>
|
</p>
|
||||||
<Dialog open={isDetectDialogOpen} onOpenChange={setIsDetectDialogOpen}>
|
<Dialog open={isDetectDialogOpen} onOpenChange={setIsDetectDialogOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="secondary" className="w-full" data-testid="button-start-detection">
|
<Button variant="secondary" className="w-full" disabled={mlStats?.ml_backend_status === "offline"} data-testid="button-start-detection">
|
||||||
<Search className="h-4 w-4 mr-2" />
|
<Search className="h-4 w-4 mr-2" />
|
||||||
Avvia Detection
|
{mlStats?.ml_backend_status === "offline" ? "ML Backend Offline" : "Avvia Detection"}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent data-testid="dialog-detection">
|
<DialogContent data-testid="dialog-detection">
|
||||||
@ -377,7 +410,7 @@ export default function TrainingPage() {
|
|||||||
>
|
>
|
||||||
Annulla
|
Annulla
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" disabled={detectMutation.isPending} data-testid="button-confirm-detection">
|
<Button type="submit" disabled={detectMutation.isPending || mlStats?.ml_backend_status === "offline"} data-testid="button-confirm-detection">
|
||||||
{detectMutation.isPending ? "Avvio..." : "Avvia Detection"}
|
{detectMutation.isPending ? "Avvio..." : "Avvia Detection"}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
-- PostgreSQL database dump
|
-- PostgreSQL database dump
|
||||||
--
|
--
|
||||||
|
|
||||||
\restrict em7gYgifRwixbLZU8Owl6B7Kqa069IOhR6jKrqfBAnZ0Vp3qeadEaTcm0QvNCf8
|
\restrict QQPZgpukcxzRMKOdS5xNsXDiphiHLW5uAuhQxN7luRJ2u8BkVkDOz1h9Un2BrJ0
|
||||||
|
|
||||||
-- Dumped from database version 16.11 (df20cf9)
|
-- Dumped from database version 16.11 (df20cf9)
|
||||||
-- Dumped by pg_dump version 16.10
|
-- Dumped by pg_dump version 16.10
|
||||||
@ -387,5 +387,5 @@ ALTER TABLE ONLY public.public_blacklist_ips
|
|||||||
-- PostgreSQL database dump complete
|
-- PostgreSQL database dump complete
|
||||||
--
|
--
|
||||||
|
|
||||||
\unrestrict em7gYgifRwixbLZU8Owl6B7Kqa069IOhR6jKrqfBAnZ0Vp3qeadEaTcm0QvNCf8
|
\unrestrict QQPZgpukcxzRMKOdS5xNsXDiphiHLW5uAuhQxN7luRJ2u8BkVkDOz1h9Un2BrJ0
|
||||||
|
|
||||||
|
|||||||
@ -1,34 +1,39 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# CHECK BACKEND - Verifica e riavvia backend Python se necessario
|
# CHECK BACKEND - Verifica e riavvia backend Python se necessario
|
||||||
|
# Usa systemctl per gestire il servizio (con virtual environment)
|
||||||
|
# Nota: questo script può girare come root o come user ids
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
PROCESS_NAME="python3.11 python_ml/main.py"
|
|
||||||
PID_FILE="/var/log/ids/backend.pid"
|
|
||||||
LOG_FILE="/var/log/ids/backend.log"
|
LOG_FILE="/var/log/ids/backend.log"
|
||||||
WORK_DIR="/opt/ids"
|
|
||||||
|
|
||||||
mkdir -p /var/log/ids
|
mkdir -p /var/log/ids
|
||||||
|
|
||||||
# Check if backend is running
|
# Check if systemd service is active
|
||||||
if pgrep -f "$PROCESS_NAME" > /dev/null; then
|
if systemctl is-active --quiet ids-ml-backend; then
|
||||||
# Backend running, update PID
|
|
||||||
pgrep -f "$PROCESS_NAME" > "$PID_FILE"
|
|
||||||
exit 0
|
exit 0
|
||||||
else
|
fi
|
||||||
echo "[$(date)] Backend Python NON attivo, riavvio..." >> "$LOG_FILE"
|
|
||||||
|
# Verifica anche se il processo Python è attivo (fallback)
|
||||||
# Kill any orphaned Python processes
|
if pgrep -f "python.*main.py" > /dev/null; then
|
||||||
pkill -f "python_ml/main.py" 2>/dev/null
|
exit 0
|
||||||
|
fi
|
||||||
# Wait a moment
|
|
||||||
sleep 2
|
echo "[$(date)] Backend Python NON attivo, riavvio..." >> "$LOG_FILE"
|
||||||
|
|
||||||
# Start backend
|
# Prova prima con systemctl (funziona se eseguito come root o con sudo configurato)
|
||||||
cd "$WORK_DIR/python_ml"
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
nohup /usr/bin/python3.11 main.py >> "$LOG_FILE" 2>&1 &
|
systemctl restart ids-ml-backend
|
||||||
NEW_PID=$!
|
else
|
||||||
echo $NEW_PID > "$PID_FILE"
|
# Se non siamo root, prova con sudo (richiede sudoers configurato)
|
||||||
|
sudo systemctl restart ids-ml-backend 2>/dev/null
|
||||||
echo "[$(date)] Backend riavviato con PID: $NEW_PID" >> "$LOG_FILE"
|
fi
|
||||||
|
|
||||||
|
# Wait for startup
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
if systemctl is-active --quiet ids-ml-backend || pgrep -f "python.*main.py" > /dev/null; then
|
||||||
|
echo "[$(date)] Backend riavviato con successo" >> "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "[$(date)] ERRORE: Backend non si è avviato. Controlla: journalctl -u ids-ml-backend" >> "$LOG_FILE"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -1,41 +1,23 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# CHECK FRONTEND - Verifica e riavvia frontend Node.js se necessario
|
# CHECK FRONTEND - Verifica se backend Node.js e' attivo
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
PROCESS_NAME="npm run dev"
|
LOG_FILE="/var/log/ids/backend.log"
|
||||||
PID_FILE="/var/log/ids/frontend.pid"
|
|
||||||
LOG_FILE="/var/log/ids/frontend.log"
|
|
||||||
WORK_DIR="/opt/ids"
|
|
||||||
|
|
||||||
mkdir -p /var/log/ids
|
mkdir -p /var/log/ids
|
||||||
|
|
||||||
# Check if frontend is running
|
if systemctl is-active --quiet ids-backend.service 2>/dev/null; then
|
||||||
if pgrep -f "vite" > /dev/null; then
|
|
||||||
# Frontend running, update PID
|
|
||||||
pgrep -f "vite" > "$PID_FILE"
|
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
echo "[$(date)] Frontend Node NON attivo, riavvio..." >> "$LOG_FILE"
|
echo "[$(date)] Backend Node.js NON attivo" >> "$LOG_FILE"
|
||||||
|
systemctl start ids-backend.service 2>> "$LOG_FILE" || true
|
||||||
# Kill any orphaned Node processes
|
|
||||||
pkill -f "vite" 2>/dev/null
|
sleep 3
|
||||||
pkill -f "npm run dev" 2>/dev/null
|
|
||||||
|
if systemctl is-active --quiet ids-backend.service 2>/dev/null; then
|
||||||
# Wait a moment
|
echo "[$(date)] Backend riavviato con successo via systemd" >> "$LOG_FILE"
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Start frontend with environment variables from .env
|
|
||||||
cd "$WORK_DIR"
|
|
||||||
if [ -f "$WORK_DIR/.env" ]; then
|
|
||||||
# Load .env and start npm with those variables
|
|
||||||
nohup env $(cat "$WORK_DIR/.env" | grep -v '^#' | xargs) npm run dev >> "$LOG_FILE" 2>&1 &
|
|
||||||
else
|
else
|
||||||
# Fallback: start without .env (will use system env vars)
|
echo "[$(date)] ERRORE: Backend non si e' avviato - verificare con: journalctl -u ids-backend -n 20" >> "$LOG_FILE"
|
||||||
nohup npm run dev >> "$LOG_FILE" 2>&1 &
|
|
||||||
fi
|
fi
|
||||||
NEW_PID=$!
|
|
||||||
echo $NEW_PID > "$PID_FILE"
|
|
||||||
|
|
||||||
echo "[$(date)] Frontend riavviato con PID: $NEW_PID" >> "$LOG_FILE"
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -18,43 +18,49 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "📋 Installing systemd service files..."
|
echo "Installing systemd service files..."
|
||||||
|
|
||||||
# Copy service files
|
# Copy service files
|
||||||
|
cp "$PROJECT_ROOT/deployment/systemd/ids-backend.service" /etc/systemd/system/
|
||||||
cp "$PROJECT_ROOT/deployment/systemd/ids-ml-backend.service" /etc/systemd/system/
|
cp "$PROJECT_ROOT/deployment/systemd/ids-ml-backend.service" /etc/systemd/system/
|
||||||
cp "$PROJECT_ROOT/deployment/systemd/ids-syslog-parser.service" /etc/systemd/system/
|
cp "$PROJECT_ROOT/deployment/systemd/ids-syslog-parser.service" /etc/systemd/system/
|
||||||
|
cp "$PROJECT_ROOT/deployment/systemd/ids-auto-block.service" /etc/systemd/system/
|
||||||
|
|
||||||
# Ensure correct permissions
|
# Ensure correct permissions
|
||||||
|
chmod 644 /etc/systemd/system/ids-backend.service
|
||||||
chmod 644 /etc/systemd/system/ids-ml-backend.service
|
chmod 644 /etc/systemd/system/ids-ml-backend.service
|
||||||
chmod 644 /etc/systemd/system/ids-syslog-parser.service
|
chmod 644 /etc/systemd/system/ids-syslog-parser.service
|
||||||
|
chmod 644 /etc/systemd/system/ids-auto-block.service
|
||||||
|
|
||||||
echo "✅ Service files copied to /etc/systemd/system/"
|
echo "Service files copied to /etc/systemd/system/"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🔄 Reloading systemd daemon..."
|
echo "Reloading systemd daemon..."
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🔧 Enabling services to start on boot..."
|
echo "Enabling services to start on boot..."
|
||||||
|
systemctl enable ids-backend.service
|
||||||
systemctl enable ids-ml-backend.service
|
systemctl enable ids-ml-backend.service
|
||||||
systemctl enable ids-syslog-parser.service
|
systemctl enable ids-syslog-parser.service
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "========================================="
|
echo "========================================="
|
||||||
echo "✅ Installation Complete!"
|
echo "Installation Complete!"
|
||||||
echo "========================================="
|
echo "========================================="
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next steps:"
|
echo "Next steps:"
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. Start the services:"
|
echo "1. Start the services:"
|
||||||
|
echo " sudo systemctl start ids-backend"
|
||||||
echo " sudo systemctl start ids-ml-backend"
|
echo " sudo systemctl start ids-ml-backend"
|
||||||
echo " sudo systemctl start ids-syslog-parser"
|
echo " sudo systemctl start ids-syslog-parser"
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Check status:"
|
echo "2. Check status:"
|
||||||
echo " sudo systemctl status ids-ml-backend"
|
echo " sudo systemctl status ids-backend ids-ml-backend ids-syslog-parser"
|
||||||
echo " sudo systemctl status ids-syslog-parser"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. View logs:"
|
echo "3. View logs:"
|
||||||
|
echo " tail -f /var/log/ids/backend.log"
|
||||||
echo " tail -f /var/log/ids/ml_backend.log"
|
echo " tail -f /var/log/ids/ml_backend.log"
|
||||||
echo " tail -f /var/log/ids/syslog_parser.log"
|
echo " tail -f /var/log/ids/syslog_parser.log"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# RESTART ALL - Riavvio completo sistema IDS
|
# RESTART ALL - Riavvio completo sistema IDS
|
||||||
|
# Usa systemctl per ML Backend, processo diretto per frontend
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
LOG_FILE="/var/log/ids/cron.log"
|
LOG_FILE="/var/log/ids/cron.log"
|
||||||
|
|
||||||
echo "$(date): === RESTART SETTIMANALE SISTEMA IDS ===" >> "$LOG_FILE"
|
echo "$(date): === RESTART SETTIMANALE SISTEMA IDS ===" >> "$LOG_FILE"
|
||||||
|
|
||||||
# Stop all services
|
# Stop ML Backend via systemctl
|
||||||
echo "$(date): Arresto servizi..." >> "$LOG_FILE"
|
echo "$(date): Arresto servizi..." >> "$LOG_FILE"
|
||||||
pkill -f "python_ml/main.py"
|
systemctl stop ids-ml-backend 2>/dev/null
|
||||||
pkill -f "vite"
|
|
||||||
pkill -f "npm run dev"
|
# Stop frontend processes
|
||||||
|
pkill -f "vite" 2>/dev/null
|
||||||
|
pkill -f "npm run dev" 2>/dev/null
|
||||||
|
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
@ -20,10 +23,26 @@ echo "$(date): Pulizia file temporanei..." >> "$LOG_FILE"
|
|||||||
rm -f /var/log/ids/*.pid
|
rm -f /var/log/ids/*.pid
|
||||||
find /tmp -name "ids_*" -mtime +7 -delete 2>/dev/null
|
find /tmp -name "ids_*" -mtime +7 -delete 2>/dev/null
|
||||||
|
|
||||||
# Restart services
|
# Restart ML Backend via systemctl
|
||||||
echo "$(date): Riavvio servizi..." >> "$LOG_FILE"
|
echo "$(date): Riavvio servizi..." >> "$LOG_FILE"
|
||||||
/opt/ids/deployment/check_backend.sh >> "$LOG_FILE" 2>&1
|
systemctl start ids-ml-backend
|
||||||
sleep 3
|
sleep 3
|
||||||
|
|
||||||
|
# Restart frontend via check script
|
||||||
/opt/ids/deployment/check_frontend.sh >> "$LOG_FILE" 2>&1
|
/opt/ids/deployment/check_frontend.sh >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
# Verify ML Backend
|
||||||
|
if systemctl is-active --quiet ids-ml-backend; then
|
||||||
|
echo "$(date): ML Backend avviato con successo" >> "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "$(date): ERRORE: ML Backend non si è avviato" >> "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify Frontend
|
||||||
|
if pgrep -f "vite" > /dev/null; then
|
||||||
|
echo "$(date): Frontend avviato con successo" >> "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "$(date): ERRORE: Frontend non si è avviato" >> "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "$(date): Restart completato!" >> "$LOG_FILE"
|
echo "$(date): Restart completato!" >> "$LOG_FILE"
|
||||||
|
|||||||
@ -1,58 +1,56 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Restart IDS Frontend (Node.js/Express/Vite)
|
# Restart IDS Frontend (Node.js/Express)
|
||||||
# Utility per restart manuale del server frontend
|
# Utility per restart manuale del server frontend via systemd
|
||||||
#
|
#
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "🔄 Restart Frontend Node.js..."
|
echo "Restart Backend Node.js via systemd..."
|
||||||
|
|
||||||
# Kill AGGRESSIVO di tutti i processi Node/Vite
|
# Stop servizio
|
||||||
echo "⏸️ Stopping all Node/Vite processes..."
|
echo "Stopping ids-backend..."
|
||||||
pkill -9 -f "node.*tsx" 2>/dev/null || true
|
sudo systemctl stop ids-backend.service 2>/dev/null || true
|
||||||
pkill -9 -f "vite" 2>/dev/null || true
|
|
||||||
pkill -9 -f "npm run dev" 2>/dev/null || true
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
# Kill processo sulla porta 5000 (se esiste)
|
# Kill eventuali processi orfani sulla porta 5000
|
||||||
echo "🔍 Liberando porta 5000..."
|
echo "Liberando porta 5000..."
|
||||||
lsof -ti:5000 | xargs kill -9 2>/dev/null || true
|
lsof -ti:5000 | xargs kill -9 2>/dev/null || true
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
# Verifica porta LIBERA
|
# Verifica porta libera
|
||||||
if lsof -Pi :5000 -sTCP:LISTEN -t >/dev/null 2>&1; then
|
if lsof -Pi :5000 -sTCP:LISTEN -t >/dev/null 2>&1; then
|
||||||
echo "❌ ERRORE: Porta 5000 ancora occupata!"
|
echo "ERRORE: Porta 5000 ancora occupata!"
|
||||||
echo "Processi sulla porta:"
|
echo "Processi sulla porta:"
|
||||||
lsof -i:5000
|
lsof -i:5000
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✅ Porta 5000 libera"
|
echo "Porta 5000 libera"
|
||||||
|
|
||||||
# Restart usando check_frontend.sh
|
# Start servizio
|
||||||
echo "🚀 Starting frontend..."
|
echo "Starting ids-backend..."
|
||||||
/opt/ids/deployment/check_frontend.sh
|
sudo systemctl start ids-backend.service
|
||||||
|
|
||||||
# Attendi avvio completo
|
# Attendi avvio completo
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
# Verifica avvio
|
# Verifica avvio
|
||||||
if pgrep -f "vite" > /dev/null; then
|
if systemctl is-active --quiet ids-backend.service; then
|
||||||
PID=$(pgrep -f "vite")
|
echo "Backend avviato con successo"
|
||||||
echo "✅ Frontend avviato con PID: $PID"
|
echo "Server disponibile su: http://localhost:5000"
|
||||||
echo "📡 Server disponibile su: http://localhost:5000"
|
|
||||||
|
|
||||||
# Test rapido
|
# Test rapido
|
||||||
sleep 2
|
sleep 2
|
||||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5000/ 2>/dev/null || echo "000")
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5000/ 2>/dev/null || echo "000")
|
||||||
if [ "$HTTP_CODE" = "200" ]; then
|
if [ "$HTTP_CODE" = "200" ]; then
|
||||||
echo "✅ HTTP test OK (200)"
|
echo "HTTP test OK (200)"
|
||||||
else
|
else
|
||||||
echo "⚠️ HTTP test: $HTTP_CODE"
|
echo "HTTP test: $HTTP_CODE (potrebbe essere in fase di avvio)"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "❌ Errore: Frontend non avviato!"
|
echo "ERRORE: Backend non avviato!"
|
||||||
echo "📋 Controlla log: tail -f /var/log/ids/frontend.log"
|
echo "Controlla log: journalctl -u ids-backend -n 20"
|
||||||
|
sudo journalctl -u ids-backend -n 20 --no-pager
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=IDS Auto-Blocking Service - Detect and Block Malicious IPs
|
Description=IDS Auto-Blocking Service - Detect and Block Malicious IPs
|
||||||
Documentation=https://github.com/yourusername/ids
|
After=network.target postgresql-16.service
|
||||||
After=network.target ids-ml-backend.service postgresql-16.service
|
Wants=ids-ml-backend.service
|
||||||
Requires=ids-ml-backend.service
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
@ -23,8 +22,8 @@ SyslogIdentifier=ids-auto-block
|
|||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
|
|
||||||
# Timeout: max 3 minuti per detection+blocking
|
# Timeout: max 8 minuti per detection+blocking
|
||||||
TimeoutStartSec=180
|
TimeoutStartSec=480
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
32
deployment/systemd/ids-backend.service
Normal file
32
deployment/systemd/ids-backend.service
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=IDS Node.js Backend (Express API + Frontend)
|
||||||
|
After=network.target postgresql-16.service
|
||||||
|
Wants=postgresql-16.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=ids
|
||||||
|
Group=ids
|
||||||
|
WorkingDirectory=/opt/ids
|
||||||
|
EnvironmentFile=/opt/ids/.env
|
||||||
|
Environment=NODE_ENV=production
|
||||||
|
Environment=PORT=5000
|
||||||
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
|
|
||||||
|
ExecStartPre=/bin/bash -c 'test -f /opt/ids/dist/index.js || (echo "ERRORE: dist/index.js non trovato - eseguire npm run build" && exit 1)'
|
||||||
|
ExecStart=/usr/bin/env node dist/index.js
|
||||||
|
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
StartLimitInterval=300
|
||||||
|
StartLimitBurst=10
|
||||||
|
|
||||||
|
LimitNOFILE=65536
|
||||||
|
MemoryMax=1G
|
||||||
|
|
||||||
|
StandardOutput=append:/var/log/ids/backend.log
|
||||||
|
StandardError=append:/var/log/ids/backend.log
|
||||||
|
SyslogIdentifier=ids-backend
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@ -3,59 +3,92 @@
|
|||||||
IDS Auto-Blocking Script
|
IDS Auto-Blocking Script
|
||||||
Rileva e blocca automaticamente IP con risk_score >= 80
|
Rileva e blocca automaticamente IP con risk_score >= 80
|
||||||
Eseguito periodicamente da systemd timer (ogni 5 minuti)
|
Eseguito periodicamente da systemd timer (ogni 5 minuti)
|
||||||
|
|
||||||
|
Flusso:
|
||||||
|
1. Chiama Node.js /api/ml/detect per eseguire detection ML
|
||||||
|
2. Chiama Node.js /api/ml/block-all-critical per bloccare IP critici sui router
|
||||||
"""
|
"""
|
||||||
import requests
|
import requests
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
NODE_API_URL = "http://localhost:5000"
|
||||||
ML_API_URL = "http://localhost:8000"
|
ML_API_URL = "http://localhost:8000"
|
||||||
|
|
||||||
def auto_block():
|
def auto_block():
|
||||||
"""Esegue detection e blocking automatico degli IP critici"""
|
"""Esegue detection e blocking automatico degli IP critici"""
|
||||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
print(f"[{timestamp}] 🔍 Starting auto-block detection...")
|
print(f"[{timestamp}] Starting auto-block cycle...")
|
||||||
|
|
||||||
|
# Step 1: Esegui detection via ML Backend (se disponibile)
|
||||||
try:
|
try:
|
||||||
# Chiama endpoint ML /detect con auto_block=true
|
print(f"[{timestamp}] Step 1: Detection ML...")
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
f"{ML_API_URL}/detect",
|
f"{ML_API_URL}/detect",
|
||||||
json={
|
json={
|
||||||
"max_records": 5000, # Analizza ultimi 5000 log
|
"max_records": 50000,
|
||||||
"hours_back": 1.0, # Ultima ora
|
"hours_back": 1.0,
|
||||||
"risk_threshold": 80.0, # Solo IP critici (score >= 80)
|
"risk_threshold": 75.0,
|
||||||
"auto_block": True # BLOCCA AUTOMATICAMENTE
|
"auto_block": False
|
||||||
},
|
},
|
||||||
timeout=120 # 2 minuti timeout
|
timeout=120
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
data = response.json()
|
data = response.json()
|
||||||
detections = len(data.get("detections", []))
|
detections = len(data.get("detections", []))
|
||||||
|
print(f"[{timestamp}] Detection completata: {detections} anomalie rilevate")
|
||||||
|
else:
|
||||||
|
print(f"[{timestamp}] Detection API error: HTTP {response.status_code}")
|
||||||
|
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
print(f"[{timestamp}] ML Backend non raggiungibile, skip detection (blocco IP esistenti continua)")
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
print(f"[{timestamp}] ML Detection timeout, skip (blocco IP esistenti continua)")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[{timestamp}] Detection error: {e}")
|
||||||
|
|
||||||
|
# Step 2: Blocca IP critici (score >= 80) via Node.js
|
||||||
|
try:
|
||||||
|
print(f"[{timestamp}] Step 2: Blocco IP critici sui router...")
|
||||||
|
response = requests.post(
|
||||||
|
f"{NODE_API_URL}/api/ml/block-all-critical",
|
||||||
|
json={
|
||||||
|
"min_score": 80,
|
||||||
|
"limit": 200,
|
||||||
|
"list_name": "ddos_blocked"
|
||||||
|
},
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
blocked = data.get("blocked", 0)
|
blocked = data.get("blocked", 0)
|
||||||
|
failed = data.get("failed", 0)
|
||||||
|
skipped = data.get("skipped", 0)
|
||||||
|
remaining = data.get("remaining", 0)
|
||||||
|
|
||||||
if blocked > 0:
|
if blocked > 0:
|
||||||
print(f"✓ Detection completata: {detections} anomalie rilevate, {blocked} IP bloccati")
|
print(f"[{timestamp}] {blocked} IP bloccati sui router, {failed} falliti, {skipped} gia' bloccati")
|
||||||
else:
|
else:
|
||||||
print(f"✓ Detection completata: {detections} anomalie rilevate, nessun nuovo IP da bloccare")
|
print(f"[{timestamp}] Nessun nuovo IP da bloccare ({skipped} gia' bloccati)")
|
||||||
|
|
||||||
|
if remaining > 0:
|
||||||
|
print(f"[{timestamp}] Rimangono {remaining} IP critici da bloccare")
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
print(f"✗ API error: HTTP {response.status_code}")
|
print(f"[{timestamp}] Block API error: HTTP {response.status_code} - {response.text[:200]}")
|
||||||
print(f" Response: {response.text}")
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
print("✗ ERRORE: ML Backend non raggiungibile su http://localhost:8000")
|
print(f"[{timestamp}] ERRORE: Node.js backend non raggiungibile su {NODE_API_URL}")
|
||||||
print(" Verifica che ids-ml-backend.service sia attivo:")
|
|
||||||
print(" sudo systemctl status ids-ml-backend")
|
|
||||||
return 1
|
return 1
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
print("✗ ERRORE: Timeout dopo 120 secondi. Detection troppo lenta?")
|
print(f"[{timestamp}] ERRORE: Timeout blocco IP (300s)")
|
||||||
return 1
|
return 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"✗ ERRORE imprevisto: {type(e).__name__}: {e}")
|
print(f"[{timestamp}] ERRORE imprevisto: {type(e).__name__}: {e}")
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -28,7 +28,7 @@ The IDS employs a React-based frontend for real-time monitoring, detection visua
|
|||||||
- **Automated Blocking**: Critical IPs (score >= 80) are automatically blocked in parallel across configured MikroTik routers via their REST API. **Auto-unblock on whitelist**: When an IP is added to the whitelist, it is automatically removed from all router blocklists. Manual unblock button available in Detections page.
|
- **Automated Blocking**: Critical IPs (score >= 80) are automatically blocked in parallel across configured MikroTik routers via their REST API. **Auto-unblock on whitelist**: When an IP is added to the whitelist, it is automatically removed from all router blocklists. Manual unblock button available in Detections page.
|
||||||
- **Public Lists Integration (v2.0.0 - CIDR Complete)**: Automatic fetcher syncs blacklist/whitelist feeds every 10 minutes (Spamhaus, Talos, AWS, GCP, Cloudflare, IANA, NTP Pool). **Full CIDR support** using PostgreSQL INET/CIDR types with `<<=` containment operators for network range matching. Priority-based merge logic: Manual whitelist > Public whitelist > Blacklist (CIDR-aware). Detections created for blacklisted IPs/ranges (excluding whitelisted ranges). CRUD API + UI for list management. See `deployment/docs/PUBLIC_LISTS_V2_CIDR.md` for implementation details.
|
- **Public Lists Integration (v2.0.0 - CIDR Complete)**: Automatic fetcher syncs blacklist/whitelist feeds every 10 minutes (Spamhaus, Talos, AWS, GCP, Cloudflare, IANA, NTP Pool). **Full CIDR support** using PostgreSQL INET/CIDR types with `<<=` containment operators for network range matching. Priority-based merge logic: Manual whitelist > Public whitelist > Blacklist (CIDR-aware). Detections created for blacklisted IPs/ranges (excluding whitelisted ranges). CRUD API + UI for list management. See `deployment/docs/PUBLIC_LISTS_V2_CIDR.md` for implementation details.
|
||||||
- **Automatic Cleanup**: An hourly systemd timer (`cleanup_detections.py`) removes old detections (48h) and auto-unblocks IPs (2h).
|
- **Automatic Cleanup**: An hourly systemd timer (`cleanup_detections.py`) removes old detections (48h) and auto-unblocks IPs (2h).
|
||||||
- **Service Monitoring & Management**: A dashboard provides real-time status (ML Backend, Database, Syslog Parser). API endpoints, secured with API key authentication and Systemd integration, allow for service management (start/stop/restart) of Python services.
|
- **Service Monitoring & Management**: A dashboard provides real-time status (ML Backend, Database, Syslog Parser, Analytics Aggregator). **Syslog Parser check is database-based** (counts logs in last 30 minutes) and independent of ML Backend availability. ML Stats endpoint has database fallback when Python backend is offline. Training UI shows offline warning and disables actions when ML Backend is unavailable. API endpoints, secured with API key authentication and Systemd integration, allow for service management (start/stop/restart) of Python services.
|
||||||
- **IP Geolocation**: Integration with `ip-api.com` enriches detection data with geographical and AS information, utilizing intelligent caching.
|
- **IP Geolocation**: Integration with `ip-api.com` enriches detection data with geographical and AS information, utilizing intelligent caching.
|
||||||
- **Database Management**: PostgreSQL is used for all persistent data. An intelligent database versioning system ensures efficient SQL migrations (v8 with forced INET/CIDR column types for network range matching). Migration 008 unconditionally recreates INET columns to fix type mismatches. Dual-mode database drivers (`@neondatabase/serverless` for Replit, `pg` for AlmaLinux) ensure environment compatibility.
|
- **Database Management**: PostgreSQL is used for all persistent data. An intelligent database versioning system ensures efficient SQL migrations (v8 with forced INET/CIDR column types for network range matching). Migration 008 unconditionally recreates INET columns to fix type mismatches. Dual-mode database drivers (`@neondatabase/serverless` for Replit, `pg` for AlmaLinux) ensure environment compatibility.
|
||||||
- **Microservices**: Clear separation of concerns between the Python ML backend and the Node.js API backend.
|
- **Microservices**: Clear separation of concerns between the Python ML backend and the Node.js API backend.
|
||||||
|
|||||||
413
server/mikrotik.ts
Normal file
413
server/mikrotik.ts
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
const VERBOSE = process.env.MIKROTIK_DEBUG === '1' || process.env.MIKROTIK_DEBUG === 'true';
|
||||||
|
|
||||||
|
interface RouterConfig {
|
||||||
|
id: string;
|
||||||
|
ipAddress: string;
|
||||||
|
apiPort: number;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BlockResult {
|
||||||
|
routerIp: string;
|
||||||
|
routerName?: string;
|
||||||
|
success: boolean;
|
||||||
|
alreadyExists?: boolean;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mikrotikRequest(
|
||||||
|
router: RouterConfig,
|
||||||
|
method: string,
|
||||||
|
path: string,
|
||||||
|
body?: any,
|
||||||
|
timeoutMs: number = 8000
|
||||||
|
): Promise<{ status: number; data: any }> {
|
||||||
|
const useHttps = router.apiPort === 443;
|
||||||
|
const protocol = useHttps ? "https" : "http";
|
||||||
|
const url = `${protocol}://${router.ipAddress}:${router.apiPort}${path}`;
|
||||||
|
const auth = Buffer.from(`${router.username}:${router.password}`).toString("base64");
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
const origTlsReject = process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
||||||
|
if (useHttps) {
|
||||||
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fetchOptions: RequestInit = {
|
||||||
|
method,
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Basic ${auth}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
signal: controller.signal,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
fetchOptions.body = JSON.stringify(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, fetchOptions);
|
||||||
|
clearTimeout(timeout);
|
||||||
|
|
||||||
|
let data: any;
|
||||||
|
const text = await response.text();
|
||||||
|
try {
|
||||||
|
data = JSON.parse(text);
|
||||||
|
} catch {
|
||||||
|
data = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elapsed = Date.now() - startTime;
|
||||||
|
if (VERBOSE) {
|
||||||
|
const bodyStr = body ? ` body=${JSON.stringify(body)}` : '';
|
||||||
|
const dataPreview = typeof data === 'string' ? data.substring(0, 200) : JSON.stringify(data).substring(0, 200);
|
||||||
|
console.log(`[MIKROTIK] ${method} ${url} => HTTP ${response.status} (${elapsed}ms)${bodyStr} response=${dataPreview}`);
|
||||||
|
} else if (response.status >= 400) {
|
||||||
|
const dataPreview = typeof data === 'string' ? data.substring(0, 100) : JSON.stringify(data).substring(0, 100);
|
||||||
|
console.warn(`[MIKROTIK] ${method} ${router.ipAddress}${path} => HTTP ${response.status} (${elapsed}ms) err=${dataPreview}`);
|
||||||
|
} else if (elapsed > 5000) {
|
||||||
|
console.warn(`[MIKROTIK] SLOW: ${method} ${router.ipAddress}${path} => HTTP ${response.status} (${elapsed}ms)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: response.status, data };
|
||||||
|
} catch (error: any) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
const elapsed = Date.now() - startTime;
|
||||||
|
const errMsg = error.name === 'AbortError' ? `TIMEOUT after ${timeoutMs}ms` : error.message;
|
||||||
|
console.error(`[MIKROTIK] ${method} ${url} => ERRORE: ${errMsg} (${elapsed}ms)`);
|
||||||
|
if (useHttps && origTlsReject !== undefined) {
|
||||||
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = origTlsReject;
|
||||||
|
} else if (useHttps) {
|
||||||
|
delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
if (useHttps) {
|
||||||
|
if (origTlsReject !== undefined) {
|
||||||
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = origTlsReject;
|
||||||
|
} else {
|
||||||
|
delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function testRouterConnection(router: RouterConfig): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const { status } = await mikrotikRequest(router, "GET", "/rest/system/identity");
|
||||||
|
return status === 200;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getExistingBlockedIps(
|
||||||
|
router: RouterConfig,
|
||||||
|
listName: string = "ddos_blocked"
|
||||||
|
): Promise<Set<string>> {
|
||||||
|
try {
|
||||||
|
if (VERBOSE) console.log(`[MIKROTIK] Fetching address-list da router ${router.ipAddress} (list=${listName}, timeout=20s)...`);
|
||||||
|
const { status, data } = await mikrotikRequest(router, "GET", "/rest/ip/firewall/address-list", undefined, 20000);
|
||||||
|
if (status === 200 && Array.isArray(data)) {
|
||||||
|
const ips = new Set<string>();
|
||||||
|
const allLists = new Map<string, number>();
|
||||||
|
for (const entry of data) {
|
||||||
|
const count = allLists.get(entry.list) || 0;
|
||||||
|
allLists.set(entry.list, count + 1);
|
||||||
|
if (entry.list === listName) {
|
||||||
|
ips.add(entry.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const listsInfo = Array.from(allLists.entries()).map(([name, count]) => `${name}:${count}`).join(', ');
|
||||||
|
console.log(`[MIKROTIK] Router ${router.ipAddress}: ${data.length} entries totali (${listsInfo}), ${ips.size} in list "${listName}"`);
|
||||||
|
return ips;
|
||||||
|
}
|
||||||
|
console.warn(`[MIKROTIK] Router ${router.ipAddress}: risposta inattesa status=${status}, data non e' array`);
|
||||||
|
return new Set();
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(`[MIKROTIK] Router ${router.ipAddress}: ERRORE fetch address-list: ${e.message}`);
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addToAddressList(
|
||||||
|
router: RouterConfig,
|
||||||
|
ipAddress: string,
|
||||||
|
listName: string = "ddos_blocked",
|
||||||
|
comment: string = "",
|
||||||
|
timeoutDuration: string = "1h"
|
||||||
|
): Promise<BlockResult> {
|
||||||
|
try {
|
||||||
|
const { status, data } = await mikrotikRequest(router, "POST", "/rest/ip/firewall/address-list/add", {
|
||||||
|
list: listName,
|
||||||
|
address: ipAddress,
|
||||||
|
comment: comment || `IDS block ${new Date().toISOString()}`,
|
||||||
|
timeout: timeoutDuration,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 200 || status === 201) {
|
||||||
|
if (VERBOSE) console.log(`[BLOCK] OK: ${ipAddress} aggiunto su router ${router.ipAddress} (HTTP ${status})`);
|
||||||
|
return { routerIp: router.ipAddress, success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 400 || status === 409) {
|
||||||
|
const text = typeof data === "string" ? data.toLowerCase() : JSON.stringify(data).toLowerCase();
|
||||||
|
if (text.includes("already") || text.includes("exists") || text.includes("duplicate") || text.includes("failure: already")) {
|
||||||
|
if (VERBOSE) console.log(`[BLOCK] SKIP: ${ipAddress} gia' presente su router ${router.ipAddress} (HTTP ${status})`);
|
||||||
|
return { routerIp: router.ipAddress, success: true, alreadyExists: true };
|
||||||
|
}
|
||||||
|
console.warn(`[BLOCK] VERIFICA: ${ipAddress} su router ${router.ipAddress} HTTP ${status} risposta="${text.substring(0, 150)}", verifico lista...`);
|
||||||
|
try {
|
||||||
|
const verifyResult = await mikrotikRequest(router, "GET", "/rest/ip/firewall/address-list");
|
||||||
|
if (verifyResult.status === 200 && Array.isArray(verifyResult.data)) {
|
||||||
|
for (const entry of verifyResult.data) {
|
||||||
|
if (entry.address === ipAddress && entry.list === listName) {
|
||||||
|
console.log(`[BLOCK] CONFERMATO: ${ipAddress} trovato nella lista di router ${router.ipAddress} dopo verifica`);
|
||||||
|
return { routerIp: router.ipAddress, success: true, alreadyExists: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (verifyErr: any) {
|
||||||
|
console.error(`[BLOCK] ERRORE verifica: ${ipAddress} su router ${router.ipAddress}: ${verifyErr.message}`);
|
||||||
|
}
|
||||||
|
const errMsg = `HTTP ${status}: ${typeof data === "string" ? data : JSON.stringify(data)}`;
|
||||||
|
console.error(`[BLOCK] FALLITO: ${ipAddress} su router ${router.ipAddress}: ${errMsg}`);
|
||||||
|
return {
|
||||||
|
routerIp: router.ipAddress,
|
||||||
|
success: false,
|
||||||
|
error: errMsg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const errMsg = `HTTP ${status}: ${typeof data === "string" ? data : JSON.stringify(data)}`;
|
||||||
|
console.error(`[BLOCK] FALLITO: ${ipAddress} su router ${router.ipAddress}: ${errMsg}`);
|
||||||
|
return {
|
||||||
|
routerIp: router.ipAddress,
|
||||||
|
success: false,
|
||||||
|
error: errMsg,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
const errMsg = error.name === 'AbortError' ? `TIMEOUT (8s)` : (error.message || "Connection failed");
|
||||||
|
console.error(`[BLOCK] ERRORE: ${ipAddress} su router ${router.ipAddress}: ${errMsg}`);
|
||||||
|
return {
|
||||||
|
routerIp: router.ipAddress,
|
||||||
|
success: false,
|
||||||
|
error: errMsg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeFromAddressList(
|
||||||
|
router: RouterConfig,
|
||||||
|
ipAddress: string,
|
||||||
|
listName: string = "ddos_blocked"
|
||||||
|
): Promise<BlockResult> {
|
||||||
|
try {
|
||||||
|
if (VERBOSE) console.log(`[UNBLOCK] Rimozione ${ipAddress} da router ${router.ipAddress} (list=${listName})...`);
|
||||||
|
const { status, data } = await mikrotikRequest(router, "GET", "/rest/ip/firewall/address-list");
|
||||||
|
if (status !== 200 || !Array.isArray(data)) {
|
||||||
|
console.error(`[UNBLOCK] ERRORE: impossibile leggere address-list da router ${router.ipAddress}: HTTP ${status}`);
|
||||||
|
return { routerIp: router.ipAddress, success: false, error: "Failed to read address list" };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entry of data) {
|
||||||
|
if (entry.address === ipAddress && entry.list === listName) {
|
||||||
|
const entryId = entry[".id"];
|
||||||
|
const delResult = await mikrotikRequest(router, "DELETE", `/rest/ip/firewall/address-list/${entryId}`);
|
||||||
|
if (delResult.status === 200 || delResult.status === 204) {
|
||||||
|
console.log(`[UNBLOCK] OK: ${ipAddress} rimosso da router ${router.ipAddress}`);
|
||||||
|
return { routerIp: router.ipAddress, success: true };
|
||||||
|
}
|
||||||
|
console.error(`[UNBLOCK] FALLITO: eliminazione ${ipAddress} da router ${router.ipAddress}: HTTP ${delResult.status}`);
|
||||||
|
return { routerIp: router.ipAddress, success: false, error: `Delete failed: ${delResult.status}` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERBOSE) console.log(`[UNBLOCK] ${ipAddress} non trovato su router ${router.ipAddress} (gia' assente)`);
|
||||||
|
return { routerIp: router.ipAddress, success: true };
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`[UNBLOCK] ERRORE: ${ipAddress} su router ${router.ipAddress}: ${error.message}`);
|
||||||
|
return { routerIp: router.ipAddress, success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function blockIpOnAllRouters(
|
||||||
|
routers: RouterConfig[],
|
||||||
|
ipAddress: string,
|
||||||
|
listName: string = "ddos_blocked",
|
||||||
|
comment: string = "",
|
||||||
|
timeoutDuration: string = "1h"
|
||||||
|
): Promise<BlockResult[]> {
|
||||||
|
const enabled = routers.filter((r) => r.enabled);
|
||||||
|
const results = await Promise.allSettled(
|
||||||
|
enabled.map((r) => addToAddressList(r, ipAddress, listName, comment, timeoutDuration))
|
||||||
|
);
|
||||||
|
|
||||||
|
return results.map((r, i) =>
|
||||||
|
r.status === "fulfilled" ? r.value : { routerIp: enabled[i].ipAddress, success: false, error: String(r.reason) }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unblockIpOnAllRouters(
|
||||||
|
routers: RouterConfig[],
|
||||||
|
ipAddress: string,
|
||||||
|
listName: string = "ddos_blocked"
|
||||||
|
): Promise<BlockResult[]> {
|
||||||
|
const enabled = routers.filter((r) => r.enabled);
|
||||||
|
const results = await Promise.allSettled(
|
||||||
|
enabled.map((r) => removeFromAddressList(r, ipAddress, listName))
|
||||||
|
);
|
||||||
|
|
||||||
|
return results.map((r, i) =>
|
||||||
|
r.status === "fulfilled" ? r.value : { routerIp: enabled[i].ipAddress, success: false, error: String(r.reason) }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function bulkBlockIps(
|
||||||
|
routers: RouterConfig[],
|
||||||
|
ipList: string[],
|
||||||
|
listName: string = "ddos_blocked",
|
||||||
|
commentPrefix: string = "IDS bulk-block",
|
||||||
|
timeoutDuration: string = "1h",
|
||||||
|
concurrency: number = 10
|
||||||
|
): Promise<{ blocked: number; failed: number; skipped: number; details: Array<{ ip: string; status: string }> }> {
|
||||||
|
const enabled = routers.filter((r) => r.enabled);
|
||||||
|
if (enabled.length === 0) {
|
||||||
|
return { blocked: 0, failed: 0, skipped: 0, details: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[BULK-BLOCK] Starting: ${ipList.length} IPs on ${enabled.length} routers (${enabled.map(r => r.ipAddress).join(', ')})`);
|
||||||
|
|
||||||
|
const routerStatus = new Map<string, { ok: number; fail: number; skip: number }>();
|
||||||
|
for (const r of enabled) {
|
||||||
|
routerStatus.set(r.ipAddress, { ok: 0, fail: 0, skip: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingCache = new Map<string, Set<string>>();
|
||||||
|
await Promise.allSettled(
|
||||||
|
enabled.map(async (router) => {
|
||||||
|
const start = Date.now();
|
||||||
|
const existing = await getExistingBlockedIps(router, listName);
|
||||||
|
const elapsed = Date.now() - start;
|
||||||
|
existingCache.set(router.ipAddress, existing);
|
||||||
|
console.log(`[BULK-BLOCK] Router ${router.ipAddress}: ${existing.size} IPs already in list (${elapsed}ms)`);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const newIps: string[] = [];
|
||||||
|
const skippedIps: string[] = [];
|
||||||
|
|
||||||
|
for (const ip of ipList) {
|
||||||
|
let alreadyOnAll = true;
|
||||||
|
for (const router of enabled) {
|
||||||
|
const existing = existingCache.get(router.ipAddress) || new Set();
|
||||||
|
if (!existing.has(ip)) {
|
||||||
|
alreadyOnAll = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (alreadyOnAll) {
|
||||||
|
skippedIps.push(ip);
|
||||||
|
} else {
|
||||||
|
newIps.push(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[BULK-BLOCK] ${skippedIps.length} already blocked, ${newIps.length} new to block`);
|
||||||
|
|
||||||
|
let blocked = 0;
|
||||||
|
let failed = 0;
|
||||||
|
const details: Array<{ ip: string; status: string }> = [];
|
||||||
|
const partialIps: string[] = [];
|
||||||
|
const failedIps: string[] = [];
|
||||||
|
|
||||||
|
async function processIp(ip: string) {
|
||||||
|
const routerResults = await Promise.allSettled(
|
||||||
|
enabled.map(async (router) => {
|
||||||
|
const existing = existingCache.get(router.ipAddress) || new Set();
|
||||||
|
if (existing.has(ip)) {
|
||||||
|
const st = routerStatus.get(router.ipAddress);
|
||||||
|
if (st) st.skip++;
|
||||||
|
return { success: true, skipped: true, routerIp: router.ipAddress };
|
||||||
|
}
|
||||||
|
const start = Date.now();
|
||||||
|
const result = await addToAddressList(router, ip, listName, `${commentPrefix} ${ip}`, timeoutDuration);
|
||||||
|
const elapsed = Date.now() - start;
|
||||||
|
const st = routerStatus.get(router.ipAddress);
|
||||||
|
if (result.success) {
|
||||||
|
if (st) st.ok++;
|
||||||
|
} else {
|
||||||
|
if (st) st.fail++;
|
||||||
|
}
|
||||||
|
return { success: result.success, skipped: false, routerIp: router.ipAddress, elapsed, error: result.error };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const perRouterDetail = routerResults.map((r) => {
|
||||||
|
if (r.status === 'fulfilled') {
|
||||||
|
const v = r.value;
|
||||||
|
if (v.skipped) return `${v.routerIp}:SKIP`;
|
||||||
|
if (v.success) return `${v.routerIp}:OK(${v.elapsed}ms)`;
|
||||||
|
return `${v.routerIp}:FAIL(${v.elapsed}ms,${v.error})`;
|
||||||
|
}
|
||||||
|
return 'REJECTED';
|
||||||
|
}).join(' | ');
|
||||||
|
|
||||||
|
const anySuccess = routerResults.some(
|
||||||
|
(r) => r.status === "fulfilled" && r.value.success
|
||||||
|
);
|
||||||
|
const allSuccess = routerResults.every(
|
||||||
|
(r) => r.status === "fulfilled" && r.value.success
|
||||||
|
);
|
||||||
|
|
||||||
|
if (anySuccess) {
|
||||||
|
blocked++;
|
||||||
|
details.push({ ip, status: "blocked" });
|
||||||
|
if (!allSuccess) {
|
||||||
|
partialIps.push(ip);
|
||||||
|
if (VERBOSE) console.warn(`[BULK-BLOCK] PARZIALE: IP ${ip}: ${perRouterDetail}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failed++;
|
||||||
|
failedIps.push(ip);
|
||||||
|
details.push({ ip, status: "failed" });
|
||||||
|
if (VERBOSE) console.error(`[BULK-BLOCK] FALLITO: IP ${ip}: ${perRouterDetail}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bulkStart = Date.now();
|
||||||
|
for (let i = 0; i < newIps.length; i += concurrency) {
|
||||||
|
const batch = newIps.slice(i, i + concurrency);
|
||||||
|
await Promise.allSettled(batch.map((ip) => processIp(ip)));
|
||||||
|
|
||||||
|
const progress = Math.min(i + concurrency, newIps.length);
|
||||||
|
if (progress === newIps.length || progress % 50 === 0) {
|
||||||
|
const elapsed = ((Date.now() - bulkStart) / 1000).toFixed(1);
|
||||||
|
console.log(`[BULK-BLOCK] Progress: ${progress}/${newIps.length} (${elapsed}s, ${blocked} ok, ${failed} fail)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ip of skippedIps) {
|
||||||
|
details.push({ ip, status: "already_blocked" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalElapsed = ((Date.now() - bulkStart) / 1000).toFixed(1);
|
||||||
|
routerStatus.forEach((st, routerIp) => {
|
||||||
|
console.log(`[BULK-BLOCK] Router ${routerIp}: ${st.ok} blocked, ${st.fail} failed, ${st.skip} skipped`);
|
||||||
|
});
|
||||||
|
console.log(`[BULK-BLOCK] Completato in ${totalElapsed}s: ${blocked} blocked, ${failed} failed, ${skippedIps.length} already_blocked, ${partialIps.length} parziali`);
|
||||||
|
if (failedIps.length > 0) {
|
||||||
|
console.error(`[BULK-BLOCK] IP non bloccati su nessun router (${failedIps.length}): ${failedIps.slice(0, 20).join(', ')}${failedIps.length > 20 ? '...' : ''}`);
|
||||||
|
}
|
||||||
|
if (partialIps.length > 0) {
|
||||||
|
console.warn(`[BULK-BLOCK] IP bloccati solo parzialmente (${partialIps.length}): ${partialIps.slice(0, 20).join(', ')}${partialIps.length > 20 ? '...' : ''}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { blocked, failed, skipped: skippedIps.length, details };
|
||||||
|
}
|
||||||
456
server/routes.ts
456
server/routes.ts
@ -1,9 +1,10 @@
|
|||||||
import type { Express } from "express";
|
import type { Express } from "express";
|
||||||
import { createServer, type Server } from "http";
|
import { createServer, type Server } from "http";
|
||||||
import { storage } from "./storage";
|
import { storage } from "./storage";
|
||||||
import { insertRouterSchema, insertDetectionSchema, insertWhitelistSchema, insertPublicListSchema, networkAnalytics, routers, detections, networkLogs } from "@shared/schema";
|
import { insertRouterSchema, insertDetectionSchema, insertWhitelistSchema, insertPublicListSchema, networkAnalytics, routers, detections, networkLogs, trainingHistory } from "@shared/schema";
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { desc, eq, gte, sql } from "drizzle-orm";
|
import { desc, eq, gte, sql } from "drizzle-orm";
|
||||||
|
import { blockIpOnAllRouters, unblockIpOnAllRouters, bulkBlockIps, testRouterConnection } from "./mikrotik";
|
||||||
|
|
||||||
export async function registerRoutes(app: Express): Promise<Server> {
|
export async function registerRoutes(app: Express): Promise<Server> {
|
||||||
// Routers
|
// Routers
|
||||||
@ -139,28 +140,16 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
const validatedData = insertWhitelistSchema.parse(req.body);
|
const validatedData = insertWhitelistSchema.parse(req.body);
|
||||||
const item = await storage.createWhitelist(validatedData);
|
const item = await storage.createWhitelist(validatedData);
|
||||||
|
|
||||||
// Auto-unblock from routers when adding to whitelist
|
|
||||||
const mlBackendUrl = process.env.ML_BACKEND_URL || 'http://localhost:8000';
|
|
||||||
const mlApiKey = process.env.IDS_API_KEY;
|
|
||||||
try {
|
try {
|
||||||
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
const allRouters = await storage.getAllRouters();
|
||||||
if (mlApiKey) {
|
const enabledRouters = allRouters.filter(r => r.enabled);
|
||||||
headers['X-API-Key'] = mlApiKey;
|
if (enabledRouters.length > 0) {
|
||||||
}
|
const results = await unblockIpOnAllRouters(enabledRouters as any, validatedData.ipAddress);
|
||||||
const unblockResponse = await fetch(`${mlBackendUrl}/unblock-ip`, {
|
const unblocked = results.filter(r => r.success).length;
|
||||||
method: 'POST',
|
console.log(`[WHITELIST] Auto-unblocked ${validatedData.ipAddress} from ${unblocked}/${enabledRouters.length} routers`);
|
||||||
headers,
|
|
||||||
body: JSON.stringify({ ip_address: validatedData.ipAddress })
|
|
||||||
});
|
|
||||||
if (unblockResponse.ok) {
|
|
||||||
const result = await unblockResponse.json();
|
|
||||||
console.log(`[WHITELIST] Auto-unblocked ${validatedData.ipAddress} from ${result.unblocked_from} routers`);
|
|
||||||
} else {
|
|
||||||
console.warn(`[WHITELIST] Failed to auto-unblock ${validatedData.ipAddress}: ${unblockResponse.status}`);
|
|
||||||
}
|
}
|
||||||
} catch (unblockError) {
|
} catch (unblockError) {
|
||||||
// Don't fail if ML backend is unavailable
|
console.warn(`[WHITELIST] Auto-unblock failed for ${validatedData.ipAddress}:`, unblockError);
|
||||||
console.warn(`[WHITELIST] ML backend unavailable for auto-unblock: ${unblockError}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(item);
|
res.json(item);
|
||||||
@ -169,7 +158,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Unblock IP from all routers (proxy to ML backend)
|
|
||||||
app.post("/api/unblock-ip", async (req, res) => {
|
app.post("/api/unblock-ip", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { ipAddress, listName = "ddos_blocked" } = req.body;
|
const { ipAddress, listName = "ddos_blocked" } = req.body;
|
||||||
@ -178,31 +166,31 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
return res.status(400).json({ error: "IP address is required" });
|
return res.status(400).json({ error: "IP address is required" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const mlBackendUrl = process.env.ML_BACKEND_URL || 'http://localhost:8000';
|
const allRouters = await storage.getAllRouters();
|
||||||
const mlApiKey = process.env.IDS_API_KEY;
|
const enabledRouters = allRouters.filter(r => r.enabled);
|
||||||
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
||||||
if (mlApiKey) {
|
if (enabledRouters.length === 0) {
|
||||||
headers['X-API-Key'] = mlApiKey;
|
return res.status(400).json({ error: "Nessun router abilitato" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`${mlBackendUrl}/unblock-ip`, {
|
const results = await unblockIpOnAllRouters(enabledRouters as any, ipAddress, listName);
|
||||||
method: 'POST',
|
const successCount = results.filter(r => r.success).length;
|
||||||
headers,
|
|
||||||
body: JSON.stringify({ ip_address: ipAddress, list_name: listName })
|
await db.update(detections)
|
||||||
|
.set({ blocked: false })
|
||||||
|
.where(eq(detections.sourceIp, ipAddress));
|
||||||
|
|
||||||
|
console.log(`[UNBLOCK] ${ipAddress} rimosso da ${successCount}/${enabledRouters.length} router`);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `IP ${ipAddress} sbloccato da ${successCount} router`,
|
||||||
|
unblocked_from: successCount,
|
||||||
|
total_routers: enabledRouters.length,
|
||||||
|
results: results.map(r => ({ router: r.routerIp, success: r.success, error: r.error }))
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await response.text();
|
|
||||||
console.error(`[UNBLOCK] ML backend error for ${ipAddress}: ${response.status} - ${errorText}`);
|
|
||||||
return res.status(response.status).json({ error: errorText || "Failed to unblock IP" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
console.log(`[UNBLOCK] Successfully unblocked ${ipAddress} from ${result.unblocked_from || 0} routers`);
|
|
||||||
res.json(result);
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('[UNBLOCK] Error:', error);
|
console.error('[UNBLOCK] Error:', error);
|
||||||
res.status(500).json({ error: error.message || "Failed to unblock IP from routers" });
|
res.status(500).json({ error: error.message || "Errore sblocco IP" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -639,36 +627,92 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
|
|
||||||
app.post("/api/ml/block-all-critical", async (req, res) => {
|
app.post("/api/ml/block-all-critical", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { min_score = 80 } = req.body;
|
const { min_score = 80, list_name = "ddos_blocked", limit = 100 } = req.body;
|
||||||
|
const maxIps = Math.min(Number(limit) || 100, 500);
|
||||||
const controller = new AbortController();
|
|
||||||
const timeout = setTimeout(() => controller.abort(), 300000); // 5 min timeout
|
const allRouters = await storage.getAllRouters();
|
||||||
|
const enabledRouters = allRouters.filter(r => r.enabled);
|
||||||
const response = await fetch(`${ML_BACKEND_URL}/block-all-critical`, {
|
|
||||||
method: "POST",
|
if (enabledRouters.length === 0) {
|
||||||
headers: getMLBackendHeaders(),
|
return res.status(400).json({ error: "Nessun router abilitato" });
|
||||||
body: JSON.stringify({ min_score }),
|
}
|
||||||
signal: controller.signal,
|
|
||||||
});
|
const unblockedDetections = await db.execute(
|
||||||
|
sql`SELECT DISTINCT source_ip, MAX(CAST(risk_score AS FLOAT)) as max_score, MAX(anomaly_type) as anomaly_type
|
||||||
clearTimeout(timeout);
|
FROM detections
|
||||||
|
WHERE CAST(risk_score AS FLOAT) >= ${min_score}
|
||||||
if (!response.ok) {
|
AND blocked = false
|
||||||
const errorData = await response.json().catch(() => ({}));
|
AND source_ip NOT IN (SELECT ip_address FROM whitelist WHERE active = true)
|
||||||
return res.status(response.status).json({
|
GROUP BY source_ip
|
||||||
error: errorData.detail || "Block all critical failed",
|
ORDER BY max_score DESC
|
||||||
|
LIMIT ${maxIps}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const rows = (unblockedDetections as any).rows || unblockedDetections;
|
||||||
|
|
||||||
|
const totalUnblockedResult = await db.execute(
|
||||||
|
sql`SELECT COUNT(DISTINCT source_ip) as count
|
||||||
|
FROM detections
|
||||||
|
WHERE CAST(risk_score AS FLOAT) >= ${min_score}
|
||||||
|
AND blocked = false
|
||||||
|
AND source_ip NOT IN (SELECT ip_address FROM whitelist WHERE active = true)`
|
||||||
|
);
|
||||||
|
const totalUnblockedRows = (totalUnblockedResult as any).rows || totalUnblockedResult;
|
||||||
|
const totalUnblocked = parseInt(totalUnblockedRows[0]?.count || "0");
|
||||||
|
|
||||||
|
if (!rows || rows.length === 0) {
|
||||||
|
return res.json({
|
||||||
|
message: "Nessun IP critico da bloccare",
|
||||||
|
blocked: 0,
|
||||||
|
failed: 0,
|
||||||
|
total_critical: 0,
|
||||||
|
remaining: 0,
|
||||||
|
skipped: 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const ipList = rows.map((r: any) => r.source_ip);
|
||||||
res.json(data);
|
const routerInfo = enabledRouters.map((r: any) => `${r.name || r.ipAddress}(${r.ipAddress}:${r.apiPort})`).join(', ');
|
||||||
|
console.log(`[BLOCK-ALL] Avvio blocco massivo: ${ipList.length}/${totalUnblocked} IP con score >= ${min_score} su ${enabledRouters.length} router: ${routerInfo}`);
|
||||||
|
|
||||||
|
const result = await bulkBlockIps(
|
||||||
|
enabledRouters as any,
|
||||||
|
ipList,
|
||||||
|
list_name,
|
||||||
|
`IDS bulk-block (score>=${min_score})`,
|
||||||
|
"1h",
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.blocked > 0) {
|
||||||
|
const blockedIps = result.details
|
||||||
|
.filter(d => d.status === "blocked")
|
||||||
|
.map(d => d.ip);
|
||||||
|
|
||||||
|
const batchSize = 200;
|
||||||
|
for (let i = 0; i < blockedIps.length; i += batchSize) {
|
||||||
|
const batch = blockedIps.slice(i, i + batchSize);
|
||||||
|
const ipValues = batch.map(ip => `'${ip.replace(/'/g, "''")}'`).join(',');
|
||||||
|
await db.execute(
|
||||||
|
sql`UPDATE detections SET blocked = true, blocked_at = NOW() WHERE source_ip IN (${sql.raw(ipValues)}) AND blocked = false`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.log(`[BLOCK-ALL] Database aggiornato: ${blockedIps.length} IP marcati come bloccati`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const remaining = totalUnblocked - ipList.length;
|
||||||
|
res.json({
|
||||||
|
message: `Blocco massivo completato: ${result.blocked} IP bloccati, ${result.failed} falliti, ${result.skipped} già bloccati` +
|
||||||
|
(remaining > 0 ? `. Rimangono ${remaining} IP da bloccare.` : ''),
|
||||||
|
blocked: result.blocked,
|
||||||
|
failed: result.failed,
|
||||||
|
skipped: result.skipped,
|
||||||
|
total_critical: ipList.length,
|
||||||
|
remaining,
|
||||||
|
details: result.details.slice(0, 100)
|
||||||
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.name === 'AbortError') {
|
console.error('[BLOCK-ALL] Error:', error);
|
||||||
return res.status(504).json({ error: "Timeout - operazione troppo lunga" });
|
|
||||||
}
|
|
||||||
if (error.code === 'ECONNREFUSED') {
|
|
||||||
return res.status(503).json({ error: "ML backend non disponibile" });
|
|
||||||
}
|
|
||||||
res.status(500).json({ error: error.message || "Errore blocco massivo" });
|
res.status(500).json({ error: error.message || "Errore blocco massivo" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -676,7 +720,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
app.get("/api/ml/stats", async (req, res) => {
|
app.get("/api/ml/stats", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => controller.abort(), 10000); // 10s timeout for stats
|
const timeout = setTimeout(() => controller.abort(), 15000);
|
||||||
|
|
||||||
const response = await fetch(`${ML_BACKEND_URL}/stats`, {
|
const response = await fetch(`${ML_BACKEND_URL}/stats`, {
|
||||||
headers: getMLBackendHeaders(),
|
headers: getMLBackendHeaders(),
|
||||||
@ -686,62 +730,93 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
throw new Error(`HTTP ${response.status}`);
|
||||||
return res.status(response.status).json({
|
|
||||||
error: errorData.detail || "Failed to fetch ML stats",
|
|
||||||
status: response.status,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
res.json(data);
|
res.json(data);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.name === 'AbortError') {
|
console.warn(`[ML Stats] Fallback to database - ML Backend error: ${error.message || error.code || 'unknown'}`);
|
||||||
return res.status(504).json({ error: "Stats timeout" });
|
try {
|
||||||
|
const latestTraining = await db
|
||||||
|
.select()
|
||||||
|
.from(trainingHistory)
|
||||||
|
.orderBy(desc(trainingHistory.trainedAt))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
const detectionStats = await db.execute(
|
||||||
|
sql`SELECT
|
||||||
|
COUNT(*) as total_detections,
|
||||||
|
COUNT(*) FILTER (WHERE blocked = true) as blocked_count,
|
||||||
|
COUNT(*) FILTER (WHERE CAST(risk_score AS FLOAT) >= 80) as critical_count,
|
||||||
|
COUNT(DISTINCT source_ip) as unique_ips
|
||||||
|
FROM detections`
|
||||||
|
);
|
||||||
|
const statsRows = (detectionStats as any).rows || detectionStats;
|
||||||
|
|
||||||
|
const logCount = await db.execute(
|
||||||
|
sql`SELECT COUNT(*) as count FROM network_logs WHERE timestamp > NOW() - INTERVAL '24 hours'`
|
||||||
|
);
|
||||||
|
const logRows = (logCount as any).rows || logCount;
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
source: "database_fallback",
|
||||||
|
ml_backend_status: "offline",
|
||||||
|
latest_training: latestTraining[0] || null,
|
||||||
|
detections: {
|
||||||
|
total: parseInt(statsRows[0]?.total_detections || "0"),
|
||||||
|
blocked: parseInt(statsRows[0]?.blocked_count || "0"),
|
||||||
|
critical: parseInt(statsRows[0]?.critical_count || "0"),
|
||||||
|
unique_ips: parseInt(statsRows[0]?.unique_ips || "0"),
|
||||||
|
},
|
||||||
|
logs_24h: parseInt(logRows[0]?.count || "0"),
|
||||||
|
});
|
||||||
|
} catch (dbError: any) {
|
||||||
|
res.status(503).json({ error: "ML backend offline and database fallback failed" });
|
||||||
}
|
}
|
||||||
if (error.code === 'ECONNREFUSED') {
|
|
||||||
return res.status(503).json({ error: "ML backend not available" });
|
|
||||||
}
|
|
||||||
res.status(500).json({ error: error.message || "Failed to fetch ML stats" });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Services monitoring
|
// Services monitoring
|
||||||
app.get("/api/services/status", async (req, res) => {
|
app.get("/api/services/status", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
const mkService = (name: string) => ({ name, status: "unknown" as string, healthy: false, details: null as any, systemdUnit: "" as string, type: "service" as string });
|
||||||
|
|
||||||
const services = {
|
const services = {
|
||||||
mlBackend: { name: "ML Backend Python", status: "unknown", healthy: false, details: null as any },
|
nodeBackend: { ...mkService("Node.js Backend"), systemdUnit: "ids-backend", type: "service" },
|
||||||
database: { name: "PostgreSQL Database", status: "unknown", healthy: false, details: null as any },
|
mlBackend: { ...mkService("ML Backend Python"), systemdUnit: "ids-ml-backend", type: "service" },
|
||||||
syslogParser: { name: "Syslog Parser", status: "unknown", healthy: false, details: null as any },
|
database: { ...mkService("PostgreSQL Database"), systemdUnit: "postgresql-16", type: "service" },
|
||||||
analyticsAggregator: { name: "Analytics Aggregator Timer", status: "unknown", healthy: false, details: null as any },
|
syslogParser: { ...mkService("Syslog Parser"), systemdUnit: "ids-syslog-parser", type: "service" },
|
||||||
|
analyticsAggregator: { ...mkService("Analytics Aggregator"), systemdUnit: "ids-analytics-aggregator", type: "timer" },
|
||||||
|
autoBlock: { ...mkService("Auto Block"), systemdUnit: "ids-auto-block", type: "timer" },
|
||||||
|
cleanup: { ...mkService("Cleanup Detections"), systemdUnit: "ids-cleanup", type: "timer" },
|
||||||
|
listFetcher: { ...mkService("Public Lists Fetcher"), systemdUnit: "ids-list-fetcher", type: "timer" },
|
||||||
|
mlTraining: { ...mkService("ML Training Settimanale"), systemdUnit: "ids-ml-training", type: "timer" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Node.js Backend - always running if this endpoint responds
|
||||||
|
services.nodeBackend.status = "running";
|
||||||
|
services.nodeBackend.healthy = true;
|
||||||
|
services.nodeBackend.details = { port: 5000, uptime: process.uptime().toFixed(0) + "s" };
|
||||||
|
|
||||||
// Check ML Backend Python
|
// Check ML Backend Python
|
||||||
try {
|
try {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => controller.abort(), 5000);
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||||
|
const response = await fetch(`${ML_BACKEND_URL}/health`, { signal: controller.signal });
|
||||||
const response = await fetch(`${ML_BACKEND_URL}/health`, {
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
services.mlBackend.status = "running";
|
services.mlBackend.status = "running";
|
||||||
services.mlBackend.healthy = true;
|
services.mlBackend.healthy = true;
|
||||||
services.mlBackend.details = {
|
services.mlBackend.details = { modelLoaded: data.ml_model === "loaded", timestamp: data.timestamp };
|
||||||
modelLoaded: data.ml_model === "loaded",
|
|
||||||
timestamp: data.timestamp,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
services.mlBackend.status = "error";
|
services.mlBackend.status = "error";
|
||||||
services.mlBackend.details = { error: `HTTP ${response.status}` };
|
services.mlBackend.details = { error: `HTTP ${response.status}` };
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
services.mlBackend.status = "offline";
|
services.mlBackend.status = "offline";
|
||||||
services.mlBackend.details = { error: error.code === 'ECONNREFUSED' ? "Connection refused" : error.message };
|
services.mlBackend.details = { error: error.code === 'ECONNREFUSED' ? "Connessione rifiutata" : error.message };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Database
|
// Check Database
|
||||||
@ -757,89 +832,156 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
services.database.details = { error: error.message };
|
services.database.details = { error: error.message };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Python Services via authenticated endpoint
|
// Check Syslog Parser via database
|
||||||
try {
|
try {
|
||||||
const controller2 = new AbortController();
|
const recentLogsResult = await db.execute(
|
||||||
const timeout2 = setTimeout(() => controller2.abort(), 5000);
|
sql`SELECT COUNT(*) as count, MAX(timestamp) as last_log FROM network_logs WHERE timestamp > NOW() - INTERVAL '30 minutes'`
|
||||||
|
);
|
||||||
const servicesResponse = await fetch(`${ML_BACKEND_URL}/services/status`, {
|
const logRows = (recentLogsResult as any).rows || recentLogsResult;
|
||||||
headers: getMLBackendHeaders(),
|
const recentLogCount = parseInt(logRows[0]?.count || "0");
|
||||||
signal: controller2.signal,
|
const lastLogTime = logRows[0]?.last_log;
|
||||||
});
|
if (recentLogCount > 0) {
|
||||||
|
services.syslogParser.status = "running";
|
||||||
clearTimeout(timeout2);
|
services.syslogParser.healthy = true;
|
||||||
|
services.syslogParser.details = { recentLogs30min: recentLogCount, lastLog: lastLogTime };
|
||||||
if (servicesResponse.ok) {
|
|
||||||
const servicesData = await servicesResponse.json();
|
|
||||||
|
|
||||||
// Update syslog parser status
|
|
||||||
const parserInfo = servicesData.services?.syslog_parser;
|
|
||||||
if (parserInfo) {
|
|
||||||
services.syslogParser.status = parserInfo.running ? "running" : "offline";
|
|
||||||
services.syslogParser.healthy = parserInfo.running;
|
|
||||||
services.syslogParser.details = {
|
|
||||||
systemd_unit: parserInfo.systemd_unit,
|
|
||||||
pid: parserInfo.details?.pid,
|
|
||||||
error: parserInfo.error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else if (servicesResponse.status === 403) {
|
|
||||||
services.syslogParser.status = "error";
|
|
||||||
services.syslogParser.healthy = false;
|
|
||||||
services.syslogParser.details = { error: "Authentication failed" };
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`HTTP ${servicesResponse.status}`);
|
const lastLogEverResult = await db.execute(sql`SELECT MAX(timestamp) as last_log FROM network_logs`);
|
||||||
|
const lastLogEverRows = (lastLogEverResult as any).rows || lastLogEverResult;
|
||||||
|
services.syslogParser.status = "offline";
|
||||||
|
services.syslogParser.healthy = false;
|
||||||
|
services.syslogParser.details = { recentLogs30min: 0, lastLog: lastLogEverRows[0]?.last_log || "Mai", warning: "Nessun log negli ultimi 30 minuti" };
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
services.syslogParser.status = "error";
|
services.syslogParser.status = "error";
|
||||||
services.syslogParser.healthy = false;
|
services.syslogParser.details = { error: error.message };
|
||||||
services.syslogParser.details = {
|
|
||||||
error: error.code === 'ECONNREFUSED' ? "ML Backend offline" : error.message
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Analytics Aggregator (via last record timestamp)
|
// Check Analytics Aggregator (via last record timestamp)
|
||||||
try {
|
try {
|
||||||
const latestAnalytics = await db
|
const latestAnalytics = await db.select().from(networkAnalytics).orderBy(desc(networkAnalytics.date), desc(networkAnalytics.hour)).limit(1);
|
||||||
.select()
|
|
||||||
.from(networkAnalytics)
|
|
||||||
.orderBy(desc(networkAnalytics.date), desc(networkAnalytics.hour))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (latestAnalytics.length > 0) {
|
if (latestAnalytics.length > 0) {
|
||||||
const lastRun = new Date(latestAnalytics[0].date);
|
const lastRun = new Date(latestAnalytics[0].date);
|
||||||
const lastTimestamp = lastRun.toISOString();
|
const hoursSince = (Date.now() - lastRun.getTime()) / (1000 * 60 * 60);
|
||||||
const hoursSinceLastRun = (Date.now() - lastRun.getTime()) / (1000 * 60 * 60);
|
if (hoursSince < 2) {
|
||||||
|
|
||||||
if (hoursSinceLastRun < 2) {
|
|
||||||
services.analyticsAggregator.status = "running";
|
services.analyticsAggregator.status = "running";
|
||||||
services.analyticsAggregator.healthy = true;
|
services.analyticsAggregator.healthy = true;
|
||||||
services.analyticsAggregator.details = {
|
services.analyticsAggregator.details = { lastRun: latestAnalytics[0].date, hoursSinceLastRun: hoursSince.toFixed(1) };
|
||||||
lastRun: latestAnalytics[0].date,
|
|
||||||
lastTimestamp,
|
|
||||||
hoursSinceLastRun: hoursSinceLastRun.toFixed(1),
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
services.analyticsAggregator.status = "idle";
|
services.analyticsAggregator.status = "idle";
|
||||||
services.analyticsAggregator.healthy = false;
|
services.analyticsAggregator.details = { lastRun: latestAnalytics[0].date, hoursSinceLastRun: hoursSince.toFixed(1), warning: "Nessuna aggregazione nelle ultime 2 ore" };
|
||||||
services.analyticsAggregator.details = {
|
|
||||||
lastRun: latestAnalytics[0].date,
|
|
||||||
lastTimestamp,
|
|
||||||
hoursSinceLastRun: hoursSinceLastRun.toFixed(1),
|
|
||||||
warning: "No aggregation in last 2 hours",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
services.analyticsAggregator.status = "idle";
|
services.analyticsAggregator.status = "idle";
|
||||||
services.analyticsAggregator.healthy = false;
|
services.analyticsAggregator.details = { error: "Nessun dato analytics trovato" };
|
||||||
services.analyticsAggregator.details = { error: "No analytics data found" };
|
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
services.analyticsAggregator.status = "error";
|
services.analyticsAggregator.status = "error";
|
||||||
services.analyticsAggregator.healthy = false;
|
|
||||||
services.analyticsAggregator.details = { error: error.message };
|
services.analyticsAggregator.details = { error: error.message };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check Auto Block (via recent blocked detections)
|
||||||
|
try {
|
||||||
|
const recentBlockResult = await db.execute(
|
||||||
|
sql`SELECT COUNT(*) as count, MAX(detected_at) as last_block FROM detections WHERE blocked = true AND detected_at > NOW() - INTERVAL '10 minutes'`
|
||||||
|
);
|
||||||
|
const blockRows = (recentBlockResult as any).rows || recentBlockResult;
|
||||||
|
const recentBlocks = parseInt(blockRows[0]?.count || "0");
|
||||||
|
const lastBlock = blockRows[0]?.last_block;
|
||||||
|
|
||||||
|
const totalBlockedResult = await db.execute(sql`SELECT COUNT(*) as count FROM detections WHERE blocked = true`);
|
||||||
|
const totalBlockedRows = (totalBlockedResult as any).rows || totalBlockedResult;
|
||||||
|
const totalBlocked = parseInt(totalBlockedRows[0]?.count || "0");
|
||||||
|
|
||||||
|
services.autoBlock.status = recentBlocks > 0 ? "running" : "idle";
|
||||||
|
services.autoBlock.healthy = true;
|
||||||
|
services.autoBlock.details = {
|
||||||
|
recentBlocks10min: recentBlocks,
|
||||||
|
totalBlocked,
|
||||||
|
lastBlock: lastBlock || "Mai",
|
||||||
|
interval: "ogni 5 minuti"
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
services.autoBlock.status = "error";
|
||||||
|
services.autoBlock.details = { error: error.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Cleanup (via absence of old detections)
|
||||||
|
try {
|
||||||
|
const oldDetResult = await db.execute(
|
||||||
|
sql`SELECT COUNT(*) as count FROM detections WHERE detected_at < NOW() - INTERVAL '48 hours'`
|
||||||
|
);
|
||||||
|
const oldRows = (oldDetResult as any).rows || oldDetResult;
|
||||||
|
const oldDetections = parseInt(oldRows[0]?.count || "0");
|
||||||
|
|
||||||
|
const totalDetResult = await db.execute(sql`SELECT COUNT(*) as count FROM detections`);
|
||||||
|
const totalRows = (totalDetResult as any).rows || totalDetResult;
|
||||||
|
const totalDetections = parseInt(totalRows[0]?.count || "0");
|
||||||
|
|
||||||
|
services.cleanup.status = oldDetections === 0 ? "running" : "idle";
|
||||||
|
services.cleanup.healthy = oldDetections === 0;
|
||||||
|
services.cleanup.details = {
|
||||||
|
oldDetections48h: oldDetections,
|
||||||
|
totalDetections,
|
||||||
|
interval: "ogni ora",
|
||||||
|
warning: oldDetections > 0 ? `${oldDetections} detection vecchie non ancora pulite` : undefined
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
services.cleanup.status = "error";
|
||||||
|
services.cleanup.details = { error: error.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check List Fetcher (via public lists last_updated)
|
||||||
|
try {
|
||||||
|
const listsResult = await db.execute(
|
||||||
|
sql`SELECT COUNT(*) as total,
|
||||||
|
COUNT(*) FILTER (WHERE enabled = true) as enabled,
|
||||||
|
MAX(last_fetch) as last_fetch
|
||||||
|
FROM public_lists`
|
||||||
|
);
|
||||||
|
const listRows = (listsResult as any).rows || listsResult;
|
||||||
|
const totalLists = parseInt(listRows[0]?.total || "0");
|
||||||
|
const enabledLists = parseInt(listRows[0]?.enabled || "0");
|
||||||
|
const lastFetched = listRows[0]?.last_fetch;
|
||||||
|
|
||||||
|
if (lastFetched) {
|
||||||
|
const hoursSince = (Date.now() - new Date(lastFetched).getTime()) / (1000 * 60 * 60);
|
||||||
|
services.listFetcher.status = hoursSince < 1 ? "running" : "idle";
|
||||||
|
services.listFetcher.healthy = hoursSince < 1;
|
||||||
|
services.listFetcher.details = { totalLists, enabledLists, lastFetched, hoursSinceLastFetch: hoursSince.toFixed(1), interval: "ogni 10 minuti" };
|
||||||
|
} else {
|
||||||
|
services.listFetcher.status = "idle";
|
||||||
|
services.listFetcher.details = { totalLists, enabledLists, lastFetched: "Mai", interval: "ogni 10 minuti" };
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
services.listFetcher.status = "error";
|
||||||
|
services.listFetcher.details = { error: error.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ML Training (via training history)
|
||||||
|
try {
|
||||||
|
const latestTraining = await db.select().from(trainingHistory).orderBy(desc(trainingHistory.trainedAt)).limit(1);
|
||||||
|
if (latestTraining.length > 0) {
|
||||||
|
const lastTrainDate = new Date(latestTraining[0].trainedAt);
|
||||||
|
const daysSince = (Date.now() - lastTrainDate.getTime()) / (1000 * 60 * 60 * 24);
|
||||||
|
services.mlTraining.status = daysSince < 8 ? "running" : "idle";
|
||||||
|
services.mlTraining.healthy = daysSince < 8;
|
||||||
|
services.mlTraining.details = {
|
||||||
|
lastTraining: latestTraining[0].trainedAt,
|
||||||
|
daysSinceLastTraining: daysSince.toFixed(1),
|
||||||
|
lastStatus: latestTraining[0].status,
|
||||||
|
lastModel: latestTraining[0].modelVersion,
|
||||||
|
recordsProcessed: latestTraining[0].recordsProcessed,
|
||||||
|
interval: "settimanale"
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
services.mlTraining.status = "idle";
|
||||||
|
services.mlTraining.details = { lastTraining: "Mai", interval: "settimanale" };
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
services.mlTraining.status = "error";
|
||||||
|
services.mlTraining.details = { error: error.message };
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ services });
|
res.json({ services });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
res.status(500).json({ error: "Failed to check services status" });
|
res.status(500).json({ error: "Failed to check services status" });
|
||||||
@ -847,7 +989,11 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Service Control Endpoints (Secured - only allow specific systemd operations)
|
// Service Control Endpoints (Secured - only allow specific systemd operations)
|
||||||
const ALLOWED_SERVICES = ["ids-ml-backend", "ids-syslog-parser"];
|
const ALLOWED_SERVICES = [
|
||||||
|
"ids-ml-backend", "ids-syslog-parser", "ids-backend",
|
||||||
|
"ids-analytics-aggregator", "ids-auto-block", "ids-cleanup",
|
||||||
|
"ids-list-fetcher", "ids-ml-training"
|
||||||
|
];
|
||||||
const ALLOWED_ACTIONS = ["start", "stop", "restart", "status"];
|
const ALLOWED_ACTIONS = ["start", "stop", "restart", "status"];
|
||||||
|
|
||||||
app.post("/api/services/:service/:action", async (req, res) => {
|
app.post("/api/services/:service/:action", async (req, res) => {
|
||||||
|
|||||||
136
version.json
136
version.json
@ -1,7 +1,73 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.111",
|
"version": "1.0.122",
|
||||||
"lastUpdate": "2026-02-16T10:51:45.634Z",
|
"lastUpdate": "2026-02-17T09:13:40.571Z",
|
||||||
"changelog": [
|
"changelog": [
|
||||||
|
{
|
||||||
|
"version": "1.0.122",
|
||||||
|
"date": "2026-02-17",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.122"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.121",
|
||||||
|
"date": "2026-02-17",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.121"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.120",
|
||||||
|
"date": "2026-02-17",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.120"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.119",
|
||||||
|
"date": "2026-02-17",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.119"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.118",
|
||||||
|
"date": "2026-02-16",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.118"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.117",
|
||||||
|
"date": "2026-02-16",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.117"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.116",
|
||||||
|
"date": "2026-02-16",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.116"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.115",
|
||||||
|
"date": "2026-02-16",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.115"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.114",
|
||||||
|
"date": "2026-02-16",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.114"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.113",
|
||||||
|
"date": "2026-02-16",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.113"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.112",
|
||||||
|
"date": "2026-02-16",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.112"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.0.111",
|
"version": "1.0.111",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@ -235,72 +301,6 @@
|
|||||||
"date": "2025-11-25",
|
"date": "2025-11-25",
|
||||||
"type": "patch",
|
"type": "patch",
|
||||||
"description": "Deployment automatico v1.0.73"
|
"description": "Deployment automatico v1.0.73"
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.72",
|
|
||||||
"date": "2025-11-25",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.72"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.71",
|
|
||||||
"date": "2025-11-25",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.71"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.70",
|
|
||||||
"date": "2025-11-25",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.70"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.69",
|
|
||||||
"date": "2025-11-25",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.69"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.68",
|
|
||||||
"date": "2025-11-24",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.68"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.67",
|
|
||||||
"date": "2025-11-24",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.67"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.66",
|
|
||||||
"date": "2025-11-24",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.66"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.65",
|
|
||||||
"date": "2025-11-24",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.65"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.64",
|
|
||||||
"date": "2025-11-24",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.63",
|
|
||||||
"date": "2025-11-24",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.63"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.62",
|
|
||||||
"date": "2025-11-24",
|
|
||||||
"type": "patch",
|
|
||||||
"description": "Deployment automatico v1.0.62"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user