feat(e2e): add distributed E2E test framework with parametric traffic generation

Add run-e2e-test.sh with CLI parameters (--hits, --http-ratio, --dns, --tls,
--src-ips, --keep-analysis, --up) for configurable traffic generation. Traffic
runs from VM endpoints with multiple source IPs (alias IPs on eth0) to produce
distinct sessions for the ML pipeline. Fix curl TLS flags (--tlsv1.2 instead
of --tls-v1-2), skip redundant local verification in distributed mode, and
fix dashboard is_available() cache that never retried after ClickHouse recovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jacquin Antoine
2026-04-15 00:09:32 +02:00
parent 7894d39f1c
commit f88b739992
40 changed files with 2154 additions and 337 deletions

View File

@ -58,7 +58,7 @@ La deuxième couche de défense statique repose sur des dictionnaires de réputa
[Anubis](https://github.com/TecharoHQ/anubis) est un système de règles communautaire en YAML permettant de définir des actions granulaires par bot identifié. Les quatre actions disponibles sont :
- **ALLOW** : autorisation explicite (bots légitimes : Googlebot, Bingbot, bots de recherche académique)
- **DENY** : blocage avec retour 403 Forbidden signal de vérité terrain fort pour l'entraînement XGBoost
- **DENY** : blocage avec retour 403 Forbidden signal de vérité terrain fort pour l'entraînement HAT (River)
- **WEIGH** : ajout d'un score de pondération sans blocage signal auxiliaire dans le vecteur de features
- **CHALLENGE** : redirection vers un challenge (PoW ou CAPTCHA)
@ -69,7 +69,7 @@ La deuxième couche de défense statique repose sur des dictionnaires de réputa
**Priorité de correspondance** : `COALESCE(IP match, ASN match)` une correspondance CIDR précise sur l'IP prend la priorité sur la correspondance ASN plus générale. Cela reflète le principe que l'information la plus spécifique est la plus fiable.
**Valeur pour le pipeline ML** :
- Les sessions `DENY` fournissent des étiquettes de bot à haute confiance pour l'entraînement supervisé de XGBoost, sans nécessiter d'annotation manuelle.
- Les sessions `DENY` fournissent des étiquettes de bot à haute confiance pour l'entraînement incrémental du HAT (River), sans nécessiter d'annotation manuelle.
- Les sessions `WEIGH` contribuent une feature binaire `anubis_is_flagged` dans la famille F7, enrichissant le vecteur de features sans déclencher de blocage.
---
@ -292,7 +292,7 @@ XGBoost ([Chen & Guestrin, 2016](https://arxiv.org/abs/1603.02754)) est un algor
**Limites des approches supervisées** :
- **Concept drift** : un modèle entraîné sur des bots de 2024 peut être aveugle aux nouvelles techniques de 2025
- **Rareté des étiquettes** : annoter manuellement des millions de sessions HTTP est coûteux et sujet à erreur
- **Bruit des étiquettes** : les labels fournis par les analystes SOC contiennent des erreurs systématiques (faux positifs mal corrigés, biais de confirmation). Ces étiquettes bruitées empoisonnent le modèle supervisé un problème bien documenté par [Northcutt et al., 2021 (Cleanlab)](https://arxiv.org/abs/1911.00068) qui montre que les jeux de données réels contiennent 8 à 20 % de labels incorrects. Pour mitiger ce risque, notre pipeline intègre un filtre Cleanlab avant l'entraînement XGBoost (détail §3.8).
- **Bruit des étiquettes** : les labels fournis par les analystes SOC contiennent des erreurs systématiques (faux positifs mal corrigés, biais de confirmation). Ces étiquettes bruitées empoisonnent le modèle supervisé un problème bien documenté par [Northcutt et al., 2021 (Cleanlab)](https://arxiv.org/abs/1911.00068) qui montre que les jeux de données réels contiennent 8 à 20 % de labels incorrects. Pour mitiger ce risque, notre pipeline intègre un filtre Cleanlab avant l'apprentissage incrémental du HAT (détail §3.8).
- **Biais de jeu de données** : les modèles entraînés sur des données de laboratoire (CICIDS2017, NSL-KDD) généralisent mal au trafic en production, comme documenté dans la littérature sur les benchmarks de détection d'intrusions
- **Attaque par évasion adversariale** : un attaquant ayant accès ou connaissance du modèle peut crafting des sessions qui maximisent le score de légitimité
@ -413,20 +413,19 @@ Le système de détection combine trois « voix » complémentaires :
┌──────────────┼──────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌────▼──────┐
│ EIF │ │ NF │ │ XGBoost
│ (semi- │ │ (Normal- │ │(supervisé)
│supervisé) │ │ izing │ │
│ │ │ Flow) │ │
│ EIF │ │ NF │ │ HAT
│ (semi- │ │ (Normal- │ │ (River,
│supervisé) │ │ izing │ │ supervisé
│ │ │ Flow) │ │ online)
└─────┬─────┘ └─────┬─────┘ └────┬──────┘
│ │ │
eif_norm nf_norm xgb_prob
eif_norm nf_norm hat_prob
│ │ │
└──────────────┼──────────────┘
┌────────▼────────┐
Meta-Model
Stacking MLP
│ (non-linéaire) │
Fusion MLP
non-linéaire
└────────┬────────┘
┌────────▼────────┐
@ -437,7 +436,7 @@ Le système de détection combine trois « voix » complémentaires :
**Limites de la fusion linéaire**
Une fusion linéaire — combinaison convexe pondérée ou régression logistique — ne peut capturer que des frontières de décision linéaires dans l'espace des scores intermédiaires. Or les signaux EIF, NF et XGBoost peuvent exhiber des interactions non-linéaires impossibles à modéliser par une combinaison linéaire :
Une fusion linéaire — combinaison convexe pondérée ou régression logistique — ne peut capturer que des frontières de décision linéaires dans l'espace des scores intermédiaires. Or les signaux EIF, NF et HAT peuvent exhiber des interactions non-linéaires impossibles à modéliser par une combinaison linéaire :
```
Problème XOR des scores :
@ -450,18 +449,18 @@ Problème XOR des scores :
NF bas
```
Exemple concret : un bot utilisant un outilHeadless avec un JA4 fingerprint légitime (NF bas) mais un comportement de navigation atypique (EIF élevé). Le XGBoost peut compenser, mais la fusion linéaire ne peut apprendre la relation *« EIF élevé ET XGB élevé MAIS NF bas = bot »* — elle ne fait que sommer les contributions indépendantes.
Exemple concret : un bot utilisant un outilHeadless avec un JA4 fingerprint légitime (NF bas) mais un comportement de navigation atypique (EIF élevé). Le HAT peut compenser, mais la fusion linéaire ne peut apprendre la relation *« EIF élevé ET HAT élevé MAIS NF bas = bot »* — elle ne fait que sommer les contributions indépendantes.
**Stacking OOF (Out-of-Fold) et MLP méta-modèle**
Pour résoudre cette limitation, le système utilise un méta-modèle non-linéaire de type MLP (*Multi-Layer Perceptron*) entraîné via stacking Out-of-Fold :
1. **Prédictions OOF** : les modèles de base (EIF, NF, XGBoost) produisent des prédictions sur des plis de validation croisée temporelle, garantissant que le méta-modèle n'a jamais vu les données d'entraînement des modèles de base — évitant le surapprentissage (*information leakage*).
1. **Prédictions OOF** : les modèles de base (EIF, NF, HAT) produisent des prédictions sur des plis de validation croisée temporelle, garantissant que le méta-modèle n'a jamais vu les données d'entraînement des modèles de base — évitant le surapprentissage (*information leakage*).
2. **Méta-modèle MLP** : un réseau de neurones à 2 couches apprend la fonction de fusion optimale :
```
MetaFusionMLP : [eif, nf, xgb] → Linear(3,16) → BatchNorm → ReLU → Dropout(0.2)
MetaFusionMLP : [eif, nf, hat] → Linear(3,16) → BatchNorm → ReLU → Dropout(0.2)
→ Linear(16,1) → Sigmoid → P(bot)
```
@ -470,7 +469,7 @@ MetaFusionMLP : [eif, nf, xgb] → Linear(3,16) → BatchNorm → ReLU → Dropo
- **Early stopping** (patience = 5 epochs) : arrête l'entraînement dès que la loss de validation ne s'améliore plus, évitant le surapprentissage.
- **Weight decay** ($\lambda = 10^{-4}$) : pénalité L2 sur les poids du MLP pour une régularisation supplémentaire.
Le MLP apprend des frontières de décision non-linéaires dans l'espace 3D `[eif_norm, nf_norm, xgb_prob]`, capable de résoudre les patterns XOR et les interactions conditionnelles entre les trois voix. Le système de détection peut ainsi combiner automatiquement les signaux de manière optimale en fonction du type de trafic observé en production.
Le MLP apprend des frontières de décision non-linéaires dans l'espace 3D `[eif_norm, nf_norm, hat_prob]`, capable de résoudre les patterns XOR et les interactions conditionnelles entre les trois voix. Le système de détection peut ainsi combiner automatiquement les signaux de manière optimale en fonction du type de trafic observé en production.
**Calendrier de retraining** :
- HAT (supervisé) : apprentissage incrémental à chaque cycle (300s) sur les étiquettes accumulées, après filtrage Cleanlab des labels SOC bruyants (voir ci-dessous)
@ -479,18 +478,22 @@ Le MLP apprend des frontières de décision non-linéaires dans l'espace 3D `[ei
**Filtrage des labels SOC bruyants (Cleanlab)** :
Avant chaque entraînement XGBoost, les labels fournis par les analystes SOC sont filtrés via [Cleanlab](https://cleanlab.ai/) ([Northcutt et al., 2021](https://arxiv.org/abs/1911.00068)). Ce framework de *confident learning* identifie les exemples dont l'étiquette est probablement erronée en comparant les prédictions out-of-fold d'un modèle aux labels observés.
Avant chaque cycle d'apprentissage incrémental du HAT, les labels fournis par les analystes SOC sont filtrés via [Cleanlab](https://cleanlab.ai/) ([Northcutt et al., 2021](https://arxiv.org/abs/1911.00068)). Ce framework de *confident learning* identifie les exemples dont l'étiquette est probablement erronée en comparant les prédictions out-of-fold d'un modèle aux labels observés.
```python
# 1. Obtenir pred_probs via cross-validation (3 folds)
quick_model = XGBClassifier(n_estimators=80, max_depth=4)
pred_probs = cross_val_predict(quick_model, X, y, cv=3, method='predict_proba')
# 1. Obtenir pred_probs via cross-validation (3 folds) sur les labels accumulés
from river import tree
quick_model = tree.HoeffdingAdaptiveTreeClassifier()
# Les labels accumulés ce cycle sont filtrés avant injection dans le HAT
pred_probs = cross_val_predict(quick_model, X_accumulated, y_accumulated,
cv=3, method='predict_proba')
# 2. Identifier les labels douteux
issues = find_label_issues(labels=y, pred_probs=pred_probs)
# 3. Exclure les exemples bruités avant l'entraînement final
X_clean, y_clean = X[~noisy_mask], y[~noisy_mask]
# 3. N'injecter que les labels propres via learn_one()
for x_clean, y_clean in clean_samples:
hat_model.learn_one(x_clean, y_clean)
```
Ce mécanisme protège le modèle contre l'empoisonnement par des faux positifs mal corrigés ou des biais de confirmation des analystes. Le taux de labels filtrés est loggé pour surveillance. En cas d'échec de Cleanlab (erreur mémoire, dépendance manquante), le pipeline revient aux données brutes sans interruption.
@ -577,7 +580,20 @@ Le passage au stream mining élimine trois problématiques majeures du batch tra
**Validation gate** : conservée — si le taux d'anomalie sur le jeu de validation dépasse 20% après retraining EIF/NF, le nouveau modèle est rejeté et le modèle précédent conservé.
#### 2.4.4 Modélisation des phases d'attaque
**Quantification d'incertitude par Deep Ensembles**
La détection adversariale par ADWIN reposait sur l'heuristique suivante : si plus de 50% des features driftent simultanément, le drift est qualifié d'adversarial. Cette heuristique est non fondée — un pic de légitime trafic (ex. mise à jour navigateur majeure) peut déclencher un drift massif sur de nombreuses features sans pour autant être adversarial. À l'inverse, une attaque furtive ne touchant que quelques features ne serait jamais détectée.
Cette heuristique est remplacée par une mesure d'incertitude épistémique via **Deep Ensembles** ([Lakshminarayanan et al., 2017](https://arxiv.org/abs/1612.01474)) : le Normalizing Flow unique est remplacé par un ensemble de $M=5$ modèles indépendants, chacun entraîné sur un échantillon bootstrap (avec remise) de la baseline humaine. L'incertitude est mesurée par la variance inter-modèles :
$$\sigma^2(x) = \frac{1}{M} \sum_{m=1}^{M} \left( -\log p_m(x) - \overline{-\log p(x)} \right)^2$$
La logique de détection repose sur l'intuition suivante :
- **Dérive organique** (changement naturel du trafic) : les 5 modèles s'accordent sur la nouveauté → variance faible. Tous les manifolds ont capturé les mêmes structures dans la baseline, donc un nouveau pattern légitime est traité de manière cohérente.
- **Dérive adversariale** (évasion délibérée) : les 5 modèles ne s'accordent pas → variance qui explose. Un échantillon adversarial tombe dans une région de l'espace où chaque manifold a appris une frontière légèrement différente (diversité induite par le bootstrap), produisant des scores de vraisemblance très dispersés.
Le seuil `NF_UNCERTAINTY_THRESHOLD` (défaut : 1.0) est appliqué sur $\sigma^2(x)$ : tout échantillon au-dessus est tagué `is_adversarial_drift = True`. Cette approche est fondée statistiquement (variance sur un ensemble) et ne dépend pas d'un seuil arbitraire sur le nombre de features en drift.
La modélisation des phases d'attaque (Reconnaissance → Mouvement latéral → Intrusion → Exfiltration) par des modèles d'état-espace ou des processus de Markov cachés constitue une piste de recherche. L'enrichissement du clustering HDBSCAN avec ce signal de phase permettrait de distinguer des campagnes en phase de reconnaissance de campagnes en phase d'exploitation active.