# Migrations de base de données Le schéma ClickHouse de ja4-platform est géré via 13 fichiers SQL numérotés dans `shared/clickhouse/`. Toutes les migrations sont **idempotentes** (utilisation de `IF NOT EXISTS` / `IF EXISTS` / `CREATE OR REPLACE`) et doivent être appliquées dans l'ordre numérique. Le schéma utilise un **patron double-base** : | Base | Variable d'environnement | Défaut | Contenu | |------|--------------------------|--------|---------| | Logs | `CLICKHOUSE_DB_LOGS` | `ja4_logs` | `http_logs_raw`, `http_logs`, `mv_http_logs` | | Processing | `CLICKHOUSE_DB_PROCESSING` | `ja4_processing` | Agrégations, ML, vues, dictionnaires, audit | --- ## Ordre des migrations | Fichier | Lignes | Contenu | |---------|--------|---------| | `00_database.sql` | 5 | Création des bases `ja4_logs` et `ja4_processing` | | `01_raw_tables.sql` | 16 | Table d'ingestion `http_logs_raw` (MergeTree, TTL 2h) | | `02_dictionaries.sql` | 57 | Dictionnaire `dict_iplocate_asn` (IP_TRIE, CSV), tables `ref_bot_networks`, `bot_ip`, `bot_ja4` | | `03_anubis_tables.sql` | 73 | Tables de règles Anubis (`anubis_ip_rules`, `anubis_asn_rules`) et dictionnaires (`dict_anubis_ip`, `dict_anubis_asn`) | | `04_mv_http_logs.sql` | 197 | Table `http_logs` (MergeTree, TTL 30j) + vue matérialisée `mv_http_logs` (parse JSON + enrichissement Anubis COALESCE IP→ASN) | | `05_aggregation_tables.sql` | 234 | Dictionnaires de réputation (`dict_bot_ip`, `dict_bot_ja4`, `dict_browser_ja4`, `dict_asn_reputation`), 2 tables d'agrégation (`agg_host_ip_ja4_1h`, `agg_header_fingerprint_1h`) + 2 vues matérialisées | | `06_ml_tables.sql` | 144 | Tables ML (`ml_detected_anomalies`, `ml_all_scores`) + vue `view_ip_recurrence` | | `07_ai_features_view.sql` | 156 | Vue `view_ai_features_1h` (~65+ features ML depuis les agrégations + dictionnaires) | | `08_users.sql` | 22 | Utilisateurs `data_writer` et `analyst` avec permissions | | `09_audit_table.sql` | 21 | Table `audit_logs` pour le journal d'audit SOC | | `10_perf_indexes.sql` | 113 | Index secondaires et projections de performance (migration idempotente pour instances existantes) | | `11_views.sql` | 216 | Vues dashboard (`view_dashboard_entities`, `view_dashboard_user_agents`, `view_form_bruteforce_detected`, `view_host_ip_ja4_rotation`, `view_resource_cascade_1h`) | | `12_thesis_features.sql` | 580 | 4 tables d'agrégation thèse (`agg_path_sequences_1h`, `agg_request_timing_1h`, `agg_ip_behavior_1h`, `agg_resource_cascade_1h`) + 4 MVs + vue `view_thesis_features_1h` | --- ## Prérequis ### 1. Serveur ClickHouse Un serveur ClickHouse en fonctionnement, **version 24.8+** requise (support des projections AggregatingMergeTree avec `deduplicate_merge_projection_mode`). ### 2. Fichiers CSV de données Placer les fichiers suivants dans `/var/lib/clickhouse/user_files/` : | Fichier | Source | Description | Entrées approx. | |---------|--------|-------------|------------------| | `iplocate-ip-to-asn.csv` | [IPLocate](https://iplocate.io) | Correspondance IP→ASN avec pays, org, domaine | ~714K | | `bot_ip.csv` | Personnalisé | Préfixes IP de bots connus (format CIDR) | ~3,5K | | `bot_ja4.csv` | Personnalisé | Empreintes JA4 de bots connus | ~31 | | `browser_ja4.csv` | Personnalisé | Empreintes JA4 de navigateurs légitimes | ~1,2K | | `asn_reputation.csv` | Personnalisé | Labels de réputation ASN (`human`, `bot`, `unknown`) | ~82K | ### 3. Mots de passe Anubis Le fichier `03_anubis_tables.sql` contient des mots de passe par défaut (`CHANGE_ME`) pour les dictionnaires Anubis basés sur ClickHouse. Les remplacer avant d'appliquer : ```bash sed -i "s/CHANGE_ME/mot_de_passe_réel/g" 03_anubis_tables.sql ``` --- ## Comment appliquer ### Méthode recommandée : deploy_schema.sh Le script `deploy_schema.sh` applique les 13 fichiers dans l'ordre en substituant automatiquement les noms de base de données : ```bash cd shared/clickhouse/ # Avec les noms de base par défaut (ja4_logs / ja4_processing) ./deploy_schema.sh # Avec des noms personnalisés CLICKHOUSE_DB_LOGS=my_logs \ CLICKHOUSE_DB_PROCESSING=my_proc \ CLICKHOUSE_HOST=clickhouse-server \ CLICKHOUSE_USER=admin \ CLICKHOUSE_PASSWORD='secret' \ ./deploy_schema.sh ``` Variables d'environnement supportées : | Variable | Défaut | Description | |----------|--------|-------------| | `CLICKHOUSE_DB_LOGS` | `ja4_logs` | Nom de la base de logs | | `CLICKHOUSE_DB_PROCESSING` | `ja4_processing` | Nom de la base de traitement | | `CLICKHOUSE_HOST` | `localhost` | Hôte ClickHouse | | `CLICKHOUSE_PORT` | `9000` | Port natif ClickHouse | | `CLICKHOUSE_USER` | `default` | Utilisateur ClickHouse | | `CLICKHOUSE_PASSWORD` | (vide) | Mot de passe ClickHouse | ### Méthode alternative : init-stack.sh Le script `scripts/init-stack.sh` fournit une initialisation complète incluant le schéma, les migrations, la validation et le nettoyage : ```bash ./scripts/init-stack.sh ``` ### Application manuelle ```bash cd shared/clickhouse/ for f in 0*.sql 1*.sql; do echo "Application de $f..." clickhouse-client --multiquery < "$f" done ``` Avec authentification : ```bash clickhouse-client --user admin --password 'secret' --multiquery < 00_database.sql # ... répéter pour chaque fichier ``` --- ## Comment vérifier Après l'application de toutes les migrations, exécuter ces requêtes pour valider chaque étape. ### 00 — Bases de données ```sql SHOW DATABASES LIKE 'ja4%'; -- Attendu : ja4_logs, ja4_processing ``` ### 01 — Table brute ```sql EXISTS ja4_logs.http_logs_raw; -- Attendu : 1 ``` ### 02 — Dictionnaire ASN + tables de référence ```sql SELECT dictGetOrDefault('ja4_processing.dict_iplocate_asn', 'country_code', toIPv6(toIPv4('8.8.8.8')), 'MISSING'); -- Attendu : US (si CSV chargé) ou MISSING EXISTS ja4_processing.ref_bot_networks; -- Attendu : 1 ``` ### 03 — Tables Anubis ```sql EXISTS ja4_processing.anubis_ip_rules; EXISTS ja4_processing.anubis_asn_rules; -- Attendu : 1 pour chacune ``` ### 04 — http_logs + vue matérialisée ```sql EXISTS ja4_logs.http_logs; SELECT name FROM system.tables WHERE database = 'ja4_logs' AND name = 'mv_http_logs'; -- Attendu : mv_http_logs ``` ### 05 — Tables d'agrégation + dictionnaires de réputation ```sql EXISTS ja4_processing.agg_host_ip_ja4_1h; EXISTS ja4_processing.agg_header_fingerprint_1h; SELECT name FROM system.dictionaries WHERE database = 'ja4_processing' AND name IN ('dict_bot_ip', 'dict_bot_ja4', 'dict_browser_ja4', 'dict_asn_reputation'); -- Attendu : 4 lignes ``` ### 06 — Tables ML ```sql EXISTS ja4_processing.ml_detected_anomalies; EXISTS ja4_processing.ml_all_scores; SELECT name FROM system.tables WHERE database = 'ja4_processing' AND name = 'view_ip_recurrence'; -- Attendu : view_ip_recurrence ``` ### 07 — Vue de features AI ```sql SELECT name FROM system.tables WHERE database = 'ja4_processing' AND name = 'view_ai_features_1h'; -- Attendu : view_ai_features_1h ``` ### 08 — Utilisateurs ```sql SHOW GRANTS FOR data_writer; -- Attendu : GRANT INSERT, SELECT ON ja4_logs.http_logs_raw TO data_writer SHOW GRANTS FOR analyst; -- Attendu : GRANT SELECT sur 6 tables/vues ``` ### 09 — Table d'audit ```sql EXISTS ja4_processing.audit_logs; -- Attendu : 1 ``` ### 10 — Index de performance ```sql SELECT name FROM system.data_skipping_indices WHERE table = 'ml_detected_anomalies' AND database = 'ja4_processing'; -- Attendu : idx_detected_at, idx_threat_level, idx_bot_name ``` ### 11 — Vues dashboard ```sql SELECT name FROM system.tables WHERE database = 'ja4_processing' AND name LIKE 'view_%' AND engine = 'View'; -- Attendu : ≥ 7 vues (view_ip_recurrence, view_ai_features_1h, -- view_form_bruteforce_detected, view_host_ip_ja4_rotation, -- view_dashboard_user_agents, view_dashboard_entities, view_resource_cascade_1h) ``` ### 12 — Tables et vue de thèse ```sql EXISTS ja4_processing.agg_path_sequences_1h; EXISTS ja4_processing.agg_request_timing_1h; EXISTS ja4_processing.agg_ip_behavior_1h; EXISTS ja4_processing.agg_resource_cascade_1h; SELECT name FROM system.tables WHERE database = 'ja4_processing' AND name = 'view_thesis_features_1h'; -- Attendu : 1 pour chaque EXISTS, view_thesis_features_1h ``` ### Vérification complète ```sql -- Tables dans ja4_logs SELECT count() AS tables_logs FROM system.tables WHERE database = 'ja4_logs' AND name IN ('http_logs_raw', 'http_logs', 'mv_http_logs'); -- Attendu : 3 -- Tables dans ja4_processing SELECT count() AS tables_processing FROM system.tables WHERE database = 'ja4_processing' AND name IN ( 'ref_bot_networks', 'bot_ip', 'bot_ja4', 'anubis_ip_rules', 'anubis_asn_rules', 'agg_host_ip_ja4_1h', 'agg_header_fingerprint_1h', 'agg_path_sequences_1h', 'agg_request_timing_1h', 'agg_ip_behavior_1h', 'agg_resource_cascade_1h', 'ml_detected_anomalies', 'ml_all_scores', 'audit_logs' ); -- Attendu : 14 -- Dictionnaires SELECT count() AS dicts FROM system.dictionaries WHERE database = 'ja4_processing'; -- Attendu : 7 -- Vues matérialisées dans ja4_logs SELECT count() AS mvs_logs FROM system.tables WHERE database = 'ja4_logs' AND engine = 'MaterializedView'; -- Attendu : 1 -- Vues matérialisées dans ja4_processing SELECT count() AS mvs_proc FROM system.tables WHERE database = 'ja4_processing' AND engine = 'MaterializedView'; -- Attendu : 6 ``` --- ## Rollback ### Approche générale ClickHouse ne supporte pas les DDL transactionnels. Pour annuler une migration : 1. **Vues matérialisées** : supprimer la MV **avant** sa table cible 2. **Dictionnaires** : supprimer le dictionnaire avant les vues/MVs qui l'utilisent 3. **Tables** : `DROP TABLE IF EXISTS` 4. **Vues** : `DROP VIEW IF EXISTS` 5. **Utilisateurs** : `DROP USER IF EXISTS` ### Ordre de rollback (inverse de l'application) ```sql -- 12 : Tables et vue de thèse DROP VIEW IF EXISTS ja4_processing.view_thesis_features_1h; DROP VIEW IF EXISTS ja4_processing.view_resource_cascade_1h; DROP VIEW IF EXISTS ja4_processing.mv_agg_resource_cascade_1h; DROP VIEW IF EXISTS ja4_processing.mv_agg_ip_behavior_1h; DROP VIEW IF EXISTS ja4_processing.mv_agg_request_timing_1h; DROP VIEW IF EXISTS ja4_processing.mv_agg_path_sequences_1h; DROP TABLE IF EXISTS ja4_processing.agg_resource_cascade_1h; DROP TABLE IF EXISTS ja4_processing.agg_ip_behavior_1h; DROP TABLE IF EXISTS ja4_processing.agg_request_timing_1h; DROP TABLE IF EXISTS ja4_processing.agg_path_sequences_1h; -- 11 : Vues dashboard DROP VIEW IF EXISTS ja4_processing.view_dashboard_entities; DROP VIEW IF EXISTS ja4_processing.view_dashboard_user_agents; DROP VIEW IF EXISTS ja4_processing.view_host_ip_ja4_rotation; DROP VIEW IF EXISTS ja4_processing.view_form_bruteforce_detected; -- 10 : Index de performance (pas de rollback nécessaire — idempotent) -- 09 : Table d'audit DROP TABLE IF EXISTS ja4_processing.audit_logs; -- 08 : Utilisateurs DROP USER IF EXISTS data_writer; DROP USER IF EXISTS analyst; -- 07 : Vue de features AI DROP VIEW IF EXISTS ja4_processing.view_ai_features_1h; -- 06 : Tables ML DROP VIEW IF EXISTS ja4_processing.view_ip_recurrence; DROP TABLE IF EXISTS ja4_processing.ml_all_scores; DROP TABLE IF EXISTS ja4_processing.ml_detected_anomalies; -- 05 : Agrégations + dictionnaires de réputation DROP VIEW IF EXISTS ja4_processing.mv_agg_header_fingerprint_1h; DROP VIEW IF EXISTS ja4_processing.mv_agg_host_ip_ja4_1h; DROP TABLE IF EXISTS ja4_processing.agg_header_fingerprint_1h; DROP TABLE IF EXISTS ja4_processing.agg_host_ip_ja4_1h; DROP DICTIONARY IF EXISTS ja4_processing.dict_asn_reputation; DROP DICTIONARY IF EXISTS ja4_processing.dict_browser_ja4; DROP DICTIONARY IF EXISTS ja4_processing.dict_bot_ja4; DROP DICTIONARY IF EXISTS ja4_processing.dict_bot_ip; -- 04 : MV + http_logs DROP VIEW IF EXISTS ja4_logs.mv_http_logs; DROP TABLE IF EXISTS ja4_logs.http_logs; -- 03 : Anubis DROP DICTIONARY IF EXISTS ja4_processing.dict_anubis_asn; DROP DICTIONARY IF EXISTS ja4_processing.dict_anubis_ip; DROP TABLE IF EXISTS ja4_processing.anubis_asn_rules; DROP TABLE IF EXISTS ja4_processing.anubis_ip_rules; -- 02 : Dictionnaire ASN + tables de référence DROP DICTIONARY IF EXISTS ja4_processing.dict_iplocate_asn; DROP TABLE IF EXISTS ja4_processing.bot_ja4; DROP TABLE IF EXISTS ja4_processing.bot_ip; DROP TABLE IF EXISTS ja4_processing.ref_bot_networks; -- 01 : Table brute DROP TABLE IF EXISTS ja4_logs.http_logs_raw; -- 00 : Bases de données DROP DATABASE IF EXISTS ja4_processing; DROP DATABASE IF EXISTS ja4_logs; ``` ### Notes importantes - **Perte de données** : la suppression d'une table détruit toutes ses données. Toujours sauvegarder avant un rollback. - **Dépendance MV** : les vues matérialisées doivent être supprimées **avant** leur table cible. - **Dépendance dictionnaire** : les vues/MVs utilisant `dictGet()` échoueront si le dictionnaire référencé est supprimé. - **Ré-application idempotente** : après un rollback, les migrations peuvent être ré-appliquées sans risque grâce aux clauses `IF NOT EXISTS`. - **`04_mv_http_logs.sql`** est la version canonique de la vue matérialisée, remplaçant toute version antérieure dans `services/correlator/sql/init.sql`. --- ## Migrations post-déploiement Le répertoire `services/correlator/sql/migrations/` contient des instructions `ALTER TABLE` pour les déploiements existants. Les appliquer manuellement : ```bash clickhouse-client --multiquery < services/correlator/sql/migrations/.sql ``` Ces migrations sont distinctes du schéma de base et ne sont nécessaires que pour mettre à jour des instances déjà en production.