feat: ja4-platform monorepo — 5 services unified, tests & RPM builds standardized

Services:
- ja4sentinel: TLS/JA4 fingerprint capture daemon (Go, libpcap)
- logcorrelator: JA4 log correlation engine (Go, ClickHouse)
- mod_reqin_log: Apache module (C, JSON request logging)
- bot_detector: ML bot detection pipeline (Python)
- dashboard: FastAPI/Streamlit analytics UI (Python)

Shared libraries:
- shared/go/ja4common: logger, config, shutdown, ipfilter (Go module)
- shared/python/ja4_common: ClickHouseClient, ClickHouseSettings (Python package)
- shared/clickhouse/: canonical SQL migrations (10 files)

Build & packaging:
- Unified 3-stage Dockerfile.package for Go RPMs (el8/el9/el10)
- go.work workspace linking sentinel, correlator, ja4common
- Makefile with test-all, build-all, rpm-* targets

Fixes applied:
- go.work: 1.21 → 1.24.6 (required by sentinel)
- correlator Dockerfiles: golang:1.21 → golang:1.24
- replace directives in go.mod for ja4common local path
- pyproject.toml: setuptools.backends → setuptools.build_meta
- Removed static libpcap linking (unavailable on Rocky 9)
- Fixed data races in output/writers_test.go (sync.Mutex + atomic.Int32)
- Rewrote corrupted test files (logger_test.go × 2)

Test coverage:
- correlator: 67.1% total (unixsocket 80.5%, config 91.7%, app 83.3%, multi 87.7%, stdout 100%)
- sentinel: all 10 packages pass (api, capture, config, fingerprint, ipfilter, logging, output, tlsparse)

Documentation:
- README.md + docs/ (architecture, development, 5 services, shared libs, DB schema & migrations)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
toto
2026-04-07 16:42:59 +02:00
commit d469e39da7
278 changed files with 1621301 additions and 0 deletions

View File

