- bot-detector.md : architecture 11 modules, 77/65 features, ensemble triple voix (EIF+AE+XGBoost), browser 5 axes, HDBSCAN, toutes les variables d'environnement vérifiées depuis le code source - dashboard.md : corrigé stack (Jinja2+htmx, pas React+Vite), 14 pages + 35 API routes + health, dual-database, IPv4/IPv6 - python-ja4common.md : ajouté CLICKHOUSE_DB_PROCESSING/LOGS, schéma dual-database, note dashboard n'utilise pas ja4_common Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
25 KiB
Bot Detector
Service Python de détection d'anomalies par apprentissage automatique semi-supervisé sur le trafic HTTP/TLS agrégé dans ClickHouse. Fonctionne en cycle continu (par défaut toutes les 5 minutes) avec un ensemble à triple voix (Extended Isolation Forest + Autoencoder + XGBoost), enrichi par l'explicabilité SHAP, le clustering HDBSCAN et la détection multifactorielle des navigateurs.
Architecture des modules
Le service est découpé en 11 modules organisés ainsi :
__main__.py Point d'entrée (python -m bot_detector)
└─ cycle.py Boucle principale : requête ClickHouse → pipeline → insertion
├─ config.py Variables d'environnement, flags de disponibilité
├─ log.py Journalisation structurée JSON (structlog + RotatingFileHandler)
├─ infra.py Client ClickHouse (via ja4_common), health check HTTP, arrêt propre
├─ preprocessing.py Nettoyage du DataFrame, imputation, listes de features
│ └─ browser.py Identification multifactorielle des navigateurs (5 axes)
├─ pipeline.py Orchestration : filtrage → entraînement → scoring → fusion
│ ├─ models.py EIF, TrafficAutoEncoder (PyTorch), XGBoost
│ └─ scoring.py Normalisation, seuil adaptatif, SHAP, HDBSCAN, dérive
└─ (insère dans ml_all_scores + ml_detected_anomalies)
| Module | Lignes | Rôle |
|---|---|---|
config.py |
154 | Toute la configuration via os.getenv(), flags de disponibilité des librairies |
log.py |
65 | log_info(), log_decision(), append_training_history() — JSONL rotatif |
infra.py |
89 | Client ClickHouse (délègue à ja4_common), score_to_threat_level(), serveur de santé en thread daemon |
browser.py |
170 | Détection multifactorielle des navigateurs sur 5 axes pondérés |
scoring.py |
279 | Normalisation, seuil adaptatif, SHAP top-3, HDBSCAN, détection de dérive |
models.py |
478 | TrafficAutoEncoder, entraînement/chargement EIF, XGBoost, élagage de features |
preprocessing.py |
117 | preprocess_df() — nettoyage, typage, imputation, listes FEATURES / FEATURES_COMPLET |
pipeline.py |
378 | run_semi_supervised_logic() — orchestration complète d'un modèle |
cycle.py |
371 | fetch_and_analyze() — boucle principale, feedback SOC, multiwindow |
__main__.py |
41 | Point d'entrée, bannière de démarrage, boucle while True |
__init__.py |
1 | Docstring du package |
Configuration
Toute la configuration est lue via os.getenv() dans config.py. Aucun fichier YAML ni pydantic-settings.
Connexion ClickHouse
| Variable | Type | Défaut | Description |
|---|---|---|---|
CLICKHOUSE_HOST |
str | clickhouse |
Nom d'hôte du serveur ClickHouse (via ja4_common) |
CLICKHOUSE_PORT |
int | 8123 |
Port HTTP ClickHouse (via ja4_common) |
CLICKHOUSE_USER |
str | admin |
Utilisateur ClickHouse (via ja4_common) |
CLICKHOUSE_PASSWORD |
str | "" |
Mot de passe ClickHouse (via ja4_common) |
CLICKHOUSE_DB_PROCESSING |
str | ja4_processing |
Base de données ML/agrégations (fallback : CLICKHOUSE_DB) |
CLICKHOUSE_DB_LOGS |
str | ja4_logs |
Base de données des logs HTTP |
Modèle et entraînement
| Variable | Type | Défaut | Description |
|---|---|---|---|
N_ESTIMATORS |
int | 300 |
Nombre d'arbres pour l'Extended Isolation Forest |
ISOLATION_CONTAMINATION |
float | 0.001 |
Paramètre de contamination EIF (plage ]0, 0.5[) |
ANOMALY_THRESHOLD |
float | -0.05 |
Seuil de score brut pour la détection d'anomalie |
ANOMALY_PERCENTILE |
int | 5 |
Percentile pour le seuil adaptatif |
MODEL_DIR |
str | /var/lib/bot_detector |
Répertoire de persistance des modèles |
MODEL_HISTORY_COUNT |
int | 10 |
Nombre de versions de modèle conservées |
RETRAIN_INTERVAL_HOURS |
int | 24 |
Intervalle de réentraînement EIF (heures) |
DRIFT_THRESHOLD |
float | 0.30 |
Seuil de dérive KS (fraction de features driftées) |
MIN_VALID_FEATURE_RATIO |
float | 0.50 |
Ratio minimal de features valides pour entraîner |
PRUNE_VARIANCE_THRESHOLD |
float | 1e-6 |
Seuil de variance pour l'élagage de features |
VAL_ANOMALY_GATE |
float | 0.20 |
Garde-fou : taux maximum d'anomalies en validation |
Autoencoder
| Variable | Type | Défaut | Description |
|---|---|---|---|
AE_WEIGHT |
float | 0.30 |
Poids de l'Autoencoder dans le score combiné (plage ]0, 1[) |
AE_EPOCHS |
int | 50 |
Nombre d'époques d'entraînement |
AE_LATENT_DIM |
int | 16 |
Dimension de l'espace latent |
AE_LEARNING_RATE |
float | 1e-3 |
Taux d'apprentissage Adam |
XGBoost
| Variable | Type | Défaut | Description |
|---|---|---|---|
XGB_WEIGHT |
float | 0.20 |
Poids de XGBoost dans le score final (plage ]0, 1[) |
XGB_MIN_LABELS |
int | 100 |
Nombre minimal de labels SOC pour activer XGBoost |
XGB_RETRAIN_INTERVAL_HOURS |
int | 168 |
Intervalle de réentraînement XGBoost (7 jours) |
Détection navigateur
| Variable | Type | Défaut | Description |
|---|---|---|---|
BROWSER_CONFIDENCE_THRESHOLD |
float | 0.55 |
Confiance minimale pour classifier LEGITIMATE_BROWSER |
BROWSER_COHORT_RATIO |
float | 0.70 |
Si ≥ 70 % des sessions d'un JA4 sont navigateur → propagation |
Clustering et explicabilité
| Variable | Type | Défaut | Description |
|---|---|---|---|
ENABLE_CLUSTERING |
bool | true |
Activer le clustering HDBSCAN |
CLUSTERING_MIN_SAMPLES |
int | 3 |
Taille minimale de cluster |
ENABLE_SHAP |
bool | true |
Activer l'explicabilité SHAP (requiert shap installé) |
Cycle et opérations
| Variable | Type | Défaut | Description |
|---|---|---|---|
CYCLE_INTERVAL_SEC |
int | 300 |
Intervalle entre les cycles (secondes) |
MAX_CONSECUTIVE_FAILURES |
int | 3 |
Échecs consécutifs avant healthy=False |
DEDUP_TTL_MIN |
int | 60 |
TTL de déduplication inter-cycle (minutes) |
RECURRENCE_WEIGHT |
float | 0.005 |
Poids de la récurrence dans le score brut |
ENABLE_MULTIWINDOW |
bool | false |
Activer l'analyse multi-fenêtre 24h |
MULTIWINDOW_VIEW |
str | view_ai_features_24h |
Vue ClickHouse pour le mode multi-fenêtre |
ENABLE_FEEDBACK |
bool | true |
Activer l'intégration du feedback SOC |
FEEDBACK_WINDOW_DAYS |
int | 7 |
Fenêtre de feedback SOC (jours) |
Journalisation et santé
| Variable | Type | Défaut | Description |
|---|---|---|---|
BOT_DETECTOR_LOG |
str | /var/log/bot_detector/decisions.jsonl |
Chemin du fichier de décisions |
LOG_BACKUP_COUNT |
int | 7 |
Nombre de fichiers de rotation conservés |
HEALTH_PORT |
int | 8080 |
Port du serveur de santé HTTP |
Pipeline ML — Ensemble à triple voix
Vue d'ensemble
Le bot-detector utilise trois modèles en parallèle, combinés par une pondération configurable :
┌──────────────────────┐
│ Extended Isolation │
│ Forest (isotree) │──→ eif_norm (0–1)
└──────────────────────┘ │
│ × (1 − AE_WEIGHT)
┌──────────────────────┐ │
│ TrafficAutoEncoder │ ├──→ combined_norm
│ (PyTorch) │──→ ae_norm (0–1)
└──────────────────────┘ × AE_WEIGHT
│ × (1 − XGB_WEIGHT)
┌──────────────────────┐ │
│ XGBoost │ ├──→ anomaly_score
│ (supervisé, labels │──→ xgb_prob (0–1)
│ SOC) │ × XGB_WEIGHT
└──────────────────────┘
Formule du score final :
combined_norm = (1 − AE_WEIGHT) × eif_norm + AE_WEIGHT × ae_norm
anomaly_score = (1 − XGB_WEIGHT) × combined_norm + XGB_WEIGHT × xgb_prob
Avec les poids par défaut (AE_WEIGHT=0.30, XGB_WEIGHT=0.20) :
anomaly_score = 0.56 × eif_norm + 0.24 × ae_norm + 0.20 × xgb_prob
Architecture duale
Deux modèles indépendants tournent sur chaque cycle :
| Modèle | Condition | Features | Données |
|---|---|---|---|
| Complet | correlated = 1 |
77 (FEATURES_COMPLET) |
HTTP + TCP + TLS (L3→L7) |
| Applicatif | correlated = 0 |
65 (FEATURES) |
HTTP seul (L7 pur) |
En mode multi-fenêtre (ENABLE_MULTIWINDOW=true), deux variantes supplémentaires sont exécutées sur la vue 24h : Complet_24h et Applicatif_24h.
Extended Isolation Forest (EIF)
Modèle principal non supervisé. Utilise isotree.IsolationForest :
isotree.IsolationForest(
ntrees=300, # N_ESTIMATORS
ndim=min(3, n_features),
sample_size='auto',
missing_action='impute',
random_seed=42,
nthreads=-1,
)
Fallback si isotree n'est pas disponible : sklearn.ensemble.IsolationForest(n_estimators=300, contamination=CONTAMINATION).
Calibration : le score isotree brut (∈ [0, 1], >0.5 = anomalous) est converti en convention sklearn : sklearn_equiv = 0.5 − isotree_score.
TrafficAutoEncoder (PyTorch)
Architecture symétrique encodeur-décodeur :
Encodeur : n_features → dim1 → dim2 → 16 (latent)
Décodeur : 16 → dim2 → dim1 → n_features
dim1 = min(64, max(n_features, latent_dim + 4))
dim2 = min(32, max(dim1 // 2, latent_dim + 2))
- Activations :
ReLU+BatchNorm1dsur les couches cachées,Sigmoiden sortie du décodeur - Optimiseur :
Adam(lr=1e-3, weight_decay=1e-5) - Perte :
MSELoss - Entraînement : 50 époques, batch_size=256
- Score : erreur de reconstruction MSE par échantillon
- Normalisation des entrées : min-max [0, 1] par feature
XGBoost (supervisé)
Entraîné sur les labels issus du feedback SOC (table soc_feedback) :
xgb.XGBClassifier(
n_estimators=200,
max_depth=6,
learning_rate=0.1,
scale_pos_weight=auto, # max(1, n_neg / n_pos)
eval_metric='logloss',
tree_method='hist',
random_state=42,
n_jobs=-1,
)
- Labels positifs (bot) :
HIGH,CRITICAL,ANUBIS_DENY,KNOWN_BOT - Labels négatifs (légitime) :
NORMAL,LEGITIMATE_BROWSER - Activation requiert ≥
XGB_MIN_LABELS(100) labels - Réentraînement tous les
XGB_RETRAIN_INTERVAL_HOURS(168h = 7 jours)
Détection multifactorielle des navigateurs
Module browser.py — classifie chaque session sur 5 axes pondérés :
| Axe | Clé | Poids | Composantes |
|---|---|---|---|
| 1 — JA4 connu | axis_ja4_known |
0.25 | Famille navigateur identifiée dans dict_browser_ja4 → 1.0, sinon 0.0 |
| 2 — Structure JA4 | axis_ja4_struct |
0.15 | TLS 1.3 (×0.35), h2/h3 (×0.25), nb ciphers 10–25 (×0.20), nb extensions 10–25 (×0.20) |
| 3 — HTTP moderne | axis_http_modern |
0.25 | modern_browser_score ≥ 50 (×0.35), Accept-Language (×0.20), Sec-Fetch < 0.3 (×0.25), generic_accept < 0.3 (×0.10), pas de ua_ch_mismatch (×0.10) |
| 4 — Comportement navigation | axis_nav_behavior |
0.15 | has_cookie (×0.25), has_referer (×0.25), asset_ratio > 0.15 (×0.25), direct_access < 0.5 (×0.25) |
| 5 — Cohérence TLS/TCP | axis_tls_coherence |
0.20 | Pas d'alpn_mismatch (×0.25), window_scale OK (×0.20), tls12 < 0.1 (×0.20), pas d'http10 (×0.15), ALPN présent (×0.20) |
Seuil : browser_confidence ≥ 0.55 + famille identifiée → LEGITIMATE_BROWSER
Propagation par cohorte : si ≥ 70 % des sessions partageant un JA4 sont classées navigateur, les sessions NORMAL/LOW restantes avec le même JA4 sont aussi classées LEGITIMATE_BROWSER.
Inférence de famille : pour les JA4 inconnus, correspondance structurelle avec les profils _BROWSER_JA4_PROFILES (Chromium, Firefox, Safari, Tor_Browser) — requiert browser_confidence ≥ 0.45.
Scoring et normalisation
Normalisation des scores (normalize_scores)
Les scores bruts (négatifs = anomalous) sont mappés vers [0, 1] avec 1 = le plus anomalous :
result[mask] = clip(−scores / (−s_min + 1e-9), 0, 1)
Les scores ≥ 0 sont mis à 0.
Seuil adaptatif (compute_adaptive_threshold)
threshold = min(percentile(scores_négatifs, ANOMALY_PERCENTILE), ANOMALY_THRESHOLD)
Avec ANOMALY_PERCENTILE=5 et ANOMALY_THRESHOLD=-0.05.
Pénalité de récurrence
Appliquée au raw_anomaly_score :
raw_anomaly_score −= log1p(recurrence_count) × RECURRENCE_WEIGHT
Niveaux de menace
| Plage de score brut | Niveau | Interprétation |
|---|---|---|
< −0.30 |
CRITICAL | Comportement extrêmement anomalous |
< −0.15 |
HIGH | Signal d'anomalie fort |
< −0.05 |
MEDIUM | Anomalie modérée |
< 0 |
LOW | Légèrement inhabituel |
≥ 0 |
NORMAL | Trafic normal |
Clustering HDBSCAN des campagnes
Lorsque ENABLE_CLUSTERING=true, les anomalies sont regroupées en campagnes par HDBSCAN :
hdbscan.HDBSCAN(
min_cluster_size=CLUSTERING_MIN_SAMPLES, # défaut : 3
min_samples=max(2, CLUSTERING_MIN_SAMPLES − 1), # défaut : 2
cluster_selection_method='eom',
)
Espace de clustering : si un Autoencoder est disponible, le clustering s'effectue dans l'espace latent 16-dim de l'AE. Sinon, StandardScaler est appliqué sur les features brutes.
Fallback si hdbscan n'est pas disponible : DBSCAN(eps=0.5, min_samples=CLUSTERING_MIN_SAMPLES).
Chaque anomalie reçoit un campaign_id (−1 = pas de cluster).
Liste des features
Features communes — modèle Applicatif (65 features)
Comportement HTTP
| Feature | Description |
|---|---|
hits |
Nombre de requêtes dans la fenêtre |
hit_velocity |
Requêtes par seconde |
fuzzing_index |
Score de diversité chemins/paramètres |
post_ratio |
Fraction de requêtes POST |
port_exhaustion_ratio |
Fraction de ports source distincts / total |
orphan_ratio |
Requêtes sans corrélation TLS |
head_ratio |
Fraction de requêtes HEAD |
http10_ratio |
Fraction de requêtes HTTP/1.0 |
generic_accept_ratio |
Fraction d'en-têtes Accept courts |
sec_fetch_absence_rate |
Fraction sans Sec-Fetch-Site |
missing_accept_enc_ratio |
Fraction sans Accept-Encoding |
http_scheme_ratio |
Fraction utilisant HTTP (pas HTTPS) |
Gestion de connexion
| Feature | Description |
|---|---|
max_keepalives |
Max de requêtes sur une seule connexion Keep-Alive |
tcp_shared_count |
Connexions TCP partagées entre sessions |
multiplexing_efficiency |
Efficacité du multiplexage HTTP/2 |
Empreinte navigateur
| Feature | Description |
|---|---|
header_count |
Nombre d'en-têtes HTTP envoyés |
has_accept_language |
Présence de l'en-tête Accept-Language |
has_cookie |
Présence de l'en-tête Cookie |
has_referer |
Présence de l'en-tête Referer |
modern_browser_score |
Score composite de conformité navigateur (0–100) |
ua_ch_mismatch |
Incohérence User-Agent vs Client Hints |
ip_id_zero_ratio |
Paquets IP avec ID=0 (pile minimaliste/headless) |
header_order_shared_count |
IPs partageant le même ordre d'en-têtes |
header_order_confidence |
Entropie normalisée de l'ordre des en-têtes |
distinct_header_orders |
Ordres d'en-têtes distincts par IP |
is_fake_navigation |
Sec-Fetch-Mode=navigate avec destination non-document |
Comportement de navigation
| Feature | Description |
|---|---|
request_size_variance |
Variance de la taille des requêtes |
mss_mobile_mismatch |
Incohérence TCP MSS vs profil mobile |
asset_ratio |
Fraction de requêtes de ressources statiques |
direct_access_ratio |
Accès directs (sans referer) |
is_ua_rotating |
Rotation de User-Agent détectée |
distinct_ja4_count |
Empreintes JA4 distinctes par IP |
anomalous_payload_ratio |
Fraction de charges utiles anomalous |
Concentration et rareté
| Feature | Description |
|---|---|
src_port_density |
Entropie des ports source |
ja4_asn_concentration |
Concentration JA4 au sein de l'ASN |
ja4_country_concentration |
Concentration JA4 par pays |
is_rare_ja4 |
Empreinte JA4 rare (< 100 hits totaux) |
Temporel et diversité
| Feature | Description |
|---|---|
temporal_entropy |
Entropie de la distribution temporelle |
path_diversity_ratio |
Diversité des chemins URL |
url_depth_variance |
Variance de la profondeur des URL |
Anubis
| Feature | Description |
|---|---|
anubis_is_flagged |
Signal de suspicion Anubis (bot détecté, action ni ALLOW/DENY/vide) |
Navigateur multifactoriel
| Feature | Description |
|---|---|
is_known_browser |
JA4 correspond à un navigateur connu |
browser_consistency_score |
Score composite de cohérence navigateur |
browser_confidence |
Confiance globale de l'identification navigateur |
axis_ja4_known |
Score de l'axe 1 (JA4 connu) |
axis_ja4_struct |
Score de l'axe 2 (structure JA4) |
axis_http_modern |
Score de l'axe 3 (HTTP moderne) |
axis_nav_behavior |
Score de l'axe 4 (comportement navigation) |
axis_tls_coherence |
Score de l'axe 5 (cohérence TLS/TCP) |
Thèse §5 — Features avancées
| Feature | Description |
|---|---|
path_transition_entropy |
Entropie des transitions de chemins |
cadence_cv |
Coefficient de variation de la cadence de requêtes |
burst_ratio |
Fraction de requêtes en rafale |
pause_ratio |
Fraction de pauses longues |
lag1_autocorrelation |
Autocorrélation lag-1 des inter-arrivées |
benford_deviation |
Déviation par rapport à la loi de Benford |
host_diversity |
Diversité des hôtes ciblés |
host_sweep_speed |
Vitesse de balayage des hôtes |
host_coverage_uniformity |
Uniformité de couverture des hôtes |
Features TCP fenêtre
| Feature | Description |
|---|---|
true_window_size |
Taille réelle de la fenêtre TCP |
window_mss_ratio |
Ratio fenêtre TCP / MSS |
Features de détection avancées
| Feature | Description |
|---|---|
has_xff |
Présence de l'en-tête X-Forwarded-For |
unusual_content_type_ratio |
Fraction de Content-Type inhabituels |
non_standard_port_ratio |
Fraction de ports non standards |
login_post_concentration |
Concentration de POST sur les pages de login |
sec_ch_mobile_mismatch |
Incohérence Sec-CH-UA-Mobile |
Features supplémentaires — modèle Complet (+12 features TCP/TLS)
| Feature | Description |
|---|---|
tcp_jitter_variance |
Variance du jitter TCP inter-paquets |
alpn_http_mismatch |
Incohérence ALPN vs protocole HTTP réel |
is_alpn_missing |
ALPN absent dans le ClientHello |
sni_host_mismatch |
Incohérence TLS SNI vs en-tête Host HTTP |
ja3_diversity_ratio |
Ratio de diversité JA3 par IP |
syn_timing_cv |
Coefficient de variation du timing SYN |
tls12_ratio |
Fraction de connexions TLS 1.2 |
ip_df_variance |
Variance du flag Don't-Fragment IP |
avg_ttl |
TTL IP moyen (empreinte OS) |
ttl_std |
Écart-type du TTL |
no_window_scale_ratio |
Fraction sans TCP Window Scale |
ja4_drift_ratio |
Dérive JA4 intra-session (§5.5) |
Pipeline de détection
1. Requête view_ai_features_1h → DataFrame
2. Enrichissement optionnel view_thesis_features_1h (features thèse §5)
3. Prétraitement : preprocess_df() (nettoyage, browser axes, imputation)
4. Chargement du feedback SOC → reclassification
5. Chargement de la carte de récurrence (view_ip_recurrence)
6. Séparation par correlated = 1 / correlated = 0
7. Pour chaque modèle (Complet, Applicatif) :
a. Validation des features (exclure constantes/manquantes)
b. Séparation des bots connus → journalisation KNOWN_BOT
c. Filtrage de la baseline humaine (asn_label = 'human')
d. Chargement ou entraînement EIF + AE
e. Scoring du trafic inconnu (EIF + AE)
f. Chargement ou entraînement XGBoost (si labels disponibles)
g. Combinaison des scores (formule triple voix)
h. Normalisation [0, 1]
i. Seuil adaptatif
j. Pénalité de récurrence
k. SHAP (top-3 features)
l. HDBSCAN clustering → campaign_id
m. Détection de dérive (KS test)
8. Mode multi-fenêtre (si activé) : idem sur view_ai_features_24h
9. Insertion → ml_all_scores (toutes les sessions scorées)
10. Déduplication intra-cycle (garder raw_anomaly_score le plus bas par IP)
11. Déduplication inter-cycle (TTL, skip si détecté récemment sauf aggravation ≥ 0.05)
12. Insertion → ml_detected_anomalies (anomalies filtrées)
Détection de dérive (Kolmogorov-Smirnov)
Par feature, un test KS bilatéral compare la distribution courante avec la distribution d'entraînement (reconstruite par interpolation à partir d'un digest quantile p10/p25/p50/p75/p90) :
- Feature driftée si
p_value < 0.05 - Dérive globale = fraction de features driftées
- Si
drift > DRIFT_THRESHOLD(0.30) → réentraînement automatique
Fallback (sans scipy) : méthode Z-score — feature driftée si |moyenne_courante − moyenne_entraînement| / std_entraînement > 2.0.
Explicabilité SHAP
Lorsque ENABLE_SHAP=true et que la librairie shap est disponible :
- Calcul des valeurs SHAP via
TreeExplainersur le modèle EIF - Les 3 features les plus contributives sont stockées dans le champ
reason - Format :
"feature1=valeur (±shap), feature2=valeur (±shap), feature3=valeur (±shap)"
Enrichissement Anubis
La vue view_ai_features_1h enrichit chaque IP via les dictionnaires Anubis selon une cascade de priorité :
- UA + IP combinés (même
rule_id) — confiance maximale - UA seul (pas de condition IP)
- IP seul (pas de condition UA)
- Correspondance ASN
- Correspondance pays
Tables de sortie
ml_detected_anomalies
Détections d'anomalies au-dessus du seuil de menace. Engine : ReplacingMergeTree(detected_at), ORDER BY (src_ip, model_name), TTL 7 jours.
Colonnes clés : detected_at, src_ip, ja4, host, bot_name, anomaly_score, raw_anomaly_score, threat_level, model_name, recurrence, campaign_id, reason, anubis_bot_name, anubis_bot_action, anubis_bot_category, plus toutes les features ML.
ml_all_scores
Toutes les classifications (sans filtre de seuil) pour l'observabilité. Engine : ReplacingMergeTree(detected_at), ORDER BY (window_start, src_ip, ja4, host, model_name), TTL 7 jours.
Format du journal de décisions
Le fichier decisions.jsonl contient des entrées JSONL structurées :
{"event": "CYCLE_START", "cycle_id": "20260309T143000", "total": 5000, "human": 1500, "known_bot": 200, "correlated": 3000}
{"event": "ANOMALY", "src_ip": "203.0.113.42", "score": -0.25, "threat_level": "HIGH", "reason": "hit_velocity=45.2, fuzzing_index=0.8, ...", "campaign_id": 3}
{"event": "KNOWN_BOT", "src_ip": "198.51.100.10", "bot_name": "AhrefsBot"}
{"event": "CYCLE_END", "cycle_id": "20260309T143000", "anomalies": 15, "known_bots": 200, "duration_sec": 12.5}
Rotation des logs : 50 Mo max × LOG_BACKUP_COUNT sauvegardes (défaut : 7).
Point de santé
- URL :
GET http://localhost:8080/ - Réponses :
200 OK(corpsOK) ou503 Service Unavailable(corpsDEGRADED) - Exécuté dans un thread daemon au démarrage
- État mis à
FalseaprèsMAX_CONSECUTIVE_FAILURES(3) échecs consécutifs ClickHouse, remis àTruedès le premier succès
Persistance des modèles
| Fichier | Description |
|---|---|
model_<name>_<version>.joblib |
EIF sérialisé (joblib) |
model_<name>_<version>.meta.json |
Métadonnées (features, seuils, statistiques d'entraînement, digest quantile) |
model_<name>.current |
Pointeur vers la version active |
training_history.jsonl |
Historique d'entraînement |
Rotation automatique : seules les MODEL_HISTORY_COUNT dernières versions (défaut : 10) sont conservées.
Déploiement Docker
# Construction de l'image
make build-bot-detector
# Exécution avec docker-compose
cd services/bot-detector
docker-compose up -d
# Tests
make test-bot-detector
Volumes
| Chemin hôte | Chemin conteneur | Description |
|---|---|---|
./bot_detector_logs |
/var/log/bot_detector |
Journaux de décisions (JSONL) |
./bot_detector_models |
/var/lib/bot_detector |
Modèles ML persistés |
./reputation/data/user_files/bot_ip.csv |
/data/bot_ip.csv (ro) |
Liste d'IPs de bots connus |
./reputation/data/user_files/bot_ja4.csv |
/data/bot_ja4.csv (ro) |
Liste de JA4 de bots connus |
./reputation/data/user_files/asn_reputation.csv |
/data/asn_reputation.csv (ro) |
Labels de réputation ASN |