Files
ja4-platform/services/dashboard/backend/main.py
toto 9f3e0621e5 feat: split ClickHouse into dual configurable databases (ja4_logs / ja4_processing)
Architecture:
- ja4_logs: raw log ingestion (http_logs_raw, http_logs, mv_http_logs)
- ja4_processing: analytics, aggregation, ML, dictionaries, audit

Configuration (env vars):
- CLICKHOUSE_DB_LOGS (default: ja4_logs)
- CLICKHOUSE_DB_PROCESSING (default: ja4_processing)

Changes:
- SQL migrations (10 files): all mabase_prod refs → ja4_logs or ja4_processing
  with correct cross-database references (MVs, views, dicts)
- deploy_schema.sh: substitutes DB names from env vars at deploy time
- Python shared settings: added CLICKHOUSE_DB_LOGS + CLICKHOUSE_DB_PROCESSING
- Dashboard routes (19 files): replaced ~80 hardcoded mabase_prod refs
  with settings.CLICKHOUSE_DB_LOGS / settings.CLICKHOUSE_DB_PROCESSING
- Bot-detector: DB → CLICKHOUSE_DB_PROCESSING, fetch_rules.py configurable
- Correlator: DSN example updated to ja4_logs
- Docker-compose + .env files: new env vars with defaults
- All documentation updated (14 markdown files)

All tests pass: sentinel 10/10, correlator 67.1%, bot-detector 11, dashboard 20, ja4_common 18

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-07 19:10:35 +02:00

