- Replace custom directive-based config parser with YAML using gopkg.in/yaml.v3 - Rename config.example.conf to config.example.yml with YAML syntax - Update default config path to /etc/logcorrelator/logcorrelator.yml - Update Dockerfile.package to copy YAML config files - Update packaging scripts to install logcorrelator.yml - Update architecture.yml to document YAML configuration - Add yaml.v3 dependency to go.mod Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
560 lines
18 KiB
YAML
560 lines
18 KiB
YAML
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 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:
|
||
- rocky-linux-8+
|
||
- rocky-linux-9+
|
||
- 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 (Rocky Linux/RHEL/CentOS), construits intégralement dans Docker avec fpm.
|
||
formats:
|
||
- deb
|
||
- rpm
|
||
target_distros:
|
||
deb:
|
||
- debian-12+
|
||
- ubuntu-22.04+
|
||
rpm:
|
||
- rocky-linux-8+
|
||
- rocky-linux-9+
|
||
- rhel-8+
|
||
- rhel-9+
|
||
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: package_builder
|
||
description: >
|
||
Installation de fpm, rpm, dpkg-dev. Création de l'arborescence
|
||
et exécution de fpm pour générer DEB et RPM.
|
||
- name: output
|
||
description: >
|
||
Image Alpine minimale contenant les packages dans /packages/deb et /packages/rpm.
|
||
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/deb/postinst
|
||
preun: packaging/deb/prerm
|
||
postun: packaging/deb/postrm
|
||
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:
|
||
command: docker run --rm -v $(pwd)/dist/rpm:/packages rockylinux:8 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.
|
||
|