@ -0,0 +1,204 @@
# Diagnostic — Features manquantes dans `view_ai_features_1h`
> Généré le 2026-03-17 — Mis à jour le 2026-03-17 (corrections appliquées) — À destination de l'administrateur ClickHouse
## ✅ Statut des corrections (2026-03-17 13:05)
| Problème | Correction appliquée | Résultat |
|----------|---------------------|----------|
| **1** — MV `mv_agg_header_fingerprint_1h` absente | MV recréée + backfill 25h | ✅ 10 features header actives |
| **2**`header_order_shared_count` / `distinct_header_orders` globales | Se corrige avec Problème 1 | ✅ Résolu automatiquement |
| **3**`orphan_ratio` = 0 pour `correlated=1` | Comportement normal (by design) | Pas d'action requise |
| **4** — 4 vues dashboard absentes | Vues créées | ✅ |
| **5**`view_dashboard_variability` référence `header_user_agent` inexistant | Colonne remplacée par `reason` | ✅ Bug corrigé |
| **6** — Anciennes vues heuristiques orphelines | Droppées | ✅ |
Cycle post-correction (13:05) — features dans les warnings :
- `Complet` : seulement `orphan_ratio` (by design)
- `Applicatif` : `request_size_variance`, `mss_mobile_mismatch`, `is_rare_ja4` (see §4 below)
- Header features **disparues des warnings** → pipeline opérationnel ✅
---
---
## Résumé
Le service Bot Detector signale des **features non-discriminantes** à chaque cycle. Ce document en explique les causes exactes et les corrections nécessaires côté ClickHouse.
Ces avertissements **n'empêchent pas le service de fonctionner** — les features invalides sont automatiquement exclues du modèle (A7). Mais leur absence réduit la qualité de la détection.
---
## Problème 1 — Pipeline `agg_header_fingerprint_1h` arrêté ⚠️ CRITIQUE
### Symptôme
Les features suivantes sont toujours à **0** dans `view_ai_features_1h` :
- `header_count`
- `has_accept_language`
- `has_cookie`
- `has_referer`
- `modern_browser_score`
- `ua_ch_mismatch`
- `mss_mobile_mismatch` *(dépend de `modern_browser_score`)*
### Cause
La table `mabase_prod.agg_header_fingerprint_1h` (AggregatingMergeTree) n'a plus reçu de données depuis le **2026-03-13 23:00** :
```sql
SELECT max(window_start), count()
FROM mabase_prod.agg_header_fingerprint_1h;
-- Résultat : 2026-03-13 23:00:00, 73024 lignes
```
La vue fait un `LEFT JOIN` avec condition `window_start >= now() - INTERVAL 24 HOUR`, et comme aucune ligne récente n'existe dans `agg_header_fingerprint_1h`, **toutes les colonnes issues de ce JOIN retournent NULL** (→ 0 après coalesce).
### Recherche de la MV source
La liste des Materialized Views ne montre aucune MV dédiée à `agg_header_fingerprint_1h` :
```sql
SELECT name FROM system.tables
WHERE database = 'mabase_prod' AND engine = 'MaterializedView';
-- mv_agg_host_ip_ja4_1h
-- mv_http_logs
-- view_dashboard_entities_mv
-- view_dashboard_user_agents_mv
```
Aucune MV ne cible `agg_header_fingerprint_1h`. Elle est probablement alimentée par un **processus externe** (ETL, script, pipeline Kafka, etc.) qui s'est arrêté.
### Correction appliquée ✅
La MV `mv_agg_header_fingerprint_1h` était **définie dans `deploy_views.sql`** mais n'avait jamais été créée en base. Elle a été recréée le 2026-03-17 :
```sql
-- Recréation de la MV (déjà appliquée)
CREATE MATERIALIZED VIEW mabase_prod.mv_agg_header_fingerprint_1h
TO mabase_prod.agg_header_fingerprint_1h AS
SELECT
toStartOfHour(src.time) AS window_start,
toIPv6(src.src_ip) AS src_ip,
any(toString(cityHash64(src.client_headers))) AS header_order_hash,
max(toUInt16(length(src.client_headers) - length(replaceAll(src.client_headers, ',', '')) + 1)) AS header_count,
-- ... (voir deploy_views.sql §5)
FROM mabase_prod.http_logs AS src
GROUP BY window_start, src.src_ip;
```
Un **backfill de 25 heures** a été effectué depuis `http_logs` pour alimenter la table avec des données historiques (377 689 lignes insérées). Les nouvelles données sont désormais alimentées en temps réel par la MV.
### Cause historique
La MV avait été omise lors du déploiement initial. La table `agg_header_fingerprint_1h` contenait 73 024 lignes datant du 2026-03-13 (probablement issues d'un backfill manuel ponctuel), puis n'avait plus été alimentée.
---
## Problème 2 — Features non-discriminantes (agrégat global, non per-IP)
### Symptôme
Les features suivantes ont une **valeur unique non-nulle identique pour toutes les IPs** :
- `header_order_shared_count` (valeur ≈ 421 000 pour toutes les lignes)
- `distinct_header_orders` (valeur identique pour toutes les lignes)
### Cause
Ces features sont calculées via des window functions `PARTITION BY header_order_hash` :
```sql
-- Dans la vue :
count() OVER (PARTITION BY h.header_order_hash) AS header_order_shared_count
uniqExact(h.header_order_hash) OVER (PARTITION BY a.src_ip) AS distinct_header_orders
```
Comme `h.header_order_hash` est **NULL pour toutes les lignes** (problème 1 ci-dessus), la `PARTITION BY NULL` regroupe **toutes les lignes dans une seule partition**`count()` retourne le total de toutes les lignes pour chaque IP.
### Correction ✅ (auto-résolue avec Problème 1)
Ce problème s'est résolu automatiquement une fois la MV `mv_agg_header_fingerprint_1h` recréée. `header_order_hash` est désormais non-NULL, les partitions de window functions sont correctement calculées par hash d'ordre d'en-têtes.
---
## Problème 3 — `orphan_ratio` absent pour le trafic corrélé TCP
### Symptôme
`orphan_ratio` = 0 pour **toutes les lignes avec `correlated = 1`** (trafic TCP enrichi).
### Cause
La colonne `orphan_count` dans `mabase_prod.agg_host_ip_ja4_1h` est calculée par la MV `mv_agg_host_ip_ja4_1h` :
```sql
sum(IF(src.orphan_side = 'A' OR src.correlated = 0, 1, 0)) AS orphan_count
```
Pour les connexions `correlated=1`, `correlated = 0` est toujours faux, et `orphan_side = 'A'` n'est jamais vrai pour le trafic corrélé → `orphan_count = 0` systématiquement.
**C'est un comportement intentionnel** : les connexions TCP corrélées ont une réponse confirmée, donc elles ne sont pas des requêtes orphelines par définition.
### Statut
Pas d'action requise. La feature reste exclue automatiquement par A7 pour le modèle `Complet` (correlated=1).
---
## Problème 4 — Features à 0 persistantes dans le modèle Applicatif
### Symptôme (post-correction)
Depuis le 2026-03-17 13:05, le modèle `Applicatif` (trafic non-corrélé) signale encore ces features à 0 :
- `request_size_variance`
- `mss_mobile_mismatch`
- `is_rare_ja4`
### Cause
Ces features sont calculées depuis des colonnes L4/TCP qui sont **absent ou non-pertinentes pour le trafic applicatif pur** (`correlated=0`) :
| Feature | Cause |
|---------|-------|
| `request_size_variance` | `varPopMerge(total_ip_length_var)` — variance de longueur IP ; trafic non-corrélé = pas de données IP brutes fiables |
| `mss_mobile_mismatch` | Dépend de `tcp_meta_mss` et `modern_browser_score` — MSS non fiable sans corrélation TCP |
| `is_rare_ja4` | `sum(hits) OVER (PARTITION BY ja4) < 100` — dans la fenêtre Applicatif (1h, trafic réduit), tous les JA4 sont rares |
### Impact
Faible — ces features sont exclues automatiquement (A7). Elles ne dégradent pas le modèle.
---
## Impact sur le modèle IA
| Feature | Impact si absente | Statut |
|---------|-------------------|--------|
| `header_count` | Perte d'un signal fort : bots envoient souvent peu d'en-têtes | ✅ Corrigé |
| `has_accept_language` | Perte de détection des bots sans localisation | ✅ Corrigé |
| `has_cookie` | Perte de détection des sessions sans état | ✅ Corrigé |
| `has_referer` | Perte du signal de navigation directe | ✅ Corrigé |
| `modern_browser_score` | Perte du score composite de conformité navigateur | ✅ Corrigé |
| `ua_ch_mismatch` | Perte de détection des fausses déclarations UA | ✅ Corrigé |
| `header_order_shared_count` | Perte de la détection de fingerprints d'en-têtes partagés | ✅ Corrigé |
| `orphan_ratio` | Signal faible pour trafic corrélé | By design |
| `request_size_variance` | Signal L4 faible pour Applicatif | Normal |
| `mss_mobile_mismatch` | Signal TCP faible pour Applicatif | Normal |
---
## Vérification post-correction
Cycle du 2026-03-17 13:05 — résultat observé :
```
[Complet] Features à 0 : ['orphan_ratio'] ← by design ✅
[Applicatif] Features à 0 : ['request_size_variance', 'mss_mobile_mismatch', 'is_rare_ja4'] ← normales ✅
[Applicatif] Features non-discriminantes : ['tcp_shared_count'] ← agrégat global résiduel
```
Les **10 features header** (`header_count`, `has_accept_language`, `has_cookie`, `has_referer`, `modern_browser_score`, `ua_ch_mismatch`, `header_order_shared_count`, `distinct_header_orders`, `header_order_confidence`, `mss_mobile_mismatch` pour Complet) **ne sont plus dans les warnings**. Le pipeline est opérationnel.