238 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Bot Detector Dashboard - API Backend
FastAPI application pour servir le dashboard web
"""
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import os
from .config import settings
from .database import db
from .routes import metrics, detections, variability, attributes, analysis, entities, incidents, audit, reputation, fingerprints
from .routes import bruteforce, tcp_spoofing, header_fingerprint, heatmap, botnets, rotation, ml_features, investigation_summary, search, clustering
# Configuration logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Gestion du cycle de vie de l'application"""
# Startup
logger.info("Démarrage du Bot Detector Dashboard API...")
logger.info(f"ClickHouse: {settings.CLICKHOUSE_HOST}:{settings.CLICKHOUSE_PORT}")
logger.info(f"Database: {settings.CLICKHOUSE_DB}")
# Tester la connexion ClickHouse
try:
client = db.connect()
client.ping()
logger.info("Connexion ClickHouse établie avec succès")
except Exception as e:
logger.error(f"Échec de connexion ClickHouse: {e}")
raise
yield
# Shutdown
logger.info("Arrêt du Bot Detector Dashboard API...")
db.close()
# Création de l'application FastAPI
OPENAPI_TAGS = [
{
"name": "Metrics",
"description": "Métriques globales : comptages, niveaux de menace, baseline et distribution des scores ML.",
},
{
"name": "Detections",
"description": "Liste paginée et filtrée des anomalies détectées par le modèle ML. Supporte tri, recherche texte et regroupement par IP.",
},
{
"name": "investigation",
"description": (
"**Point d'entrée principal pour l'analyse d'une IP.** "
"Agrège en un seul appel : score ML, brute-force, spoofing TCP, rotation JA4, persistance et timeline 24h. "
"Retourne un `risk_score` heuristique de 0 à 100."
),
},
{
"name": "Reputation",
"description": "Réputation externe d'une IP via IP-API.com et IPinfo.io (sans clé API). Détecte proxies, VPN, Tor, hébergeurs.",
},
{
"name": "Analysis",
"description": "Analyses approfondies par IP : subnet, pays, empreintes JA4, user-agents, recommandation SOC et gestion des classifications.",
},
{
"name": "Entities",
"description": "Investigation par entité (IP, JA4, subnet, user-agent, host). Retourne détections associées, user-agents, chemins, paramètres et entités liées.",
},
{
"name": "Incidents",
"description": "Clusters d'incidents actifs regroupés par similarité comportementale. Permet la classification et le suivi des incidents.",
},
{
"name": "Fingerprints",
"description": "Analyse des empreintes JA4/TLS : spoofing, matrice JA4↔UA, user-agents suspects, cohérence par IP, JA4 légitimes et corrélation ASN.",
},
{
"name": "Bruteforce",
"description": "Détection des attaques brute-force : cibles, attaquants, timeline et détail par host.",
},
{
"name": "TCP Spoofing",
"description": "Détection du spoofing TCP/OS fingerprinting : vue d'ensemble, liste et matrice TTL×MSS.",
},
{
"name": "Header Fingerprint",
"description": "Clusters de fingerprints d'en-têtes HTTP suspects et IPs associées.",
},
{
"name": "Heatmap",
"description": "Heatmap horaire du trafic, top hosts et matrice activité/heure.",
},
{
"name": "Botnets",
"description": "Détection de botnets : spread JA4, distribution géographique par JA4, résumé global.",
},
{
"name": "Rotation",
"description": "Détection de la rotation JA4 (évasion de détection), menaces persistantes, historique JA4 par IP et score de sophistication.",
},
{
"name": "ML Features",
"description": "Données brutes du modèle ML : top anomalies, radar par IP, distribution des scores, tendances, features B et scatter plot.",
},
{
"name": "Attributes",
"description": "Listes des valeurs distinctes d'attributs (JA4, user-agents, ASN, pays…) avec comptages.",
},
{
"name": "Variability",
"description": "Variabilité comportementale : IPs par attribut, attributs par valeur, analyse des user-agents.",
},
{
"name": "Clustering",
"description": "Clustering K-Means des IPs sur les features ML. Statut du cache, clusters, points et IPs par cluster.",
},
{
"name": "Search",
"description": "Recherche rapide cross-entités (IP, JA4, host, user-agent, pays, ASN).",
},
{
"name": "Audit",
"description": "Journal d'audit SOC : création de logs, consultation filtrée, statistiques et activité par utilisateur.",
},
]
app = FastAPI(
title="Bot Detector Dashboard API",
description=(
"API REST du **Bot Detector SOC Dashboard**.\n\n"
"Permet d'interroger les bases ClickHouse (`ja4_logs` / `ja4_processing`) pour visualiser et analyser "
"les détections de bots générées par le service `bot_detector_ai`.\n\n"
"**Endpoint clé :** `GET /api/investigation/{ip}/summary` — synthèse complète en un appel.\n\n"
"Documentation interactive : `/docs` (Swagger UI) · `/redoc` (ReDoc)"
),
version="1.0.0",
openapi_tags=OPENAPI_TAGS,
lifespan=lifespan,
)
# Configuration CORS
app.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Enregistrement des routes
app.include_router(metrics.router)
app.include_router(detections.router)
app.include_router(variability.router)
app.include_router(attributes.router)
app.include_router(analysis.router)
app.include_router(entities.router)
app.include_router(incidents.router)
app.include_router(audit.router)
app.include_router(reputation.router)
app.include_router(fingerprints.router)
app.include_router(bruteforce.router)
app.include_router(tcp_spoofing.router)
app.include_router(header_fingerprint.router)
app.include_router(heatmap.router)
app.include_router(botnets.router)
app.include_router(rotation.router)
app.include_router(ml_features.router)
app.include_router(investigation_summary.router)
app.include_router(search.router)
app.include_router(clustering.router)
# Chemin vers le fichier index.html du frontend (utilisé par serve_frontend et serve_spa)
_FRONTEND_INDEX = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist", "index.html")
# Route pour servir le frontend
@app.get("/")
async def serve_frontend():
"""Sert l'application React"""
if os.path.exists(_FRONTEND_INDEX):
return FileResponse(_FRONTEND_INDEX)
return {"message": "Dashboard API - Frontend non construit. Voir /docs pour l'API."}
# Servir les assets statiques
_assets_path = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist", "assets")
if os.path.exists(_assets_path):
try:
app.mount("/assets", StaticFiles(directory=_assets_path), name="assets")
except Exception as _e:
logger.warning(f"Impossible de monter les assets statiques : {_e}")
# Health check
@app.get("/health")
async def health_check():
"""Endpoint de santé pour le health check Docker"""
try:
db.connect().ping()
return {"status": "healthy", "clickhouse": "connected"}
except Exception as e:
return {"status": "unhealthy", "clickhouse": "disconnected", "error": str(e)}
# Route catch-all pour le routing SPA (React Router) - DOIT ÊTRE EN DERNIER
# Sauf pour /api/* qui doit être géré par les routers
@app.get("/{full_path:path}")
async def serve_spa(full_path: str):
"""Redirige toutes les routes vers index.html pour le routing React"""
# Ne pas intercepter les routes API
if full_path.startswith("api/"):
raise HTTPException(status_code=404, detail="API endpoint not found")
if os.path.exists(_FRONTEND_INDEX):
return FileResponse(_FRONTEND_INDEX)
return {"message": "Dashboard API - Frontend non construit"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host=settings.API_HOST,
port=settings.API_PORT,
reload=True
)