Add ability to manually unblock IPs and improve API key handling

Add a "Unblock Router" button to the Detections page and integrate ML backend API key for authenticated requests.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 3f5fe7aa-6fa1-4aa6-a5b4-916f113bf5df
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/449cf7c4-c97a-45ae-8234-e5c5b8d6a84f/7a657272-55ba-4a79-9a2e-f1ed9bc7a528/C6BdLIt
This commit is contained in:
marco370 2026-01-02 15:50:17 +00:00
parent 16d13d6bee
commit dee64495cd
3 changed files with 60 additions and 8 deletions

View File

@ -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 <Badge variant="destructive">CRITICO</Badge>;
@ -310,6 +333,20 @@ export default function Detections() {
Whitelist
</Button>
)}
{detection.blocked && (
<Button
variant="outline"
size="sm"
onClick={() => unblockMutation.mutate(detection)}
disabled={unblockMutation.isPending}
className="w-full"
data-testid={`button-unblock-${detection.id}`}
>
<Unlock className="h-3 w-3 mr-1" />
Sblocca Router
</Button>
)}
</div>
</div>
</div>

View File

@ -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.

View File

@ -133,19 +133,26 @@ export async function registerRoutes(app: Express): Promise<Server> {
// 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<string, string> = { '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<Server> {
}
const mlBackendUrl = process.env.ML_BACKEND_URL || 'http://localhost:8000';
const mlApiKey = process.env.IDS_API_KEY;
const headers: Record<string, string> = { '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);