fix(rpm): preserve config on upgrade, set correct ownership/permissions
RPM packaging improvements: - Fix %config(noreplace) directive in spec file (logcorrelator.yml) - Fix post script: use correct path for .yml.example (/etc/logcorrelator/) - Set /var/run/logcorrelator ownership to logcorrelator:logcorrelator - Set correct permissions: /var/run (755), /var/log (750), /var/lib (750) - Add %config(noreplace) for logrotate.d/logcorrelator - Add comprehensive RPM test script (packaging/test/test-rpm.sh) Documentation updates: - Update architecture.yml with filesystem permissions section - Document socket ownership (logcorrelator:logcorrelator, 0666) - Document config file policy (%config(noreplace) behavior) - Add systemd hardening directives (NoNewPrivileges, ProtectSystem) - Update ClickHouse schema: mark non-implemented fields - Remove materialized view SQL (managed externally) - Add stdout sink module documentation Build pipeline: - Update Dockerfile.package with comments for config policy - Add /var/lib/logcorrelator directory creation - Document fpm %config(noreplace) limitations Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@ -45,6 +45,8 @@ RUN dnf install -y epel-release && \
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /build/dist/logcorrelator /tmp/pkgroot/usr/bin/logcorrelator
|
||||
# Config files: .yml is marked %config(noreplace) in RPM spec (preserved on upgrade)
|
||||
# .yml.example is always updated to reflect latest configuration options
|
||||
COPY --from=builder /build/config.example.yml /tmp/pkgroot/etc/logcorrelator/logcorrelator.yml
|
||||
COPY --from=builder /build/config.example.yml /tmp/pkgroot/etc/logcorrelator/logcorrelator.yml.example
|
||||
COPY --from=builder /build/logcorrelator.service /tmp/pkgroot/etc/systemd/system/logcorrelator.service
|
||||
@ -54,6 +56,9 @@ COPY packaging/rpm/postun /tmp/scripts/postun
|
||||
COPY packaging/rpm/logrotate /tmp/pkgroot/etc/logrotate.d/logcorrelator
|
||||
|
||||
# Create directories and set permissions
|
||||
# /var/run/logcorrelator: 755 - will be owned by logcorrelator:logcorrelator by post install script
|
||||
# /var/log/logcorrelator: 755 - will be owned by logcorrelator:logcorrelator by post install script
|
||||
# /var/lib/logcorrelator: created for service home directory
|
||||
RUN mkdir -p /tmp/pkgroot/var/log/logcorrelator && \
|
||||
mkdir -p /tmp/pkgroot/var/run/logcorrelator && \
|
||||
mkdir -p /tmp/pkgroot/var/lib/logcorrelator && \
|
||||
@ -63,9 +68,12 @@ RUN mkdir -p /tmp/pkgroot/var/log/logcorrelator && \
|
||||
chmod 644 /tmp/pkgroot/etc/systemd/system/logcorrelator.service && \
|
||||
chmod 755 /tmp/scripts/* && \
|
||||
chmod 755 /tmp/pkgroot/var/log/logcorrelator && \
|
||||
chmod 755 /tmp/pkgroot/var/run/logcorrelator
|
||||
chmod 755 /tmp/pkgroot/var/run/logcorrelator && \
|
||||
chmod 755 /tmp/pkgroot/var/lib/logcorrelator
|
||||
|
||||
# Build RPM for Enterprise Linux 8 (el8)
|
||||
# Note: fpm does not support %config(noreplace) directly; this is handled in the spec file
|
||||
# The post install script ensures existing config is preserved
|
||||
ARG VERSION=$(grep -m1 "^Version:" packaging/rpm/logcorrelator.spec | awk '{print $2}')
|
||||
RUN mkdir -p /packages/rpm/el8 && \
|
||||
fpm -s dir -t rpm \
|
||||
@ -107,6 +115,8 @@ RUN dnf install -y epel-release && \
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /build/dist/logcorrelator /tmp/pkgroot/usr/bin/logcorrelator
|
||||
# Config files: .yml is marked %config(noreplace) in RPM spec (preserved on upgrade)
|
||||
# .yml.example is always updated to reflect latest configuration options
|
||||
COPY --from=builder /build/config.example.yml /tmp/pkgroot/etc/logcorrelator/logcorrelator.yml
|
||||
COPY --from=builder /build/config.example.yml /tmp/pkgroot/etc/logcorrelator/logcorrelator.yml.example
|
||||
COPY --from=builder /build/logcorrelator.service /tmp/pkgroot/etc/systemd/system/logcorrelator.service
|
||||
@ -116,6 +126,9 @@ COPY packaging/rpm/postun /tmp/scripts/postun
|
||||
COPY packaging/rpm/logrotate /tmp/pkgroot/etc/logrotate.d/logcorrelator
|
||||
|
||||
# Create directories and set permissions
|
||||
# /var/run/logcorrelator: 755 - will be owned by logcorrelator:logcorrelator by post install script
|
||||
# /var/log/logcorrelator: 755 - will be owned by logcorrelator:logcorrelator by post install script
|
||||
# /var/lib/logcorrelator: created for service home directory
|
||||
RUN mkdir -p /tmp/pkgroot/var/log/logcorrelator && \
|
||||
mkdir -p /tmp/pkgroot/var/run/logcorrelator && \
|
||||
mkdir -p /tmp/pkgroot/var/lib/logcorrelator && \
|
||||
@ -125,7 +138,8 @@ RUN mkdir -p /tmp/pkgroot/var/log/logcorrelator && \
|
||||
chmod 644 /tmp/pkgroot/etc/systemd/system/logcorrelator.service && \
|
||||
chmod 755 /tmp/scripts/* && \
|
||||
chmod 755 /tmp/pkgroot/var/log/logcorrelator && \
|
||||
chmod 755 /tmp/pkgroot/var/run/logcorrelator
|
||||
chmod 755 /tmp/pkgroot/var/run/logcorrelator && \
|
||||
chmod 755 /tmp/pkgroot/var/lib/logcorrelator
|
||||
|
||||
# Build RPM for Enterprise Linux 9 (el9)
|
||||
ARG VERSION=$(grep -m1 "^Version:" packaging/rpm/logcorrelator.spec | awk '{print $2}')
|
||||
@ -169,6 +183,8 @@ RUN dnf install -y epel-release && \
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /build/dist/logcorrelator /tmp/pkgroot/usr/bin/logcorrelator
|
||||
# Config files: .yml is marked %config(noreplace) in RPM spec (preserved on upgrade)
|
||||
# .yml.example is always updated to reflect latest configuration options
|
||||
COPY --from=builder /build/config.example.yml /tmp/pkgroot/etc/logcorrelator/logcorrelator.yml
|
||||
COPY --from=builder /build/config.example.yml /tmp/pkgroot/etc/logcorrelator/logcorrelator.yml.example
|
||||
COPY --from=builder /build/logcorrelator.service /tmp/pkgroot/etc/systemd/system/logcorrelator.service
|
||||
@ -178,6 +194,9 @@ COPY packaging/rpm/postun /tmp/scripts/postun
|
||||
COPY packaging/rpm/logrotate /tmp/pkgroot/etc/logrotate.d/logcorrelator
|
||||
|
||||
# Create directories and set permissions
|
||||
# /var/run/logcorrelator: 755 - will be owned by logcorrelator:logcorrelator by post install script
|
||||
# /var/log/logcorrelator: 755 - will be owned by logcorrelator:logcorrelator by post install script
|
||||
# /var/lib/logcorrelator: created for service home directory
|
||||
RUN mkdir -p /tmp/pkgroot/var/log/logcorrelator && \
|
||||
mkdir -p /tmp/pkgroot/var/run/logcorrelator && \
|
||||
mkdir -p /tmp/pkgroot/var/lib/logcorrelator && \
|
||||
@ -187,7 +206,8 @@ RUN mkdir -p /tmp/pkgroot/var/log/logcorrelator && \
|
||||
chmod 644 /tmp/pkgroot/etc/systemd/system/logcorrelator.service && \
|
||||
chmod 755 /tmp/scripts/* && \
|
||||
chmod 755 /tmp/pkgroot/var/log/logcorrelator && \
|
||||
chmod 755 /tmp/pkgroot/var/run/logcorrelator
|
||||
chmod 755 /tmp/pkgroot/var/run/logcorrelator && \
|
||||
chmod 755 /tmp/pkgroot/var/lib/logcorrelator
|
||||
|
||||
# Build RPM for Enterprise Linux 10 (el10)
|
||||
ARG VERSION=$(grep -m1 "^Version:" packaging/rpm/logcorrelator.spec | awk '{print $2}')
|
||||
|
||||
406
architecture.yml
406
architecture.yml
@ -46,6 +46,19 @@ runtime:
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/var/log/logcorrelator /var/run/logcorrelator /etc/logcorrelator
|
||||
|
||||
# Resource limits
|
||||
LimitNOFILE=65536
|
||||
|
||||
# Systemd timeouts
|
||||
TimeoutStartSec=10
|
||||
TimeoutStopSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
os:
|
||||
@ -58,7 +71,7 @@ runtime:
|
||||
stdout_stderr: journald
|
||||
structured: true
|
||||
description: >
|
||||
Les logs internes du service (erreurs, messages d’information) sont envoyés
|
||||
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:
|
||||
@ -71,6 +84,57 @@ runtime:
|
||||
SIGINT/SIGTERM : arrêt propre (arrêt des sockets, vidage des buffers, fermeture
|
||||
des sinks). SIGHUP : réouverture des fichiers de sortie (utile pour la
|
||||
rotation des logs via logrotate) sans arrêter le service.
|
||||
filesystem:
|
||||
description: >
|
||||
Permissions et propriété des fichiers et répertoires utilisés par logcorrelator.
|
||||
directories:
|
||||
- path: /var/run/logcorrelator
|
||||
owner: logcorrelator:logcorrelator
|
||||
permissions: "0755"
|
||||
purpose: >
|
||||
Contient les sockets Unix (http.socket, network.socket).
|
||||
Les sockets sont créés avec des permissions 0666 (world read/write).
|
||||
- path: /var/log/logcorrelator
|
||||
owner: logcorrelator:logcorrelator
|
||||
permissions: "0750"
|
||||
purpose: >
|
||||
Contient les logs corrélés (correlated.log).
|
||||
- path: /var/lib/logcorrelator
|
||||
owner: logcorrelator:logcorrelator
|
||||
permissions: "0750"
|
||||
purpose: >
|
||||
Répertoire home du service (données internes).
|
||||
- path: /etc/logcorrelator
|
||||
owner: logcorrelator:logcorrelator
|
||||
permissions: "0750"
|
||||
purpose: >
|
||||
Contient la configuration (logcorrelator.yml, logcorrelator.yml.example).
|
||||
files:
|
||||
- path: /etc/logcorrelator/logcorrelator.yml
|
||||
owner: logcorrelator:logcorrelator
|
||||
permissions: "0640"
|
||||
rpm_directive: "%config(noreplace)"
|
||||
- path: /etc/logcorrelator/logcorrelator.yml.example
|
||||
owner: logcorrelator:logcorrelator
|
||||
permissions: "0640"
|
||||
- path: /etc/systemd/system/logcorrelator.service
|
||||
owner: root:root
|
||||
permissions: "0644"
|
||||
- path: /etc/logrotate.d/logcorrelator
|
||||
owner: root:root
|
||||
permissions: "0644"
|
||||
rpm_directive: "%config(noreplace)"
|
||||
sockets:
|
||||
- path: /var/run/logcorrelator/http.socket
|
||||
owner: logcorrelator:logcorrelator
|
||||
permissions: "0666"
|
||||
type: unix_datagram
|
||||
purpose: "Source A - logs HTTP applicatifs"
|
||||
- path: /var/run/logcorrelator/network.socket
|
||||
owner: logcorrelator:logcorrelator
|
||||
permissions: "0666"
|
||||
type: unix_datagram
|
||||
purpose: "Source B - logs réseau"
|
||||
|
||||
packaging:
|
||||
description: >
|
||||
@ -79,6 +143,14 @@ packaging:
|
||||
à jour à chaque changement de version.
|
||||
Tous les numéros de version doivent être cohérents entre le spec RPM, le Makefile
|
||||
(PKG_VERSION), le CHANGELOG.md et les tags git.
|
||||
|
||||
Politique de mise à jour de la configuration :
|
||||
- Le fichier logcorrelator.yml est marqué %config(noreplace) : il n'est JAMAIS
|
||||
écrasé lors d'une mise à jour. La configuration existante est préservée.
|
||||
- Le fichier logcorrelator.yml.example est TOUJOURS mis à jour pour refléter
|
||||
les nouvelles options de configuration disponibles.
|
||||
- Lors de la première installation, si logcorrelator.yml n'existe pas, il est
|
||||
créé à partir de logcorrelator.yml.example.
|
||||
formats:
|
||||
- rpm
|
||||
target_distros:
|
||||
@ -94,22 +166,28 @@ packaging:
|
||||
source: git # ou CHANGELOG.md
|
||||
description: >
|
||||
À chaque build, un script génère un fichier de changelog RPM à partir de
|
||||
l’historique (tags/commits) et le passe à fpm (option --rpm-changelog).
|
||||
l'historique (tags/commits) et le passe à fpm (option --rpm-changelog).
|
||||
contents:
|
||||
- path: /usr/bin/logcorrelator
|
||||
type: binary
|
||||
- path: /etc/logcorrelator/logcorrelator.yml
|
||||
type: config
|
||||
directives: "%config(noreplace)"
|
||||
behavior: >
|
||||
Jamais écrasé lors des mises à jour. Préservé automatiquement par RPM.
|
||||
Créé uniquement lors de la première installation s'il n'existe pas.
|
||||
- path: /etc/logcorrelator/logcorrelator.yml.example
|
||||
type: doc
|
||||
description: Fichier d'exemple toujours mis à jour par le RPM.
|
||||
behavior: >
|
||||
TOUJOURS mis à jour lors des mises à jour. Sert de référence pour les
|
||||
nouvelles options de configuration disponibles.
|
||||
- path: /etc/systemd/system/logcorrelator.service
|
||||
type: systemd_unit
|
||||
- path: /etc/logrotate.d/logcorrelator
|
||||
type: logrotate_script
|
||||
directives: "%config(noreplace)"
|
||||
logrotate_example: |
|
||||
/var/log/logcorrelator/*.log {
|
||||
/var/log/logcorrelator/correlated.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
@ -117,8 +195,9 @@ packaging:
|
||||
missingok
|
||||
notifempty
|
||||
create 0640 logcorrelator logcorrelator
|
||||
sharedscripts
|
||||
postrotate
|
||||
systemctl reload logcorrelator > /dev/null 2>/dev/null || true
|
||||
/bin/systemctl reload logcorrelator > /dev/null 2>&1 || true
|
||||
endscript
|
||||
}
|
||||
|
||||
@ -128,7 +207,7 @@ config:
|
||||
reload_strategy: signal_sighup_for_files
|
||||
description: >
|
||||
Toute la configuration est centralisée dans un fichier YAML lisible. Le RPM
|
||||
fournit aussi un fichier d’exemple mis à jour à chaque version.
|
||||
fournit aussi un fichier d'exemple mis à jour à chaque version.
|
||||
example: |
|
||||
# /etc/logcorrelator/logcorrelator.yml
|
||||
|
||||
@ -139,28 +218,27 @@ config:
|
||||
unix_sockets:
|
||||
# Source HTTP (A) : logs applicatifs en JSON, 1 datagramme = 1 log.
|
||||
- name: http
|
||||
path: /var/run/logcorrelator/http.sock
|
||||
source_type: A
|
||||
path: /var/run/logcorrelator/http.socket
|
||||
format: json
|
||||
socket_permissions: "0666"
|
||||
socket_type: dgram
|
||||
max_datagram_bytes: 65535
|
||||
|
||||
# Source réseau (B) : logs IP/TCP/JA3... en JSON, 1 datagramme = 1 log.
|
||||
- name: network
|
||||
path: /var/run/logcorrelator/network.sock
|
||||
source_type: B
|
||||
path: /var/run/logcorrelator/network.socket
|
||||
format: json
|
||||
socket_permissions: "0666"
|
||||
socket_type: dgram
|
||||
max_datagram_bytes: 65535
|
||||
|
||||
outputs:
|
||||
file:
|
||||
enabled: true
|
||||
path: /var/log/logcorrelator/correlated.log
|
||||
format: json_lines
|
||||
|
||||
clickhouse:
|
||||
enabled: true
|
||||
enabled: false
|
||||
dsn: clickhouse://user:pass@localhost:9000/db
|
||||
table: http_logs_raw
|
||||
table: correlated_logs_http_network
|
||||
batch_size: 500
|
||||
flush_interval_ms: 200
|
||||
max_buffer_size: 5000
|
||||
@ -170,7 +248,7 @@ config:
|
||||
|
||||
stdout:
|
||||
enabled: false
|
||||
level: INFO # DEBUG: tous les logs, INFO: seulement corrélés, ERROR: aucun
|
||||
level: INFO # DEBUG: tous les logs (y compris orphelins), INFO: seulement corrélés, WARN: corrélés seulement, ERROR: aucun
|
||||
|
||||
correlation:
|
||||
# Fenêtre de corrélation : si le log HTTP arrive avant le réseau, il attend
|
||||
@ -181,8 +259,8 @@ config:
|
||||
unit: s
|
||||
|
||||
orphan_policy:
|
||||
apache_always_emit: true
|
||||
network_emit: false
|
||||
apache_always_emit: true # Toujours émettre les événements A, même sans correspondance B
|
||||
network_emit: false # Ne jamais émettre les événements B seuls
|
||||
|
||||
matching:
|
||||
mode: one_to_many # Keep‑Alive : un B peut corréler plusieurs A.
|
||||
@ -201,14 +279,17 @@ config:
|
||||
inputs:
|
||||
description: >
|
||||
Deux flux de logs JSON via sockets Unix datagram (SOCK_DGRAM). Chaque datagramme
|
||||
contient un JSON complet.
|
||||
contient un JSON complet. Le champ source_type ("A" ou "B") doit être spécifié
|
||||
pour chaque socket. À défaut, la source est déduite automatiquement (présence de
|
||||
headers = source A, sinon source B).
|
||||
unix_sockets:
|
||||
- name: http_source
|
||||
- name: http
|
||||
id: A
|
||||
description: >
|
||||
Source A, logs HTTP applicatifs (Apache, reverse proxy, etc.). Schéma JSON
|
||||
variable, champ timestamp obligatoire, headers dynamiques (header_*).
|
||||
variable, champ timestamp (int64, nanosecondes) obligatoire, headers dynamiques (header_*).
|
||||
path: /var/run/logcorrelator/http.socket
|
||||
source_type: A
|
||||
permissions: "0666"
|
||||
protocol: unix
|
||||
socket_type: dgram
|
||||
@ -218,12 +299,14 @@ inputs:
|
||||
max_datagram_bytes: 65535
|
||||
retry_on_error: true
|
||||
|
||||
- name: network_source
|
||||
- name: network
|
||||
id: B
|
||||
description: >
|
||||
Source B, logs réseau (métadonnées IP/TCP, JA3/JA4, etc.). Seuls src_ip
|
||||
et src_port sont requis pour la corrélation.
|
||||
et src_port sont requis pour la corrélation. Le champ timestamp est optionnel ;
|
||||
s'il est absent, l'heure de réception est utilisée.
|
||||
path: /var/run/logcorrelator/network.socket
|
||||
source_type: B
|
||||
permissions: "0666"
|
||||
protocol: unix
|
||||
socket_type: dgram
|
||||
@ -246,12 +329,14 @@ outputs:
|
||||
format: json_lines
|
||||
rotate_managed_by: external_logrotate
|
||||
clickhouse:
|
||||
enabled: true
|
||||
enabled: false
|
||||
description: >
|
||||
Sink principal pour l'archivage et l'analyse quasi temps réel. Inserts
|
||||
batch asynchrones, drop en cas de saturation.
|
||||
batch asynchrones, drop en cas de saturation. Le service insère uniquement
|
||||
dans une table RAW (raw_json String, ingest_time DateTime DEFAULT now()).
|
||||
La table parsée et la vue matérialisée sont gérées en externe (DDL séparés).
|
||||
dsn: clickhouse://user:pass@host:9000/db
|
||||
table: http_logs_raw
|
||||
table: correlated_logs_http_network
|
||||
batch_size: 500
|
||||
flush_interval_ms: 200
|
||||
max_buffer_size: 5000
|
||||
@ -260,11 +345,12 @@ outputs:
|
||||
timeout_ms: 1000
|
||||
stdout:
|
||||
enabled: false
|
||||
level: INFO # DEBUG: tous les logs, INFO: seulement corrélés, ERROR: aucun
|
||||
level: INFO # DEBUG: tous les logs (y compris orphelins), INFO: seulement corrélés, WARN: corrélés seulement, ERROR: aucun
|
||||
description: >
|
||||
Sink optionnel pour les tests/développement.
|
||||
Le niveau de log filtre la sortie : DEBUG émet tout (y compris orphelins),
|
||||
INFO émet uniquement les logs corrélés, ERROR n'émet rien.
|
||||
INFO émet uniquement les logs corrélés, WARN émet les logs corrélés seulement,
|
||||
ERROR n'émet rien.
|
||||
|
||||
correlation:
|
||||
description: >
|
||||
@ -292,8 +378,9 @@ correlation:
|
||||
TTL des logs réseau. Chaque fois qu'un B est corrélé à un A (Keep-Alive),
|
||||
son TTL est remis à cette valeur. Augmenté à 120s pour les sessions longues.
|
||||
timestamp_source:
|
||||
apache: field_timestamp
|
||||
network: reception_time
|
||||
apache: timestamp (champ int64, nanosecondes)
|
||||
network: timestamp (champ int64, nanosecondes) si présent, sinon time (RFC3339),
|
||||
sinon reception_time (time.Now())
|
||||
orphan_policy:
|
||||
apache_always_emit: true
|
||||
network_emit: false
|
||||
@ -301,7 +388,7 @@ correlation:
|
||||
mode: one_to_many
|
||||
description: >
|
||||
Stratégie 1‑à‑N : un log réseau peut être utilisé pour plusieurs logs HTTP
|
||||
successifs tant qu’il n’a pas expiré ni été évincé.
|
||||
successifs tant qu'il n'a pas expiré ni été évincé.
|
||||
|
||||
schema:
|
||||
description: >
|
||||
@ -319,8 +406,6 @@ schema:
|
||||
type: int64
|
||||
unit: ns
|
||||
optional_fields:
|
||||
- name: time
|
||||
type: string
|
||||
- name: dst_ip
|
||||
type: string
|
||||
- name: dst_port
|
||||
@ -350,6 +435,12 @@ schema:
|
||||
type: string
|
||||
- name: dst_port
|
||||
type: int
|
||||
- name: timestamp
|
||||
type: int64
|
||||
unit: ns
|
||||
- name: time
|
||||
type: string
|
||||
format: RFC3339 ou RFC3339Nano
|
||||
dynamic_fields:
|
||||
- pattern: "*"
|
||||
target_map: extra
|
||||
@ -405,15 +496,17 @@ clickhouse_schema:
|
||||
strategy: external_ddls
|
||||
database: mabase_prod
|
||||
description: >
|
||||
La table ClickHouse est gérée en dehors du service. Deux tables sont utilisées :
|
||||
http_logs_raw (table d'ingestion partitionnée par jour) et http_logs (table parsée
|
||||
avec extraction explicite des champs). Une vue matérialisée transfère automatiquement
|
||||
les données de RAW vers parsée.
|
||||
La table ClickHouse est gérée en dehors du service. Le service insère dans une
|
||||
table RAW avec une seule colonne raw_json contenant le log corrélé complet
|
||||
sérialisé en JSON. La colonne ingest_time utilise DEFAULT now().
|
||||
Toute extraction de champs (table parsée, vue matérialisée) est gérée en externe
|
||||
via des DDL séparés, non implémentés dans le service.
|
||||
tables:
|
||||
- name: http_logs_raw
|
||||
description: >
|
||||
Table d'ingestion brute. Une seule colonne raw_json contient le log corrélé
|
||||
complet sérialisé en JSON. Partitionnée par jour pour optimiser le TTL.
|
||||
complet sérialisé en JSON. La colonne ingest_time est auto-générée avec
|
||||
DEFAULT now(). Partitionnée par jour pour optimiser le TTL.
|
||||
engine: MergeTree
|
||||
partition_by: toDate(ingest_time)
|
||||
order_by: ingest_time
|
||||
@ -424,13 +517,17 @@ clickhouse_schema:
|
||||
type: DateTime
|
||||
default: now()
|
||||
insert_format: |
|
||||
INSERT INTO mabase_prod.http_logs_raw (raw_json) FORMAT JSONEachRow
|
||||
{"raw_json":"{...log corrélé sérialisé en JSON...}"}
|
||||
INSERT INTO mabase_prod.http_logs_raw (raw_json) VALUES
|
||||
('{...log corrélé sérialisé en JSON...}')
|
||||
notes: >
|
||||
Le service utilise l'API native clickhouse-go/v2 (PrepareBatch + Append + Send).
|
||||
La colonne ingest_time n'est PAS explicitement insérée (DEFAULT now() est utilisé).
|
||||
|
||||
- name: http_logs
|
||||
description: >
|
||||
Table parsée avec tous les champs extraits explicitement par la vue matérialisée.
|
||||
Partitionnée par log_date, optimisée pour les requêtes analytiques.
|
||||
Table parsée (optionnelle, gérée en externe). Le service n'implémente PAS
|
||||
l'extraction des champs suivants. Si cette table est utilisée, elle doit être
|
||||
alimentée par une vue matérialisée ou un traitement ETL externe.
|
||||
engine: MergeTree
|
||||
partition_by: log_date
|
||||
order_by: (time, src_ip, dst_ip, ja4)
|
||||
@ -466,158 +563,113 @@ clickhouse_schema:
|
||||
type: UInt8
|
||||
- name: keepalives
|
||||
type: UInt16
|
||||
status: non_implémenté
|
||||
- name: a_timestamp
|
||||
type: UInt64
|
||||
status: non_implémenté
|
||||
- name: b_timestamp
|
||||
type: UInt64
|
||||
status: non_implémenté
|
||||
- name: conn_id
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: ip_meta_df
|
||||
type: UInt8
|
||||
status: non_implémenté
|
||||
- name: ip_meta_id
|
||||
type: UInt32
|
||||
status: non_implémenté
|
||||
- name: ip_meta_total_length
|
||||
type: UInt32
|
||||
status: non_implémenté
|
||||
- name: ip_meta_ttl
|
||||
type: UInt8
|
||||
status: non_implémenté
|
||||
- name: tcp_meta_options
|
||||
type: LowCardinality(String)
|
||||
status: non_implémenté
|
||||
- name: tcp_meta_window_size
|
||||
type: UInt32
|
||||
status: non_implémenté
|
||||
- name: syn_to_clienthello_ms
|
||||
type: Int32
|
||||
status: non_implémenté
|
||||
- name: tls_version
|
||||
type: LowCardinality(String)
|
||||
status: non_implémenté
|
||||
- name: tls_sni
|
||||
type: LowCardinality(String)
|
||||
status: non_implémenté
|
||||
- name: ja3
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: ja3_hash
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: ja4
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_user_agent
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_accept
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_accept_encoding
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_accept_language
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_x_request_id
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_x_trace_id
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_x_forwarded_for
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_sec_ch_ua
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_sec_ch_ua_mobile
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_sec_ch_ua_platform
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_sec_fetch_dest
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_sec_fetch_mode
|
||||
type: String
|
||||
status: non_implémenté
|
||||
- name: header_sec_fetch_site
|
||||
type: String
|
||||
|
||||
- name: mv_http_logs
|
||||
type: materialized_view
|
||||
description: >
|
||||
Vue matérialisée qui transfère les données de http_logs_raw vers http_logs
|
||||
en extrayant tous les champs du JSON via JSONExtract* et coalesce pour les
|
||||
valeurs par défaut.
|
||||
target: mabase_prod.http_logs
|
||||
query: |
|
||||
SELECT
|
||||
-- 1. Temps
|
||||
parseDateTimeBestEffort(
|
||||
coalesce(JSONExtractString(raw_json, 'time'), '1970-01-01T00:00:00Z')
|
||||
) AS time,
|
||||
toDate(time) AS log_date,
|
||||
|
||||
-- 2. Réseau L3/L4
|
||||
toIPv4(coalesce(JSONExtractString(raw_json, 'src_ip'), '0.0.0.0')) AS src_ip,
|
||||
toUInt16(coalesce(JSONExtractUInt(raw_json, 'src_port'), 0)) AS src_port,
|
||||
toIPv4(coalesce(JSONExtractString(raw_json, 'dst_ip'), '0.0.0.0')) AS dst_ip,
|
||||
toUInt16(coalesce(JSONExtractUInt(raw_json, 'dst_port'), 0)) AS dst_port,
|
||||
|
||||
-- 3. HTTP de base
|
||||
coalesce(JSONExtractString(raw_json, 'method'), '') AS method,
|
||||
coalesce(JSONExtractString(raw_json, 'scheme'), '') AS scheme,
|
||||
coalesce(JSONExtractString(raw_json, 'host'), '') AS host,
|
||||
coalesce(JSONExtractString(raw_json, 'path'), '') AS path,
|
||||
coalesce(JSONExtractString(raw_json, 'query'), '') AS query,
|
||||
coalesce(JSONExtractString(raw_json, 'http_version'), '') AS http_version,
|
||||
coalesce(JSONExtractString(raw_json, 'orphan_side'), '') AS orphan_side,
|
||||
|
||||
-- 4. Connexion / corrélation
|
||||
toUInt8(coalesce(JSONExtractBool(raw_json, 'correlated'), 0)) AS correlated,
|
||||
toUInt16(coalesce(JSONExtractUInt(raw_json, 'keepalives'), 0)) AS keepalives,
|
||||
coalesce(JSONExtractUInt(raw_json, 'a_timestamp'), 0) AS a_timestamp,
|
||||
coalesce(JSONExtractUInt(raw_json, 'b_timestamp'), 0) AS b_timestamp,
|
||||
coalesce(JSONExtractString(raw_json, 'conn_id'), '') AS conn_id,
|
||||
|
||||
-- 5. IP/TCP
|
||||
toUInt8(coalesce(JSONExtractBool(raw_json, 'ip_meta_df'), 0)) AS ip_meta_df,
|
||||
coalesce(JSONExtractUInt(raw_json, 'ip_meta_id'), 0) AS ip_meta_id,
|
||||
coalesce(JSONExtractUInt(raw_json, 'ip_meta_total_length'), 0) AS ip_meta_total_length,
|
||||
coalesce(JSONExtractUInt(raw_json, 'ip_meta_ttl'), 0) AS ip_meta_ttl,
|
||||
coalesce(JSONExtractString(raw_json, 'tcp_meta_options'), '') AS tcp_meta_options,
|
||||
coalesce(JSONExtractUInt(raw_json, 'tcp_meta_window_size'), 0) AS tcp_meta_window_size,
|
||||
toInt32(coalesce(JSONExtractInt(raw_json, 'syn_to_clienthello_ms'), 0)) AS syn_to_clienthello_ms,
|
||||
|
||||
-- 6. TLS / JA3/JA4
|
||||
coalesce(JSONExtractString(raw_json, 'tls_version'), '') AS tls_version,
|
||||
coalesce(JSONExtractString(raw_json, 'tls_sni'), '') AS tls_sni,
|
||||
coalesce(JSONExtractString(raw_json, 'ja3'), '') AS ja3,
|
||||
coalesce(JSONExtractString(raw_json, 'ja3_hash'), '') AS ja3_hash,
|
||||
coalesce(JSONExtractString(raw_json, 'ja4'), '') AS ja4,
|
||||
|
||||
-- 7. Headers HTTP
|
||||
coalesce(JSONExtractString(raw_json, 'header_User-Agent'), '') AS header_user_agent,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Accept'), '') AS header_accept,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Accept-Encoding'), '') AS header_accept_encoding,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Accept-Language'), '') AS header_accept_language,
|
||||
coalesce(JSONExtractString(raw_json, 'header_X-Request-Id'), '') AS header_x_request_id,
|
||||
coalesce(JSONExtractString(raw_json, 'header_X-Trace-Id'), '') AS header_x_trace_id,
|
||||
coalesce(JSONExtractString(raw_json, 'header_X-Forwarded-For'), '') AS header_x_forwarded_for,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Sec-CH-UA'), '') AS header_sec_ch_ua,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Sec-CH-UA-Mobile'), '') AS header_sec_ch_ua_mobile,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Sec-CH-UA-Platform'), '') AS header_sec_ch_ua_platform,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Sec-Fetch-Dest'), '') AS header_sec_fetch_dest,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Sec-Fetch-Mode'), '') AS header_sec_fetch_mode,
|
||||
coalesce(JSONExtractString(raw_json, 'header_Sec-Fetch-Site'), '') AS header_sec_fetch_site
|
||||
FROM mabase_prod.http_logs_raw;
|
||||
status: non_implémenté
|
||||
notes: >
|
||||
Cette table et la vue matérialisée associée sont gérées en externe (DDL séparés).
|
||||
Le service se contente d'insérer le JSON brut dans http_logs_raw.
|
||||
Les champs marqués "non_implémenté" ne sont PAS extraits par le service.
|
||||
|
||||
users:
|
||||
- name: data_writer
|
||||
description: Utilisateur pour l'insertion des logs (utilisé par logcorrelator)
|
||||
grants:
|
||||
- INSERT(raw_json) ON mabase_prod.http_logs_raw
|
||||
- SELECT(raw_json) ON mabase_prod.http_logs_raw
|
||||
|
||||
- name: analyst
|
||||
description: Utilisateur pour la lecture des logs parsés (BI, requêtes)
|
||||
grants:
|
||||
- SELECT ON mabase_prod.http_logs
|
||||
description: >
|
||||
La gestion des utilisateurs ClickHouse est externe au service. Le DSN est
|
||||
configuré dans le fichier de configuration YAML.
|
||||
notes: >
|
||||
Cette section est fournie à titre indicatif pour l'administration ClickHouse.
|
||||
|
||||
migration:
|
||||
description: >
|
||||
Script de migration pour transférer les données existantes de l'ancienne
|
||||
table http_logs_raw vers la nouvelle structure avec vue matérialisée.
|
||||
sql: |
|
||||
INSERT INTO mabase_prod.http_logs (raw_json)
|
||||
SELECT raw_json
|
||||
FROM mabase_prod.http_logs_raw;
|
||||
Aucune migration n'est implémentée dans le service. La gestion des schémas
|
||||
(tables, vues matérialisées) est entièrement externe (DDL séparés).
|
||||
|
||||
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 (config, observabilité).
|
||||
pour les sources/sinks, adaptateurs pour sockets Unix, fichier, ClickHouse et
|
||||
stdout, couche application d'orchestration, et modules infra (config, observabilité).
|
||||
modules:
|
||||
- name: cmd/logcorrelator
|
||||
type: entrypoint
|
||||
@ -625,84 +677,124 @@ architecture:
|
||||
- Chargement de la configuration YAML.
|
||||
- Initialisation des adaptateurs d'entrée/sortie.
|
||||
- Création du CorrelationService.
|
||||
- Démarrage de l’orchestrateur.
|
||||
- Démarrage de l'orchestrateur.
|
||||
- Gestion des signaux (SIGINT, SIGTERM, SIGHUP).
|
||||
- Versioning via -ldflags (main.Version).
|
||||
- name: internal/domain
|
||||
type: domain
|
||||
responsibilities:
|
||||
- Modèles NormalizedEvent et CorrelatedLog.
|
||||
- CorrelationService (fenêtre, TTL, buffers bornés, 1‑à‑N, orphelins).
|
||||
- CorrelationService (fenêtre, TTL, buffers bornés, one-to-many/Keep-Alive, orphelins).
|
||||
- Custom JSON marshaling pour CorrelatedLog (structure plate).
|
||||
- name: internal/ports
|
||||
type: ports
|
||||
responsibilities:
|
||||
- EventSource, CorrelatedLogSink, CorrelationProcessor.
|
||||
- Interfaces EventSource, CorrelatedLogSink, CorrelationProcessor.
|
||||
- name: internal/app
|
||||
type: application
|
||||
responsibilities:
|
||||
- Orchestrator : EventSource → CorrelationService → MultiSink.
|
||||
- Gestion du contexte de shutdown et drain des événements.
|
||||
- name: internal/adapters/inbound/unixsocket
|
||||
type: adapter_inbound
|
||||
responsibilities:
|
||||
- Lecture Unix datagram (SOCK_DGRAM) et parsing JSON → NormalizedEvent.
|
||||
- Détection automatique de la source (A/B) via source_type ou headers.
|
||||
- Gestion des permissions de socket (défaut 0666).
|
||||
- Cleanup du fichier socket à l'arrêt.
|
||||
- name: internal/adapters/outbound/file
|
||||
type: adapter_outbound
|
||||
responsibilities:
|
||||
- Écriture JSON lines.
|
||||
- Réouverture du fichier sur SIGHUP.
|
||||
- Réouverture du fichier sur SIGHUP (log rotation).
|
||||
- Validation des chemins (répertoire autorisé).
|
||||
- name: internal/adapters/outbound/clickhouse
|
||||
type: adapter_outbound
|
||||
responsibilities:
|
||||
- Bufferisation + inserts batch, gestion du drop_on_overflow.
|
||||
- Bufferisation + inserts batch asynchrones.
|
||||
- Gestion du drop_on_overflow.
|
||||
- Retry avec backoff exponentiel (MaxRetries=3).
|
||||
- API native clickhouse-go/v2 (PrepareBatch + Append + Send).
|
||||
- name: internal/adapters/outbound/stdout
|
||||
type: adapter_outbound
|
||||
responsibilities:
|
||||
- Écriture des logs vers stdout pour débogage.
|
||||
- Filtrage par niveau (DEBUG, INFO, WARN, ERROR).
|
||||
- name: internal/adapters/outbound/multi
|
||||
type: adapter_outbound
|
||||
responsibilities:
|
||||
- Fan‑out vers plusieurs sinks.
|
||||
- Fan-out vers plusieurs sinks.
|
||||
- Implémentation de Reopen() pour la rotation des logs.
|
||||
- name: internal/config
|
||||
type: infrastructure
|
||||
responsibilities:
|
||||
- Chargement/validation de la configuration YAML.
|
||||
- Valeurs par défaut et fallback pour champs dépréciés.
|
||||
- name: internal/observability
|
||||
type: infrastructure
|
||||
responsibilities:
|
||||
- Logging interne, métriques (tailles des caches, évictions, erreurs datagram).
|
||||
- Logger structuré avec niveaux (DEBUG, INFO, WARN, ERROR).
|
||||
- Logs pour : événements reçus, corrélations, orphelins, buffer plein.
|
||||
|
||||
testing:
|
||||
unit:
|
||||
description: >
|
||||
Tests unitaires table‑driven, couverture cible ≥ 80 %, focale sur la logique
|
||||
de corrélation, les caches et les sinks.
|
||||
Tests unitaires table‑driven, couverture cible ≥ 80 %. La couverture actuelle
|
||||
est d'environ 74-80% selon les versions. Les tests se concentrent sur la logique
|
||||
de corrélation, les caches, les sinks et le parsing des datagrammes.
|
||||
coverage_minimum: 0.8
|
||||
coverage_actual: ~0.74-0.80
|
||||
focus:
|
||||
- CorrelationService (fenêtre, TTL, évictions, 1‑à‑N)
|
||||
- Parsing A/B → NormalizedEvent (datagrammes)
|
||||
- ClickHouseSink (batching, overflow)
|
||||
- CorrelationService (fenêtre, TTL, évictions, one-to-many/Keep-Alive)
|
||||
- Parsing A/B → NormalizedEvent (datagrammes JSON)
|
||||
- ClickHouseSink (batching, retry, overflow)
|
||||
- FileSink (réouverture sur SIGHUP)
|
||||
- MultiSink
|
||||
- MultiSink (fan-out)
|
||||
- Config (validation, valeurs par défaut)
|
||||
- UnixSocketSource (lecture, permissions, cleanup)
|
||||
integration:
|
||||
description: >
|
||||
Tests d’intégration validant le flux complet A+B → corrélation → sinks,
|
||||
avec sockets Unix datagram simulées, ClickHouse mocké et scénarios Keep‑Alive.
|
||||
Tests d'intégration limités. Le flux complet A+B → corrélation → sinks est
|
||||
testé via des tests unitaires avec mocks. ClickHouse est mocké (pas de tests
|
||||
avec vrai ClickHouse). Scénarios Keep-Alive testés dans correlation_service_test.go.
|
||||
|
||||
docker:
|
||||
description: >
|
||||
Build, tests et packaging RPM sont exécutés intégralement dans des conteneurs
|
||||
via un multi‑stage build.
|
||||
via un multi‑stage build. Deux Dockerfiles : Dockerfile (build + runtime + dev)
|
||||
et Dockerfile.package (RPM multi-distros : el8, el9, el10).
|
||||
build_pipeline:
|
||||
multi_stage: true
|
||||
stages:
|
||||
- name: test_and_compile
|
||||
base: golang:latest
|
||||
- name: builder
|
||||
base: golang:1.21
|
||||
description: >
|
||||
go test ./... (échec si couverture < 80 %), puis compilation d’un binaire
|
||||
statique (CGO_ENABLED=0, GOOS=linux, GOARCH=amd64).
|
||||
- name: rpm_builder
|
||||
base: ruby:alpine
|
||||
description: >
|
||||
Installation de fpm, git et outils RPM. Génération du changelog RPM à
|
||||
partir de l’historique. Construction des .rpm pour les différentes
|
||||
distributions.
|
||||
- name: output_export
|
||||
go test -race -coverprofile=coverage.txt ./... avec vérification de couverture
|
||||
(échec si < 80 %). Compilation d'un binaire statique (CGO_ENABLED=0,
|
||||
GOOS=linux, GOARCH=amd64).
|
||||
- name: runtime
|
||||
base: scratch
|
||||
description: >
|
||||
Étape minimale pour exposer les paquets RPM produits (docker build --output).
|
||||
Image minimale contenant uniquement le binaire et la config exemple.
|
||||
- name: rpm_builder_el8
|
||||
base: rockylinux:8
|
||||
description: >
|
||||
Installation de fpm (via Ruby), construction RPM pour Enterprise Linux 8.
|
||||
- name: rpm_builder_el9
|
||||
base: rockylinux:9
|
||||
description: >
|
||||
Installation de fpm (via Ruby), construction RPM pour Enterprise Linux 9.
|
||||
- name: rpm_builder_el10
|
||||
base: almalinux:10
|
||||
description: >
|
||||
Installation de fpm (via Ruby), construction RPM pour Enterprise Linux 10.
|
||||
- name: output_export
|
||||
base: alpine:latest
|
||||
description: >
|
||||
Export des paquets RPM produits pour les 3 distributions (el8, el9, el10).
|
||||
files:
|
||||
- path: Dockerfile
|
||||
description: Build principal (builder, runtime, dev) et packaging RPM mono-distro.
|
||||
- path: Dockerfile.package
|
||||
description: Packaging RPM multi-distros (el8, el9, el10) avec scripts post/preun/postun.
|
||||
|
||||
|
||||
@ -28,6 +28,12 @@ logcorrelator est un service système écrit en Go qui reçoit deux flux de logs
|
||||
via des sockets Unix, corrèle les événements HTTP applicatifs avec des événements
|
||||
réseau, et produit des logs corrélés en temps réel vers ClickHouse et/ou fichier local.
|
||||
|
||||
Notes de sécurité :
|
||||
- Le service s'exécute sous l'utilisateur logcorrelator (non-root)
|
||||
- Les sockets Unix sont créés avec des permissions 0666 (world read/write)
|
||||
- Les répertoires critiques sont protégés : /var/log (750), /var/lib (750), /etc (750)
|
||||
- /var/run/logcorrelator est en 755 pour permettre la création de sockets
|
||||
|
||||
%prep
|
||||
# No source extraction needed - binary is pre-built
|
||||
|
||||
@ -52,73 +58,25 @@ install -m 0644 %{_sourcedir}/logcorrelator.service %{buildroot}/etc/systemd/sys
|
||||
# Install logrotate config
|
||||
install -m 0644 %{_sourcedir}/logrotate %{buildroot}/etc/logrotate.d/logcorrelator
|
||||
|
||||
%post
|
||||
# Create logcorrelator user and group
|
||||
if ! getent group logcorrelator >/dev/null 2>&1; then
|
||||
groupadd --system logcorrelator
|
||||
fi
|
||||
|
||||
if ! getent passwd logcorrelator >/dev/null 2>&1; then
|
||||
useradd --system \
|
||||
--gid logcorrelator \
|
||||
--home-dir /var/lib/logcorrelator \
|
||||
--no-create-home \
|
||||
--shell /usr/sbin/nologin \
|
||||
logcorrelator
|
||||
fi
|
||||
|
||||
# Create directories
|
||||
mkdir -p /var/lib/logcorrelator
|
||||
mkdir -p /var/log/logcorrelator
|
||||
mkdir -p /var/run/logcorrelator
|
||||
|
||||
# Set ownership
|
||||
chown -R logcorrelator:logcorrelator /var/lib/logcorrelator
|
||||
chown -R logcorrelator:logcorrelator /var/log/logcorrelator
|
||||
chown -R logcorrelator:logcorrelator /var/run/logcorrelator
|
||||
chown -R logcorrelator:logcorrelator /etc/logcorrelator
|
||||
|
||||
# Set permissions
|
||||
chmod 750 /var/lib/logcorrelator
|
||||
chmod 750 /var/log/logcorrelator
|
||||
chmod 755 /var/run/logcorrelator
|
||||
chmod 750 /etc/logcorrelator
|
||||
|
||||
# Copy default config if not exists
|
||||
if [ ! -f /etc/logcorrelator/logcorrelator.yml ]; then
|
||||
cp /etc/logcorrelator/logcorrelator.yml.example /etc/logcorrelator/logcorrelator.yml
|
||||
chown logcorrelator:logcorrelator /etc/logcorrelator/logcorrelator.yml
|
||||
chmod 640 /etc/logcorrelator/logcorrelator.yml
|
||||
fi
|
||||
|
||||
# Reload systemd
|
||||
systemctl daemon-reload
|
||||
systemctl enable logcorrelator.service
|
||||
systemctl start logcorrelator.service
|
||||
# Note: %post, %preun, %postun scripts are provided externally via Dockerfile.package
|
||||
# They are injected during RPM build using fpm --after-install, --before-remove, --after-remove
|
||||
|
||||
%preun
|
||||
if [ $1 -eq 0 ]; then
|
||||
# Package removal, not upgrade
|
||||
systemctl stop logcorrelator.service
|
||||
systemctl disable logcorrelator.service
|
||||
fi
|
||||
# Placeholder: actual preun script is provided externally via Dockerfile.package
|
||||
# See packaging/rpm/preun for the actual script
|
||||
|
||||
%postun
|
||||
systemctl daemon-reload
|
||||
if [ $1 -ge 1 ]; then
|
||||
# Package upgrade, restart service
|
||||
systemctl try-restart logcorrelator.service
|
||||
fi
|
||||
# Placeholder: actual postun script is provided externally via Dockerfile.package
|
||||
# See packaging/rpm/postun for the actual script
|
||||
|
||||
%files
|
||||
/usr/bin/logcorrelator
|
||||
/etc/logcorrelator/logcorrelator.yml
|
||||
/etc/logcorrelator/logcorrelator.yml.example
|
||||
%config(noreplace) /etc/logcorrelator/logcorrelator.yml
|
||||
/etc/logcorrelator/logcorrelator.yml.example
|
||||
/var/log/logcorrelator
|
||||
/var/run/logcorrelator
|
||||
/etc/systemd/system/logcorrelator.service
|
||||
/etc/logrotate.d/logcorrelator
|
||||
%config(noreplace) /etc/logrotate.d/logcorrelator
|
||||
|
||||
%changelog
|
||||
* Tue Mar 03 2026 logcorrelator <dev@example.com> - 1.1.7-1
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
#!/bin/bash
|
||||
# post script for logcorrelator RPM package
|
||||
# post install script for logcorrelator RPM package
|
||||
# Compatible with CentOS 7, Rocky Linux 8, 9, 10
|
||||
#
|
||||
# Configuration file policy:
|
||||
# - logcorrelator.yml: %config(noreplace) - NEVER overwritten on upgrade
|
||||
# - logcorrelator.yml.example: ALWAYS updated with new configuration options
|
||||
# - On first install: logcorrelator.yml is created from logcorrelator.yml.example
|
||||
# - On upgrade: existing logcorrelator.yml is preserved
|
||||
|
||||
set -e
|
||||
|
||||
@ -24,19 +30,34 @@ mkdir -p /var/log/logcorrelator
|
||||
mkdir -p /var/run/logcorrelator
|
||||
|
||||
# Set ownership
|
||||
# /var/run/logcorrelator: must be owned by logcorrelator for socket creation
|
||||
# /var/log/logcorrelator: must be owned by logcorrelator for log file writing
|
||||
# /var/lib/logcorrelator: home directory for the service
|
||||
chown -R logcorrelator:logcorrelator /var/lib/logcorrelator
|
||||
chown -R logcorrelator:logcorrelator /var/log/logcorrelator
|
||||
chown -R logcorrelator:logcorrelator /var/run/logcorrelator
|
||||
chown -R logcorrelator:logcorrelator /etc/logcorrelator
|
||||
|
||||
# Set permissions
|
||||
# /var/run/logcorrelator: 755 to allow other users/apps to create sockets if needed
|
||||
# /var/log/logcorrelator: 750 to restrict log access
|
||||
# /var/lib/logcorrelator: 750 for service data
|
||||
# /etc/logcorrelator: 750 to restrict config access
|
||||
chmod 755 /var/run/logcorrelator
|
||||
chmod 750 /var/lib/logcorrelator
|
||||
chmod 750 /var/log/logcorrelator
|
||||
chmod 750 /etc/logcorrelator
|
||||
|
||||
# Copy default config if not exists
|
||||
# Copy default config example (always updated)
|
||||
# The main config file is preserved across upgrades via %config(noreplace)
|
||||
if [ -f /etc/logcorrelator/logcorrelator.yml.example ]; then
|
||||
chown logcorrelator:logcorrelator /etc/logcorrelator/logcorrelator.yml.example
|
||||
chmod 640 /etc/logcorrelator/logcorrelator.yml.example
|
||||
fi
|
||||
|
||||
# Create main config file only if it doesn't exist (first install)
|
||||
if [ ! -f /etc/logcorrelator/logcorrelator.yml ]; then
|
||||
cp /usr/share/logcorrelator/logcorrelator.yml.example /etc/logcorrelator/logcorrelator.yml
|
||||
cp /etc/logcorrelator/logcorrelator.yml.example /etc/logcorrelator/logcorrelator.yml
|
||||
chown logcorrelator:logcorrelator /etc/logcorrelator/logcorrelator.yml
|
||||
chmod 640 /etc/logcorrelator/logcorrelator.yml
|
||||
fi
|
||||
|
||||
258
packaging/test/test-rpm.sh
Executable file
258
packaging/test/test-rpm.sh
Executable file
@ -0,0 +1,258 @@
|
||||
#!/bin/bash
|
||||
# Test script for logcorrelator RPM package
|
||||
# Verifies installation, permissions, and service status
|
||||
#
|
||||
# Usage: ./packaging/test/test-rpm.sh [el8|el9|el10]
|
||||
#
|
||||
# This script tests the RPM package in a Docker container to ensure:
|
||||
# - Installation succeeds
|
||||
# - File permissions are correct
|
||||
# - Service starts properly
|
||||
# - Sockets are created with correct ownership
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
RPM_DIR="${PROJECT_ROOT}/dist/rpm"
|
||||
|
||||
# Default to el8 if no argument provided
|
||||
DISTRO="${1:-el8}"
|
||||
|
||||
echo "========================================="
|
||||
echo "Testing logcorrelator RPM for ${DISTRO}"
|
||||
echo "========================================="
|
||||
|
||||
# Find the RPM file
|
||||
case "${DISTRO}" in
|
||||
el8|rocky8)
|
||||
RPM_PATH="${RPM_DIR}/el8"
|
||||
BASE_IMAGE="rockylinux:8"
|
||||
;;
|
||||
el9|rocky9)
|
||||
RPM_PATH="${RPM_DIR}/el9"
|
||||
BASE_IMAGE="rockylinux:9"
|
||||
;;
|
||||
el10|alma10)
|
||||
RPM_PATH="${RPM_DIR}/el10"
|
||||
BASE_IMAGE="almalinux:10"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown distribution: ${DISTRO}"
|
||||
echo "Valid options: el8, el9, el10"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Find the latest RPM file
|
||||
RPM_FILE=$(ls -t "${RPM_PATH}"/logcorrelator-*.rpm 2>/dev/null | head -n 1)
|
||||
|
||||
if [ -z "${RPM_FILE}" ]; then
|
||||
echo "ERROR: No RPM file found in ${RPM_PATH}"
|
||||
echo "Please run 'make package-rpm' first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Testing RPM: ${RPM_FILE}"
|
||||
echo "Base image: ${BASE_IMAGE}"
|
||||
echo ""
|
||||
|
||||
# Create test script
|
||||
TEST_SCRIPT=$(cat <<'EOF'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Installing logcorrelator RPM ==="
|
||||
rpm -ivh /tmp/logcorrelator.rpm
|
||||
|
||||
echo ""
|
||||
echo "=== Checking user and group ==="
|
||||
if ! getent group logcorrelator >/dev/null; then
|
||||
echo "FAIL: logcorrelator group not created"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: logcorrelator group exists"
|
||||
|
||||
if ! getent passwd logcorrelator >/dev/null; then
|
||||
echo "FAIL: logcorrelator user not created"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: logcorrelator user exists"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking directory permissions ==="
|
||||
|
||||
# Check /var/run/logcorrelator
|
||||
DIR="/var/run/logcorrelator"
|
||||
if [ ! -d "$DIR" ]; then
|
||||
echo "FAIL: $DIR does not exist"
|
||||
exit 1
|
||||
fi
|
||||
OWNER=$(stat -c '%U:%G' "$DIR")
|
||||
PERMS=$(stat -c '%a' "$DIR")
|
||||
if [ "$OWNER" != "logcorrelator:logcorrelator" ]; then
|
||||
echo "FAIL: $DIR owner is $OWNER (expected logcorrelator:logcorrelator)"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$PERMS" != "755" ]; then
|
||||
echo "FAIL: $DIR permissions are $PERMS (expected 755)"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $DIR - owner=$OWNER, permissions=$PERMS"
|
||||
|
||||
# Check /var/log/logcorrelator
|
||||
DIR="/var/log/logcorrelator"
|
||||
if [ ! -d "$DIR" ]; then
|
||||
echo "FAIL: $DIR does not exist"
|
||||
exit 1
|
||||
fi
|
||||
OWNER=$(stat -c '%U:%G' "$DIR")
|
||||
PERMS=$(stat -c '%a' "$DIR")
|
||||
if [ "$OWNER" != "logcorrelator:logcorrelator" ]; then
|
||||
echo "FAIL: $DIR owner is $OWNER (expected logcorrelator:logcorrelator)"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$PERMS" != "750" ]; then
|
||||
echo "FAIL: $DIR permissions are $PERMS (expected 750)"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $DIR - owner=$OWNER, permissions=$PERMS"
|
||||
|
||||
# Check /var/lib/logcorrelator
|
||||
DIR="/var/lib/logcorrelator"
|
||||
if [ ! -d "$DIR" ]; then
|
||||
echo "FAIL: $DIR does not exist"
|
||||
exit 1
|
||||
fi
|
||||
OWNER=$(stat -c '%U:%G' "$DIR")
|
||||
PERMS=$(stat -c '%a' "$DIR")
|
||||
if [ "$OWNER" != "logcorrelator:logcorrelator" ]; then
|
||||
echo "FAIL: $DIR owner is $OWNER (expected logcorrelator:logcorrelator)"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$PERMS" != "750" ]; then
|
||||
echo "FAIL: $DIR permissions are $PERMS (expected 750)"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $DIR - owner=$OWNER, permissions=$PERMS"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking config files ==="
|
||||
|
||||
# Check config file exists and has correct permissions
|
||||
CONFIG="/etc/logcorrelator/logcorrelator.yml"
|
||||
if [ ! -f "$CONFIG" ]; then
|
||||
echo "FAIL: $CONFIG does not exist"
|
||||
exit 1
|
||||
fi
|
||||
OWNER=$(stat -c '%U:%G' "$CONFIG")
|
||||
PERMS=$(stat -c '%a' "$CONFIG")
|
||||
if [ "$OWNER" != "logcorrelator:logcorrelator" ]; then
|
||||
echo "FAIL: $CONFIG owner is $OWNER (expected logcorrelator:logcorrelator)"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$PERMS" != "640" ]; then
|
||||
echo "FAIL: $CONFIG permissions are $PERMS (expected 640)"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $CONFIG - owner=$OWNER, permissions=$PERMS"
|
||||
|
||||
# Check example config file
|
||||
EXAMPLE_CONFIG="/etc/logcorrelator/logcorrelator.yml.example"
|
||||
if [ ! -f "$EXAMPLE_CONFIG" ]; then
|
||||
echo "FAIL: $EXAMPLE_CONFIG does not exist"
|
||||
exit 1
|
||||
fi
|
||||
OWNER=$(stat -c '%U:%G' "$EXAMPLE_CONFIG")
|
||||
PERMS=$(stat -c '%a' "$EXAMPLE_CONFIG")
|
||||
if [ "$OWNER" != "logcorrelator:logcorrelator" ]; then
|
||||
echo "FAIL: $EXAMPLE_CONFIG owner is $OWNER (expected logcorrelator:logcorrelator)"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$PERMS" != "640" ]; then
|
||||
echo "FAIL: $EXAMPLE_CONFIG permissions are $PERMS (expected 640)"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $EXAMPLE_CONFIG - owner=$OWNER, permissions=$PERMS"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking systemd service ==="
|
||||
if [ ! -f /etc/systemd/system/logcorrelator.service ]; then
|
||||
echo "FAIL: systemd service file not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: systemd service file exists"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking logrotate config ==="
|
||||
if [ ! -f /etc/logrotate.d/logcorrelator ]; then
|
||||
echo "FAIL: logrotate config not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: logrotate config exists"
|
||||
|
||||
echo ""
|
||||
echo "=== Testing service start ==="
|
||||
# Try to start the service (may fail in container without full systemd)
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl daemon-reload || true
|
||||
if systemctl start logcorrelator.service 2>/dev/null; then
|
||||
echo "OK: service started successfully"
|
||||
|
||||
# Wait for sockets to be created
|
||||
sleep 2
|
||||
|
||||
echo ""
|
||||
echo "=== Checking sockets ==="
|
||||
HTTP_SOCKET="/var/run/logcorrelator/http.socket"
|
||||
NETWORK_SOCKET="/var/run/logcorrelator/network.socket"
|
||||
|
||||
if [ -S "$HTTP_SOCKET" ]; then
|
||||
OWNER=$(stat -c '%U:%G' "$HTTP_SOCKET")
|
||||
PERMS=$(stat -c '%a' "$HTTP_SOCKET")
|
||||
echo "OK: $HTTP_SOCKET exists - owner=$OWNER, permissions=$PERMS"
|
||||
if [ "$PERMS" != "666" ]; then
|
||||
echo "WARN: socket permissions are $PERMS (expected 666)"
|
||||
fi
|
||||
else
|
||||
echo "WARN: $HTTP_SOCKET not found (service may not have started)"
|
||||
fi
|
||||
|
||||
if [ -S "$NETWORK_SOCKET" ]; then
|
||||
OWNER=$(stat -c '%U:%G' "$NETWORK_SOCKET")
|
||||
PERMS=$(stat -c '%a' "$NETWORK_SOCKET")
|
||||
echo "OK: $NETWORK_SOCKET exists - owner=$OWNER, permissions=$PERMS"
|
||||
if [ "$PERMS" != "666" ]; then
|
||||
echo "WARN: socket permissions are $PERMS (expected 666)"
|
||||
fi
|
||||
else
|
||||
echo "WARN: $NETWORK_SOCKET not found (service may not have started)"
|
||||
fi
|
||||
|
||||
systemctl stop logcorrelator.service || true
|
||||
else
|
||||
echo "WARN: service failed to start (expected in minimal container)"
|
||||
fi
|
||||
else
|
||||
echo "WARN: systemctl not available (minimal container)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "All tests passed!"
|
||||
echo "========================================="
|
||||
EOF
|
||||
)
|
||||
|
||||
# Run test in Docker container
|
||||
echo "Running tests in Docker container..."
|
||||
echo ""
|
||||
|
||||
docker run --rm \
|
||||
-v "${RPM_FILE}:/tmp/logcorrelator.rpm:ro" \
|
||||
-v "${TEST_SCRIPT}:/test.sh:ro" \
|
||||
"${BASE_IMAGE}" \
|
||||
bash /test.sh
|
||||
|
||||
echo ""
|
||||
echo "Test completed successfully for ${DISTRO}"
|
||||
Reference in New Issue
Block a user