docs: réécriture complète de la documentation des services en français
- 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>
This commit is contained in:
@ -1,221 +1,549 @@
|
||||
# Bot Detector
|
||||
|
||||
The bot-detector is a Python service that performs machine-learning anomaly detection on aggregated HTTP/TLS traffic features stored in ClickHouse. It runs on a continuous cycle (default: every 5 minutes), using Isolation Forest to identify suspicious traffic patterns, enriched with SHAP explainability, DBSCAN clustering, and Anubis bot-rule enrichment.
|
||||
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.
|
||||
|
||||
## ML Algorithm
|
||||
---
|
||||
|
||||
### Isolation Forest (Semi-Supervised)
|
||||
## Architecture des modules
|
||||
|
||||
The core algorithm is **Isolation Forest** (Liu, Ting & Zhou, 2008) — an unsupervised anomaly detection algorithm that isolates anomalies by randomly partitioning feature space. Anomalies require fewer partitions to isolate than normal points.
|
||||
|
||||
The approach is **semi-supervised** because:
|
||||
1. **Known bots** are identified a priori via reputation dictionaries (IP, JA4, ASN)
|
||||
2. **Human baseline** is identified via ASN reputation labels (`asn_label = 'human'`)
|
||||
3. The model trains **only on human-baseline traffic** (minimum 500 sessions required)
|
||||
4. Unknown traffic is scored by deviation from the human profile
|
||||
|
||||
### Two-Model Architecture
|
||||
|
||||
| Model | Condition | Features | Data |
|
||||
|-------|-----------|----------|------|
|
||||
| **Complet** | `correlated = 1` | 35 | HTTP + TCP + TLS (full pipeline data) |
|
||||
| **Applicatif** | `correlated = 0` | 31 | HTTP only (no TLS correlation available) |
|
||||
|
||||
### Threat Levels
|
||||
|
||||
| Score Range | Level | Interpretation |
|
||||
|------------|-------|----------------|
|
||||
| `< -0.30` | **CRITICAL** | Extremely anomalous behavior |
|
||||
| `< -0.15` | **HIGH** | Strong anomaly signal |
|
||||
| `< -0.05` | **MEDIUM** | Moderate anomaly |
|
||||
| `≥ -0.05` | **LOW** | Slightly unusual |
|
||||
|
||||
## Feature List
|
||||
|
||||
### Common Features (31 — Applicatif model)
|
||||
|
||||
#### HTTP Behavior
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `hits` | Request count in the window |
|
||||
| `hit_velocity` | Requests per second |
|
||||
| `fuzzing_index` | Path/parameter diversity anomaly score |
|
||||
| `post_ratio` | Fraction of POST requests |
|
||||
| `port_exhaustion_ratio` | Fraction of distinct source ports / total |
|
||||
| `orphan_ratio` | Requests without TLS correlation |
|
||||
| `head_ratio` | Fraction of HEAD requests |
|
||||
| `http10_ratio` | Fraction of HTTP/1.0 requests |
|
||||
| `generic_accept_ratio` | Fraction of short Accept headers |
|
||||
| `sec_fetch_absence_rate` | Fraction missing Sec-Fetch-Site |
|
||||
| `missing_accept_enc_ratio` | Fraction missing Accept-Encoding |
|
||||
| `http_scheme_ratio` | Fraction using HTTP (not HTTPS) |
|
||||
|
||||
#### Connection Management
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `max_keepalives` | Max requests on a single Keep-Alive connection |
|
||||
| `tcp_shared_count` | TCP connections shared between sessions |
|
||||
| `multiplexing_efficiency` | HTTP/2 multiplexing efficiency |
|
||||
|
||||
#### Browser Fingerprint
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `header_count` | HTTP headers sent |
|
||||
| `has_accept_language` | Accept-Language header presence |
|
||||
| `has_cookie` | Cookie header presence |
|
||||
| `has_referer` | Referer header presence |
|
||||
| `modern_browser_score` | Composite browser compliance score (0–100) |
|
||||
| `ua_ch_mismatch` | User-Agent vs Client Hints inconsistency |
|
||||
| `ip_id_zero_ratio` | IP packets with ID=0 (headless/minimal stack) |
|
||||
| `header_order_shared_count` | IPs sharing same header order |
|
||||
| `header_order_confidence` | Normalized entropy of header order |
|
||||
| `distinct_header_orders` | Distinct header orderings per IP |
|
||||
| `is_fake_navigation` | Sec-Fetch-Mode=navigate with non-document dest |
|
||||
|
||||
#### Navigation Patterns
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `request_size_variance` | Variance of request sizes |
|
||||
| `mss_mobile_mismatch` | TCP MSS vs mobile profile inconsistency |
|
||||
| `asset_ratio` | Static asset request fraction |
|
||||
| `direct_access_ratio` | Direct accesses (no referer) |
|
||||
| `is_ua_rotating` | User-Agent rotation detected (flag) |
|
||||
| `distinct_ja4_count` | Distinct JA4 fingerprints per IP |
|
||||
| `anomalous_payload_ratio` | Anomalous payload size fraction |
|
||||
|
||||
#### Concentration & Rarity
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `src_port_density` | Source port entropy |
|
||||
| `ja4_asn_concentration` | JA4 concentration within ASN |
|
||||
| `ja4_country_concentration` | JA4 concentration per country |
|
||||
| `is_rare_ja4` | Rare JA4 fingerprint (< 100 total hits) |
|
||||
|
||||
#### Temporal & Diversity
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `temporal_entropy` | Temporal distribution entropy |
|
||||
| `path_diversity_ratio` | URL path diversity |
|
||||
| `url_depth_variance` | URL depth variance |
|
||||
| `ja3_diversity_ratio` | JA3 diversity ratio per IP |
|
||||
|
||||
### Additional TCP/TLS Features (Complet model only — 4 extra)
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `tcp_jitter_variance` | TCP inter-packet jitter variance |
|
||||
| `alpn_http_mismatch` | ALPN vs actual HTTP protocol mismatch |
|
||||
| `is_alpn_missing` | ALPN absent in ClientHello |
|
||||
| `sni_host_mismatch` | TLS SNI vs HTTP Host mismatch |
|
||||
|
||||
### L4 Fingerprint Features (Complet model)
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| `avg_ttl` | Average IP TTL (OS fingerprint) |
|
||||
| `ttl_std` | TTL standard deviation |
|
||||
| `no_window_scale_ratio` | Fraction without TCP window scale |
|
||||
| `syn_timing_cv` | SYN timing coefficient of variation |
|
||||
| `tls12_ratio` | Fraction of TLS 1.2 connections |
|
||||
| `ip_df_variance` | IP Don't-Fragment flag variance |
|
||||
|
||||
## Detection Pipeline
|
||||
Le service est découpé en **11 modules** organisés ainsi :
|
||||
|
||||
```
|
||||
1. Read view_ai_features_1h (last 24h) → DataFrame
|
||||
2. Read view_ip_recurrence → recurrence map
|
||||
3. Clean columns (fillna, astype)
|
||||
4. Split by correlated=1 / correlated=0
|
||||
5. For each model (Complet, Applicatif):
|
||||
a. A7: Validate features (exclude missing/constant)
|
||||
b. Separate known bots → log as KNOWN_BOT
|
||||
c. Filter human baseline (asn_label='human', min 500 sessions)
|
||||
d. Load or train Isolation Forest model
|
||||
e. A1: Check concept drift (KS test on features)
|
||||
f. Score unknown traffic
|
||||
g. A10: Normalize scores to [-1, 0]
|
||||
h. A2: Compute adaptive threshold = min(percentile_5, ANOMALY_THRESHOLD)
|
||||
i. A6: Apply recurrence weighting
|
||||
j. Filter scores below threshold
|
||||
k. A4: SHAP explainability (top 5 features)
|
||||
l. A8: DBSCAN clustering (campaign detection)
|
||||
6. Concatenate results, deduplicate by src_ip (keep lowest score)
|
||||
7. A5: Deduplication with TTL (skip recently reported IPs)
|
||||
8. Insert into ml_detected_anomalies + ml_all_scores
|
||||
__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)
|
||||
```
|
||||
|
||||
## Concept Drift Detection (A1)
|
||||
| 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 |
|
||||
|
||||
Uses the **Kolmogorov-Smirnov test** to compare feature distributions between the current data and the training data. If the fraction of drifted features exceeds `DRIFT_THRESHOLD` (default: 0.30), the model is retrained.
|
||||
---
|
||||
|
||||
## SHAP Explainability (A4)
|
||||
## Configuration
|
||||
|
||||
When enabled (`ENABLE_SHAP=true`), computes SHAP values for each detected anomaly using `shap.TreeExplainer`. The top 5 contributing features are stored in the `reason` field.
|
||||
Toute la configuration est lue via `os.getenv()` dans `config.py`. Aucun fichier YAML ni pydantic-settings.
|
||||
|
||||
## DBSCAN Clustering (A8)
|
||||
### Connexion ClickHouse
|
||||
|
||||
When enabled (`ENABLE_CLUSTERING=true`), applies DBSCAN on anomaly feature vectors to group related anomalies into campaigns. Each anomaly gets a `campaign_id` (-1 = no cluster).
|
||||
| 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 |
|
||||
|
||||
## Anubis Bot-Rule Enrichment
|
||||
### Modèle et entraînement
|
||||
|
||||
The `view_ai_features_1h` view enriches each IP with Anubis bot detection using a priority cascade:
|
||||
1. **UA + IP combined** (same `rule_id`) — highest confidence
|
||||
2. **UA only** (no IP requirement)
|
||||
3. **IP only** (no UA requirement)
|
||||
4. **ASN match**
|
||||
5. **Country match**
|
||||
| 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 |
|
||||
|
||||
## Environment Variables
|
||||
### Autoencoder
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `CLICKHOUSE_HOST` | string | `clickhouse` | ClickHouse server hostname |
|
||||
| `CLICKHOUSE_PORT` | int | `8123` | ClickHouse HTTP port |
|
||||
| `CLICKHOUSE_DB` | string | `ja4_processing` | Database name |
|
||||
| `CLICKHOUSE_USER` | string | `admin` | ClickHouse username |
|
||||
| `CLICKHOUSE_PASSWORD` | string | `""` | ClickHouse password |
|
||||
| `ISOLATION_CONTAMINATION` | float | `0.02` | Contamination parameter for Isolation Forest |
|
||||
| `ANOMALY_THRESHOLD` | float | `-0.03` | Score threshold for anomaly detection |
|
||||
| `ANOMALY_PERCENTILE` | int | `5` | Percentile for adaptive threshold (A2) |
|
||||
| `CYCLE_INTERVAL_SEC` | int | `300` | Seconds between detection cycles |
|
||||
| `MAX_CONSECUTIVE_FAILURES` | int | `3` | Max consecutive failures before exit |
|
||||
| `BOT_DETECTOR_LOG` | string | `/var/log/bot_detector/decisions.jsonl` | Decision log file path |
|
||||
| `LOG_BACKUP_COUNT` | int | `7` | Number of rotated log backups |
|
||||
| `MODEL_DIR` | string | `/var/lib/bot_detector` | Model persistence directory |
|
||||
| `RETRAIN_INTERVAL_HOURS` | int | `24` | Hours between model retraining |
|
||||
| `MODEL_HISTORY_COUNT` | int | `10` | Number of model versions to keep |
|
||||
| `DRIFT_THRESHOLD` | float | `0.30` | KS-test drift threshold (A1) |
|
||||
| `ENABLE_MULTIWINDOW` | bool | `false` | Enable 24h multi-window analysis (A3) |
|
||||
| `MULTIWINDOW_VIEW` | string | `view_ai_features_24h` | View for multi-window mode |
|
||||
| `ENABLE_SHAP` | bool | `true` | Enable SHAP explainability (A4) |
|
||||
| `DEDUP_TTL_MIN` | int | `60` | Deduplication TTL in minutes (A5) |
|
||||
| `RECURRENCE_WEIGHT` | float | `0.005` | Recurrence score weighting factor (A6) |
|
||||
| `MIN_VALID_FEATURE_RATIO` | float | `0.50` | Min valid feature ratio (A7) |
|
||||
| `ENABLE_CLUSTERING` | bool | `true` | Enable DBSCAN clustering (A8) |
|
||||
| `CLUSTERING_MIN_SAMPLES` | int | `3` | DBSCAN min samples per cluster |
|
||||
| `HEALTH_PORT` | int | `8080` | Health check HTTP server port |
|
||||
| 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 |
|
||||
|
||||
## Output Tables
|
||||
### 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` :
|
||||
|
||||
```python
|
||||
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` + `BatchNorm1d` sur les couches cachées, `Sigmoid` en 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`) :
|
||||
|
||||
```python
|
||||
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 :
|
||||
|
||||
```python
|
||||
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 `TreeExplainer` sur 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é :
|
||||
|
||||
1. **UA + IP combinés** (même `rule_id`) — confiance maximale
|
||||
2. **UA seul** (pas de condition IP)
|
||||
3. **IP seul** (pas de condition UA)
|
||||
4. **Correspondance ASN**
|
||||
5. **Correspondance pays**
|
||||
|
||||
---
|
||||
|
||||
## Tables de sortie
|
||||
|
||||
### ml_detected_anomalies
|
||||
|
||||
Anomaly detections above the threat threshold. Engine: `ReplacingMergeTree(detected_at)`, ORDER BY `(src_ip)`, TTL 30 days.
|
||||
Détections d'anomalies au-dessus du seuil de menace. Engine : `ReplacingMergeTree(detected_at)`, ORDER BY `(src_ip, model_name)`, TTL 7 jours.
|
||||
|
||||
Key columns: `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 all ML features.
|
||||
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
|
||||
|
||||
All classifications (no threshold filter) for observability. Engine: `ReplacingMergeTree(detected_at)`, ORDER BY `(window_start, src_ip, ja4, host, model_name)`, TTL 3 days.
|
||||
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.
|
||||
|
||||
## Decision Log Format
|
||||
---
|
||||
|
||||
The `decisions.jsonl` file contains structured JSONL entries:
|
||||
## Format du journal de décisions
|
||||
|
||||
Le fichier `decisions.jsonl` contient des entrées JSONL structurées :
|
||||
|
||||
```json
|
||||
{"event": "CYCLE_START", "cycle_id": "20260309T143000", "total": 5000, "human": 1500, "known_bot": 200, "correlated": 3000}
|
||||
@ -224,42 +552,52 @@ The `decisions.jsonl` file contains structured JSONL entries:
|
||||
{"event": "CYCLE_END", "cycle_id": "20260309T143000", "anomalies": 15, "known_bots": 200, "duration_sec": 12.5}
|
||||
```
|
||||
|
||||
Log rotation: 50 MB max size × `LOG_BACKUP_COUNT` backups (default 7).
|
||||
Rotation des logs : 50 Mo max × `LOG_BACKUP_COUNT` sauvegardes (défaut : 7).
|
||||
|
||||
## Health Check Endpoint
|
||||
---
|
||||
|
||||
- **URL**: `GET http://localhost:8080/`
|
||||
- **Response**: `200 OK` with status JSON
|
||||
- Runs in a separate thread
|
||||
## Point de santé
|
||||
|
||||
## Model Persistence
|
||||
- **URL** : `GET http://localhost:8080/`
|
||||
- **Réponses** : `200 OK` (corps `OK`) ou `503 Service Unavailable` (corps `DEGRADED`)
|
||||
- Exécuté dans un thread daemon au démarrage
|
||||
- État mis à `False` après `MAX_CONSECUTIVE_FAILURES` (3) échecs consécutifs ClickHouse, remis à `True` dès le premier succès
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `model_<name>_<version>.joblib` | Serialized Isolation Forest (joblib) |
|
||||
| `model_<name>_<version>.meta.json` | Model metadata (features, thresholds, training stats) |
|
||||
| `model_<name>.current` | Pointer to active model version |
|
||||
| `training_history.jsonl` | Training history log |
|
||||
---
|
||||
|
||||
Models are rotated: only the last `MODEL_HISTORY_COUNT` versions (default 10) are kept.
|
||||
## Persistance des modèles
|
||||
|
||||
## Docker Deployment
|
||||
| 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
|
||||
|
||||
```bash
|
||||
# Build
|
||||
# Construction de l'image
|
||||
make build-bot-detector
|
||||
|
||||
# Run with docker-compose
|
||||
# Exécution avec docker-compose
|
||||
cd services/bot-detector
|
||||
docker-compose up -d
|
||||
|
||||
# Tests
|
||||
make test-bot-detector
|
||||
```
|
||||
|
||||
### Volumes
|
||||
|
||||
| Host Path | Container Path | Description |
|
||||
|-----------|---------------|-------------|
|
||||
| `./bot_detector_logs` | `/var/log/bot_detector` | Decision logs (JSONL) |
|
||||
| `./bot_detector_models` | `/var/lib/bot_detector` | Persisted ML models |
|
||||
| `./reputation/data/user_files/bot_ip.csv` | `/data/bot_ip.csv` (ro) | Known bot IP list |
|
||||
| `./reputation/data/user_files/bot_ja4.csv` | `/data/bot_ja4.csv` (ro) | Known bot JA4 list |
|
||||
| `./reputation/data/user_files/asn_reputation.csv` | `/data/asn_reputation.csv` (ro) | ASN reputation labels |
|
||||
| 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 |
|
||||
|
||||
Reference in New Issue
Block a user