service: name: logcorrelator context: http-network-correlation language: go pattern: hexagonal description: > logcorrelator est un service système (lancé par systemd) écrit en Go, chargé de recevoir deux flux de logs JSON via des sockets Unix, de corréler les événements HTTP applicatifs (source A, typiquement Apache ou reverse proxy) avec des événements réseau (source B, métadonnées IP/TCP, JA3/JA4, etc.) sur la base de la combinaison strictement définie src_ip + src_port, avec une fenêtre temporelle configurable. Le service produit un log corrélé unique pour chaque paire correspondante, émet toujours les événements A même lorsqu’aucun événement B corrélé n’est disponible, n’émet jamais de logs B seuls, et pousse les logs agrégés en temps quasi réel vers ClickHouse et/ou un fichier local, en minimisant la rétention en mémoire et sur disque. runtime: deployment: unit_type: systemd description: > logcorrelator est livré sous forme de binaire autonome, exécuté comme un service systemd. L'unité systemd assure le démarrage automatique au boot, le redémarrage en cas de crash, et une intégration standard dans l'écosystème Linux (notamment sur CentOS 7 et Rocky Linux 8+). binary_path: /usr/bin/logcorrelator config_path: /etc/logcorrelator/logcorrelator.yml user: logcorrelator group: logcorrelator restart: on-failure systemd_unit: path: /etc/systemd/system/logcorrelator.service content_example: | [Unit] Description=logcorrelator service After=network.target [Service] Type=simple User=logcorrelator Group=logcorrelator ExecStart=/usr/bin/logcorrelator -config /etc/logcorrelator/logcorrelator.yml Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target os: supported: - centos-7 - rocky-linux-8+ - rocky-linux-9+ - rocky-linux-10+ - autres-linux-recentes logs: stdout_stderr: journald structured: true description: > Les logs internes du service (erreurs, messages d’information) sont envoyés vers stdout/stderr et collectés par journald. Ils sont structurés et ne contiennent pas de données personnelles. signals: graceful_shutdown: - SIGINT - SIGTERM description: > En réception de SIGINT ou SIGTERM, le service arrête proprement la lecture des sockets Unix, vide les buffers d’envoi (dans les limites de la politique de drop), ferme les connexions ClickHouse puis s’arrête. config: format: yaml location: /etc/logcorrelator/logcorrelator.yml description: > Toute la configuration est centralisée dans un fichier YAML lisible, stocké dans /etc/logcorrelator. reload_strategy: restart_service example: | # Service configuration service: name: logcorrelator language: go # Input sources (at least 2 required) inputs: unix_sockets: - name: apache_source path: /var/run/logcorrelator/apache.sock format: json - name: network_source path: /var/run/logcorrelator/network.sock format: json # File output outputs: file: enabled: true path: /var/log/logcorrelator/correlated.log # ClickHouse output outputs: clickhouse: enabled: false dsn: clickhouse://user:pass@localhost:9000/db table: correlated_logs_http_network batch_size: 500 # Correlation configuration correlation: key: - src_ip - src_port time_window: value: 1 unit: s orphan_policy: apache_always_emit: true network_emit: false inputs: description: > Le service consomme deux flux de logs JSON via des sockets Unix. Le schéma exact des logs pour chaque source est flexible et peut évoluer. Seuls quelques champs sont nécessaires pour la corrélation. unix_sockets: - name: apache_source id: A description: > Source A, destinée aux logs HTTP applicatifs (Apache, reverse proxy, etc.). Le schéma JSON est variable, avec un champ timestamp numérique obligatoire et des champs header_* dynamiques. path: /var/run/logcorrelator/apache.sock protocol: unix mode: stream format: json framing: line retry_on_error: true - name: network_source id: B description: > Source B, destinée aux logs réseau (métadonnées IP/TCP, JA3/JA4, etc.). Le schéma JSON est variable ; seuls src_ip et src_port sont requis. path: /var/run/logcorrelator/network.sock protocol: unix mode: stream format: json framing: line retry_on_error: true outputs: description: > Les logs corrélés sont envoyés vers un ou plusieurs sinks. MultiSink permet de diffuser chaque log corrélé vers plusieurs destinations (fichier, ClickHouse, stdout…). sinks: file: enabled: true description: > Sink vers fichier local, utile pour debug ou archivage local. Écrit un JSON par ligne dans le chemin configuré. Rotation gérée par logrotate ou équivalent. path: /var/log/logcorrelator/correlated.log format: json_lines rotate_managed_by: external clickhouse: enabled: true description: > Sink principal pour l’archivage et l’analyse en temps quasi réel. Les logs corrélés sont insérés en batch dans ClickHouse avec un small buffer et des inserts asynchrones. En cas de saturation ou d’indisponibilité ClickHouse, les logs sont drop pour éviter de saturer la machine locale. dsn: clickhouse://user:pass@host:9000/db table: correlated_logs_http_network batch_size: 500 flush_interval_ms: 200 max_buffer_size: 5000 drop_on_overflow: true async_insert: true timeout_ms: 1000 stdout: enabled: false description: > Sink optionnel vers stdout pour les tests et le développement. correlation: description: > Corrélation strictement basée sur src_ip + src_port et une fenêtre temporelle configurable. Aucun autre champ (dst_ip, dst_port, JA3/JA4, headers HTTP...) n’est utilisé pour la décision de corrélation. key: - src_ip - src_port time_window: value: 1 unit: s description: > Fenêtre de temps symétrique appliquée aux timestamps de A et B. Deux événements sont corrélés si |tA - tB| <= time_window. La valeur et l’unité sont définies dans le TOML. timestamp_source: apache: field_timestamp network: reception_time description: > Pour A, utilisation du champ numérique "timestamp" (epoch ns). Pour B, utilisation du temps de réception local. orphan_policy: apache_always_emit: true network_emit: false description: > A est toujours émis (même sans B) avec correlated=false et orphan_side="A". B n’est jamais émis seul. matching: mode: one_to_one_first_match description: > Stratégie 1‑à‑1, premier match : lors de l’arrivée d’un événement, on cherche le premier événement compatible dans le buffer de l’autre source. Les autres restent en attente ou expirent. schema: description: > Les schémas des sources A et B sont variables. Le service impose seulement quelques champs obligatoires nécessaires à la corrélation et accepte des champs supplémentaires sans modification de code. source_A: description: > Logs HTTP applicatifs (Apache/reverse proxy) au format JSON. Schéma variable, avec champs obligatoires pour corrélation (src_ip, src_port, timestamp) et collecte des autres champs dans des maps. required_fields: - name: src_ip type: string description: Adresse IP source client. - name: src_port type: int description: Port source client. - name: timestamp type: int64 unit: ns description: Timestamp de référence pour la corrélation. optional_fields: - name: time type: string format: rfc3339 - name: dst_ip type: string - name: dst_port type: int - name: method type: string - name: path type: string - name: host type: string - name: http_version type: string dynamic_fields: - pattern: header_* target_map: headers description: > Tous les champs header_* sont collectés dans headers[clé] = valeur. - pattern: "*" target_map: extra description: > Tous les champs non reconnus explicitement vont dans extra. source_B: description: > Logs réseau JSON (IP/TCP, JA3/JA4...). Schéma variable. src_ip et src_port sont obligatoires pour la corrélation, le reste est libre. required_fields: - name: src_ip type: string - name: src_port type: int optional_fields: - name: dst_ip type: string - name: dst_port type: int dynamic_fields: - pattern: "*" target_map: extra description: > Tous les autres champs (ip_meta_*, tcp_meta_*, ja3, ja4, etc.) sont rangés dans extra. normalized_event: description: > Représentation interne unifiée des événements A/B sur laquelle opère la logique de corrélation. fields: - name: source type: enum("A","B") - name: timestamp type: time.Time - name: src_ip type: string - name: src_port type: int - name: dst_ip type: string optional: true - name: dst_port type: int optional: true - name: headers type: map[string]string optional: true - name: extra type: map[string]any description: Champs additionnels provenant de A ou B. correlated_log: description: > Structure du log corrélé émis vers les sinks (fichier, ClickHouse). Contient les informations de corrélation, les infos communes et les contenus de A/B. fields: - name: timestamp type: time.Time - name: src_ip type: string - name: src_port type: int - name: dst_ip type: string optional: true - name: dst_port type: int optional: true - name: correlated type: bool - name: orphan_side type: string - name: apache type: map[string]any optional: true - name: network type: map[string]any optional: true - name: extra type: map[string]any description: Champs dérivés éventuels. clickhouse_schema: strategy: external_ddls description: > logcorrelator ne gère pas les ALTER TABLE. La table ClickHouse doit être créée/modifiée en dehors du service. logcorrelator remplit les colonnes existantes qu’il connaît et met NULL si un champ manque. base_columns: - name: timestamp type: DateTime64(9) - name: src_ip type: String - name: src_port type: UInt32 - name: dst_ip type: String - name: dst_port type: UInt32 - name: correlated type: UInt8 - name: orphan_side type: String - name: apache type: JSON - name: network type: JSON dynamic_fields: mode: map_or_additional_columns description: > Les champs dynamiques peuvent être exposés via colonnes dédiées créées par migration, ou via Map/JSON. architecture: description: > Architecture hexagonale : domaine de corrélation indépendant, ports abstraits pour les sources/sinks, adaptateurs pour sockets Unix, fichier et ClickHouse, couche application d’orchestration, et modules infra pour config/observabilité. modules: - name: cmd/logcorrelator type: entrypoint responsibilities: - Chargement configuration TOML. - Initialisation des adaptateurs d’entrée/sortie. - Création du CorrelationService. - Démarrage de l’orchestrateur. - Gestion du cycle de vie (signaux systemd). - name: internal/domain type: domain responsibilities: - Modèles NormalizedEvent et CorrelatedLog. - Implémentation de CorrelationService (buffers, fenêtre, orphelins). - name: internal/ports type: ports responsibilities: - EventSource, CorrelatedLogSink, TimeProvider. - name: internal/app type: application responsibilities: - Orchestrator : relier EventSource → CorrelationService → MultiSink. - name: internal/adapters/inbound/unixsocket type: adapter_inbound responsibilities: - Lecture sockets Unix + parsing JSON → NormalizedEvent. - name: internal/adapters/outbound/file type: adapter_outbound responsibilities: - Écriture fichier JSON lines. - name: internal/adapters/outbound/clickhouse type: adapter_outbound responsibilities: - Bufferisation + inserts batch vers ClickHouse. - Application de drop_on_overflow. - name: internal/adapters/outbound/multi type: adapter_outbound responsibilities: - Fan-out vers plusieurs sinks. - name: internal/config type: infrastructure responsibilities: - Chargement/validation config TOML. - name: internal/observability type: infrastructure responsibilities: - Logging et métriques internes. testing: unit: description: > Tests unitaires table-driven avec couverture cible ≥ 80 %. Focalisés sur la logique de corrélation, parsing et sink ClickHouse.[web:94][web:98][web:102] coverage_minimum: 0.8 focus: - CorrelationService - Parsing A/B → NormalizedEvent - ClickHouseSink (batching, overflow) - MultiSink integration: description: > Tests d’intégration validant le flux complet A+B → corrélation → sinks, avec sockets simulés et ClickHouse mocké. docker: description: > Build et tests entièrement encapsulés dans Docker, avec multi‑stage build : un stage builder pour compiler et tester, un stage runtime minimal pour exécuter le service.[web:95][web:103] images: builder: base: golang:latest purpose: build_and_test runtime: base: gcr.io/distroless/base-debian12 purpose: run_binary_only build: multi_stage: true steps: - name: unit_tests description: > go test ./... avec génération de couverture. Le build échoue si la couverture est < 80 %. - name: compile_binary description: > Compilation CGO_ENABLED=0, GOOS=linux, GOARCH=amd64 pour un binaire statique /usr/bin/logcorrelator. - name: assemble_runtime_image description: > Copie du binaire dans l’image runtime et définition de l’ENTRYPOINT. packaging: description: > logcorrelator est distribué sous forme de packages .deb (Debian/Ubuntu) et .rpm (CentOS 7, Rocky Linux/RHEL 8, 9, 10), construits intégralement dans Docker avec fpm. formats: - deb - rpm target_distros: deb: - debian-12+ - ubuntu-22.04+ rpm: - centos-7 - rocky-linux-8+ - rocky-linux-9+ - rocky-linux-10+ - rhel-7+ - rhel-8+ - rhel-9+ - rhel-10+ tool: fpm build_pipeline: dockerfile: Dockerfile.package stages: - name: builder description: > Compilation du binaire Go avec CGO_ENABLED=0 pour un binaire statique. GOOS=linux GOARCH=amd64. - name: deb_package_builder description: > Construction du package DEB pour Debian/Ubuntu avec fpm. - name: rpm_centos7_builder description: > Construction du package RPM pour CentOS 7 (el7) avec fpm. - name: rpm_rocky8_builder description: > Construction du package RPM pour Rocky Linux 8 (el8) avec fpm. - name: rpm_rocky9_builder description: > Construction du package RPM pour Rocky Linux 9 (el9) avec fpm. - name: rpm_rocky10_builder description: > Construction du package RPM pour Rocky Linux 10 (el10) avec fpm. - name: output description: > Image Alpine minimale contenant les packages dans /packages/deb et /packages/rpm/{centos7,rocky8,rocky9,rocky10}. files: binary: source: dist/logcorrelator dest: /usr/bin/logcorrelator mode: "0755" config: - source: config.example.yml dest: /etc/logcorrelator/logcorrelator.yml mode: "0640" config_file: true - source: config.example.yml dest: /usr/share/logcorrelator/logcorrelator.yml.example mode: "0640" directories: - path: /var/log/logcorrelator mode: "0755" - path: /var/run/logcorrelator mode: "0755" - path: /etc/logcorrelator mode: "0750" maintainer_scripts: deb: postinst: packaging/deb/postinst prerm: packaging/deb/prerm postrm: packaging/deb/postrm rpm: post: packaging/rpm/post preun: packaging/rpm/preun postun: packaging/rpm/postun dependencies: deb: - systemd rpm: - systemd verify: deb: command: docker run --rm -v $(pwd)/dist/deb:/packages debian:latest sh -c "apt-get update && apt-get install -y /packages/*.deb" rpm: centos7: command: docker run --rm -v $(pwd)/dist/rpm/centos7:/packages centos:7 sh -c "yum install -y /packages/*.rpm" rocky8: command: docker run --rm -v $(pwd)/dist/rpm/rocky8:/packages rockylinux:8 sh -c "dnf install -y /packages/*.rpm" rocky9: command: docker run --rm -v $(pwd)/dist/rpm/rocky9:/packages rockylinux:9 sh -c "dnf install -y /packages/*.rpm" rocky10: command: docker run --rm -v $(pwd)/dist/rpm/rocky10:/packages rockylinux:10 sh -c "dnf install -y /packages/*.rpm" non_functional: performance: target_latency_ms: 1000 description: > Latence visée < 1 s entre réception et insertion ClickHouse, avec batching léger. reliability: drop_on_clickhouse_failure: true description: > En cas de ClickHouse lent/HS, les logs sont drop au‑delà du buffer pour protéger la machine. security: user_separation: true privileges: least description: > Service sous utilisateur dédié, pas de secrets en clair dans les logs, principe de moindre privilège.