diff --git a/client/src/pages/Detections.tsx b/client/src/pages/Detections.tsx index a6ab5a4..2c1efae 100644 --- a/client/src/pages/Detections.tsx +++ b/client/src/pages/Detections.tsx @@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Slider } from "@/components/ui/slider"; -import { AlertTriangle, Search, Shield, Globe, MapPin, Building2, ShieldPlus, ShieldCheck } from "lucide-react"; +import { AlertTriangle, Search, Shield, Globe, MapPin, Building2, ShieldPlus, ShieldCheck, Unlock } from "lucide-react"; import { format } from "date-fns"; import { useState } from "react"; import type { Detection, Whitelist } from "@shared/schema"; @@ -63,7 +63,7 @@ export default function Detections() { onSuccess: (_, detection) => { toast({ title: "IP aggiunto alla whitelist", - description: `${detection.sourceIp} è stato aggiunto alla whitelist con successo.`, + description: `${detection.sourceIp} è stato aggiunto alla whitelist e sbloccato dai router.`, }); queryClient.invalidateQueries({ queryKey: ["/api/whitelist"] }); queryClient.invalidateQueries({ queryKey: ["/api/detections"] }); @@ -77,6 +77,29 @@ export default function Detections() { } }); + // Mutation per sbloccare IP dai router + const unblockMutation = useMutation({ + mutationFn: async (detection: Detection) => { + return await apiRequest("POST", "/api/unblock-ip", { + ipAddress: detection.sourceIp + }); + }, + onSuccess: (data: any, detection) => { + toast({ + title: "IP sbloccato", + description: `${detection.sourceIp} è stato rimosso dalla blocklist di ${data.unblocked_from || 0} router.`, + }); + queryClient.invalidateQueries({ queryKey: ["/api/detections"] }); + }, + onError: (error: any, detection) => { + toast({ + title: "Errore sblocco", + description: error.message || `Impossibile sbloccare ${detection.sourceIp} dai router.`, + variant: "destructive", + }); + } + }); + const getRiskBadge = (riskScore: string) => { const score = parseFloat(riskScore); if (score >= 85) return CRITICO; @@ -310,6 +333,20 @@ export default function Detections() { Whitelist )} + + {detection.blocked && ( + + )} diff --git a/replit.md b/replit.md index f9c1fa7..ee9b2b8 100644 --- a/replit.md +++ b/replit.md @@ -25,7 +25,7 @@ The IDS employs a React-based frontend for real-time monitoring, detection visua **Key Architectural Decisions & Features:** - **Log Collection & Processing**: MikroTik syslog data (UDP:514) is parsed by `syslog_parser.py` and stored in PostgreSQL with a 3-day retention policy. The parser includes auto-reconnect and error recovery mechanisms. - **Machine Learning**: An Isolation Forest model (sklearn.IsolectionForest) trained on 25 network log features performs real-time anomaly detection, assigning a risk score (0-100 across five risk levels). A hybrid ML detector (Isolation Forest + Ensemble Classifier with weighted voting) reduces false positives. The system supports weekly automatic retraining of models. -- **Automated Blocking**: Critical IPs (score >= 80) are automatically blocked in parallel across configured MikroTik routers via their REST API. +- **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. - **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. diff --git a/server/routes.ts b/server/routes.ts index 3c8ce6e..a41ec82 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -133,19 +133,26 @@ export async function registerRoutes(app: Express): Promise { // 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 { + const headers: Record = { 'Content-Type': 'application/json' }; + if (mlApiKey) { + headers['X-API-Key'] = mlApiKey; + } const unblockResponse = await fetch(`${mlBackendUrl}/unblock-ip`, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + 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) { // Don't fail if ML backend is unavailable - console.log(`[WHITELIST] ML backend unavailable for auto-unblock: ${unblockError}`); + console.warn(`[WHITELIST] ML backend unavailable for auto-unblock: ${unblockError}`); } res.json(item); @@ -164,18 +171,26 @@ export async function registerRoutes(app: Express): Promise { } const mlBackendUrl = process.env.ML_BACKEND_URL || 'http://localhost:8000'; + const mlApiKey = process.env.IDS_API_KEY; + const headers: Record = { 'Content-Type': 'application/json' }; + if (mlApiKey) { + headers['X-API-Key'] = mlApiKey; + } + const response = await fetch(`${mlBackendUrl}/unblock-ip`, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers, body: JSON.stringify({ ip_address: ipAddress, list_name: listName }) }); if (!response.ok) { - const error = await response.text(); - return res.status(response.status).json({ error: error || "Failed to unblock IP" }); + 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) { console.error('[UNBLOCK] Error:', error);