Initial commit: logcorrelator with unified packaging (DEB + RPM using fpm)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
521
architecture.yml
Normal file
521
architecture.yml
Normal file
@ -0,0 +1,521 @@
|
||||
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.toml
|
||||
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.toml
|
||||
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: toml
|
||||
location: /etc/logcorrelator/logcorrelator.toml
|
||||
description: >
|
||||
Toute la configuration est centralisée dans un fichier TOML lisible, stocké
|
||||
dans /etc/logcorrelator. Ni YAML ni JSON ne sont utilisés pour la config.
|
||||
reload_strategy: restart_service
|
||||
example: |
|
||||
[service]
|
||||
name = "logcorrelator"
|
||||
language = "go"
|
||||
|
||||
[[inputs.unix_sockets]]
|
||||
name = "apache_source"
|
||||
path = "/var/run/logcorrelator/apache.sock"
|
||||
format = "json"
|
||||
|
||||
[[inputs.unix_sockets]]
|
||||
name = "network_source"
|
||||
path = "/var/run/logcorrelator/network.sock"
|
||||
format = "json"
|
||||
|
||||
[outputs.file]
|
||||
enabled = true
|
||||
path = "/var/log/logcorrelator/correlated.log"
|
||||
|
||||
[outputs.clickhouse]
|
||||
enabled = true
|
||||
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
|
||||
|
||||
[correlation]
|
||||
key = ["src_ip", "src_port"]
|
||||
|
||||
[correlation.time_window]
|
||||
value = 1
|
||||
unit = "s"
|
||||
|
||||
[correlation.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 doit être distribué en package .rpm pour Rocky Linux (8+),
|
||||
construit intégralement dans Docker à partir du binaire compilé.[web:96][web:99][web:101]
|
||||
formats:
|
||||
- rpm
|
||||
target_distros:
|
||||
- rocky-linux-8+
|
||||
- rocky-linux-9+
|
||||
tool: fpm
|
||||
build_pipeline:
|
||||
steps:
|
||||
- name: build_binary_in_docker
|
||||
description: >
|
||||
Utiliser l’image builder pour compiler logcorrelator et installer le
|
||||
binaire dans un répertoire de staging (par ex. /tmp/pkgroot/usr/bin/logcorrelator).
|
||||
- name: prepare_filesystem_layout
|
||||
description: >
|
||||
Créer la hiérarchie :
|
||||
- /usr/bin/logcorrelator
|
||||
- /etc/logcorrelator/logcorrelator.toml (exemple)
|
||||
- /etc/systemd/system/logcorrelator.service (unit)
|
||||
- /var/log/logcorrelator (répertoire de logs)
|
||||
- name: run_fpm_in_docker
|
||||
description: >
|
||||
Lancer un conteneur fpm (par ex. image ruby:fpm) avec montage de
|
||||
/tmp/pkgroot, et exécuter fpm -s dir -t rpm pour générer le .rpm
|
||||
compatible Rocky Linux.
|
||||
- name: verify_rpm_on_rocky
|
||||
description: >
|
||||
Tester l’installation et le démarrage du service dans un conteneur
|
||||
Rocky Linux 8/9 (docker run --rm -it rockylinux:8), en installant le
|
||||
.rpm, en activant le service systemd et en vérifiant qu’il démarre
|
||||
correctement.
|
||||
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user