feat: add logrotate support with SIGHUP signal handling
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
- Add Reopenable interface in api/types.go for log rotation support - Add FileWriter.Reopen() method to reopen log files after rotation - Add MultiWriter.Reopen() method to propagate rotation to all writers - Update main.go to handle SIGHUP signal for systemctl reload - Add logrotate configuration file (packaging/logrotate/ja4sentinel) - Update systemd service with ExecReload for graceful rotation - Update architecture.yml with logrotate documentation - Update RPM spec and Dockerfile.package to include logrotate files - Bump version to 1.1.0 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@ -35,7 +35,7 @@ COPY . .
|
||||
|
||||
# Build binary for Linux
|
||||
# Binary will be dynamically linked but compatible with all RHEL-based distros
|
||||
ARG VERSION=1.0.8
|
||||
ARG VERSION=1.1.0
|
||||
ARG BUILD_TIME=""
|
||||
ARG GIT_COMMIT=""
|
||||
RUN mkdir -p dist && \
|
||||
@ -53,7 +53,7 @@ FROM rockylinux:9 AS rpm-builder
|
||||
WORKDIR /package
|
||||
|
||||
# VERSION must be redeclared for each stage that needs it
|
||||
ARG VERSION=1.0.8
|
||||
ARG VERSION=1.1.0
|
||||
|
||||
# Install rpm-build tools (Rocky Linux 9)
|
||||
RUN dnf install -y \
|
||||
@ -64,7 +64,8 @@ RUN dnf install -y \
|
||||
&& dnf clean all
|
||||
|
||||
# Setup rpmbuild directory structure
|
||||
RUN mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||
RUN mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} && \
|
||||
mkdir -p /root/rpmbuild/SOURCES/logrotate
|
||||
|
||||
# Copy spec file
|
||||
COPY packaging/rpm/ja4sentinel.spec /root/rpmbuild/SPECS/ja4sentinel.spec
|
||||
@ -72,11 +73,13 @@ COPY packaging/rpm/ja4sentinel.spec /root/rpmbuild/SPECS/ja4sentinel.spec
|
||||
# Copy binary from Go builder and other files to SOURCES
|
||||
COPY --from=builder /build/dist/ja4sentinel /root/rpmbuild/SOURCES/ja4sentinel
|
||||
COPY packaging/systemd/ja4sentinel.service /root/rpmbuild/SOURCES/ja4sentinel.service
|
||||
COPY packaging/logrotate/ja4sentinel /root/rpmbuild/SOURCES/logrotate/ja4sentinel
|
||||
COPY config.yml.example /root/rpmbuild/SOURCES/config.yml
|
||||
|
||||
# Set permissions
|
||||
RUN chmod 755 /root/rpmbuild/SOURCES/ja4sentinel && \
|
||||
chmod 644 /root/rpmbuild/SOURCES/ja4sentinel.service && \
|
||||
chmod 644 /root/rpmbuild/SOURCES/logrotate/ja4sentinel && \
|
||||
chmod 640 /root/rpmbuild/SOURCES/config.yml
|
||||
|
||||
# Build RPM for Rocky Linux 8 (el8)
|
||||
|
||||
@ -205,6 +205,13 @@ type Logger interface {
|
||||
Error(component, message string, details map[string]string)
|
||||
}
|
||||
|
||||
// Reopenable defines the interface for components that support log file rotation.
|
||||
// Implementations must reopen their output files when receiving a SIGHUP signal.
|
||||
// This is used by systemctl reload to switch to new log files after logrotate.
|
||||
type Reopenable interface {
|
||||
Reopen() error
|
||||
}
|
||||
|
||||
// Helper functions for creating and converting records
|
||||
|
||||
// NewLogRecord creates a flattened LogRecord from TLSClientHello and Fingerprints.
|
||||
|
||||
@ -8,6 +8,9 @@ project:
|
||||
(via psanford/tlsfingerprint), enrichir avec des métadonnées IP/TCP,
|
||||
et loguer les résultats (IP, ports, JA4, meta) vers une ou plusieurs
|
||||
sorties configurables (socket UNIX, stdout, fichier, ...).
|
||||
Le service est géré par systemd avec support de rotation des logs via logrotate.
|
||||
La commande `systemctl reload ja4sentinel` permet de réouvrir les fichiers de log
|
||||
après rotation (signal SIGHUP).
|
||||
languages:
|
||||
- go
|
||||
goals:
|
||||
@ -122,6 +125,7 @@ modules:
|
||||
- "Construire les instances des modules (capture, tlsparse, fingerprint, output, logging)."
|
||||
- "Brancher les modules entre eux selon l'architecture pipeline."
|
||||
- "Gérer les signaux système (arrêt propre)."
|
||||
- "Gérer le signal SIGHUP pour la rotation des logs (systemctl reload)."
|
||||
allowed_dependencies:
|
||||
- "config"
|
||||
- "capture"
|
||||
@ -410,16 +414,16 @@ architecture:
|
||||
|
||||
flow_control:
|
||||
connection_states:
|
||||
description: "États simplifiés d’un flux TCP pour minimiser la capture."
|
||||
description: "États simplifiés d'un flux TCP pour minimiser la capture."
|
||||
states:
|
||||
- name: "NEW"
|
||||
description: "Observation d’un SYN client sur un port surveillé, création d’un état minimal (IP/TCP meta)."
|
||||
description: "Observation d'un SYN client sur un port surveillé, création d'un état minimal (IP/TCP meta)."
|
||||
- name: "WAIT_CLIENT_HELLO"
|
||||
description: "Accumulation des segments TCP nécessaires pour extraire un ClientHello complet."
|
||||
- name: "JA4_DONE"
|
||||
description: "JA4 calculé et logué, on arrête de suivre ce flux."
|
||||
rules:
|
||||
- "Pas de tableaux imbriqués ni d’objets deeply nested."
|
||||
- "Pas de tableaux imbriqués ni d'objets deeply nested."
|
||||
- "Toutes les métadonnées IP/TCP sont flatten sous forme de champs scalaires nommés."
|
||||
- "Les noms de champs suivent la convention: ip_meta_*, tcp_meta_*, ja*."
|
||||
- "Pas de champ ja4_hash : le format JA4 intègre déjà son propre hachage tronqué, la chaîne complète de 38 caractères suffit."
|
||||
@ -447,3 +451,23 @@ flow_control:
|
||||
- "ja3"
|
||||
- "ja3_hash"
|
||||
|
||||
packaging:
|
||||
rpm:
|
||||
description: "Package RPM pour déploiement sur serveurs Linux."
|
||||
files:
|
||||
- path: "/etc/logrotate.d/ja4sentinel"
|
||||
description: "Script logrotate pour la rotation des fichiers de log."
|
||||
note: "Fourni par le RPM, configure la rotation quotidienne avec compression."
|
||||
- path: "/etc/systemd/system/ja4sentinel.service"
|
||||
description: "Unité systemd pour la gestion du service."
|
||||
note: "Doit inclure Type=notify et ExecReload=/bin/kill -HUP $MAINPID pour supporter systemctl reload."
|
||||
logrotate:
|
||||
description: "Configuration logrotate pour la rotation des logs."
|
||||
behavior:
|
||||
- "Rotation quotidienne ou selon taille."
|
||||
- "Compression des logs archivés."
|
||||
- "Envoi du signal SIGHUP au service après rotation pour réouvrir les fichiers."
|
||||
reload_mechanism:
|
||||
- "systemctl reload ja4sentinel déclenche le handler SIGHUP."
|
||||
- "Le service réouvre ses fichiers de log sans redémarrage complet."
|
||||
|
||||
|
||||
@ -107,9 +107,9 @@ func main() {
|
||||
}()
|
||||
}
|
||||
|
||||
// Setup signal handling
|
||||
// Setup signal handling for shutdown and log rotation
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
||||
|
||||
// Create pipeline components
|
||||
captureEngine := capture.New()
|
||||
@ -202,19 +202,41 @@ func main() {
|
||||
}()
|
||||
|
||||
// Wait for shutdown signal or capture error
|
||||
for {
|
||||
select {
|
||||
case sig := <-sigChan:
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
// Handle log rotation - reopen output files
|
||||
appLogger.Info("main", "Received SIGHUP, reopening log files", nil)
|
||||
if mw, ok := outputWriter.(api.Reopenable); ok {
|
||||
if err := mw.Reopen(); err != nil {
|
||||
appLogger.Error("main", "Failed to reopen log files", map[string]string{
|
||||
"error": err.Error(),
|
||||
})
|
||||
} else {
|
||||
appLogger.Info("main", "Log files reopened successfully", nil)
|
||||
}
|
||||
} else {
|
||||
appLogger.Warn("main", "Output writer does not support log rotation", nil)
|
||||
}
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
appLogger.Info("main", "Received shutdown signal", map[string]string{
|
||||
"signal": sig.String(),
|
||||
})
|
||||
goto shutdown
|
||||
}
|
||||
case err := <-captureErrChan:
|
||||
if err != nil {
|
||||
appLogger.Error("capture", "Capture engine failed", map[string]string{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
goto shutdown
|
||||
}
|
||||
}
|
||||
|
||||
shutdown:
|
||||
// Graceful shutdown
|
||||
appLogger.Info("main", "Shutting down...", nil)
|
||||
|
||||
|
||||
@ -182,6 +182,28 @@ func (w *FileWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reopen reopens the log file (for logrotate support)
|
||||
func (w *FileWriter) Reopen() error {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
|
||||
if err := w.file.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close file during reopen: %w", err)
|
||||
}
|
||||
|
||||
// Open new file
|
||||
newFile, err := os.OpenFile(w.path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to reopen file %s: %w", w.path, err)
|
||||
}
|
||||
|
||||
w.file = newFile
|
||||
w.encoder = json.NewEncoder(newFile)
|
||||
w.currentSize = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnixSocketWriter writes log records to a UNIX socket with reconnection logic
|
||||
type UnixSocketWriter struct {
|
||||
socketPath string
|
||||
@ -495,6 +517,23 @@ func (mw *MultiWriter) CloseAll() error {
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// Reopen reopens all writers that support log rotation
|
||||
func (mw *MultiWriter) Reopen() error {
|
||||
mw.mutex.Lock()
|
||||
defer mw.mutex.Unlock()
|
||||
|
||||
var lastErr error
|
||||
for _, w := range mw.writers {
|
||||
if reopenable, ok := w.(api.Reopenable); ok {
|
||||
if err := reopenable.Reopen(); err != nil {
|
||||
lastErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// BuilderImpl implements the api.Builder interface
|
||||
type BuilderImpl struct{}
|
||||
|
||||
|
||||
17
packaging/logrotate/ja4sentinel
Normal file
17
packaging/logrotate/ja4sentinel
Normal file
@ -0,0 +1,17 @@
|
||||
# Logrotate configuration for ja4sentinel
|
||||
# Install to: /etc/logrotate.d/ja4sentinel
|
||||
|
||||
/var/log/ja4sentinel/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 7
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0600 root root
|
||||
sharedscripts
|
||||
postrotate
|
||||
# Send SIGHUP to ja4sentinel to reopen log files
|
||||
/bin/systemctl reload ja4sentinel 2>/dev/null || true
|
||||
endscript
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
%if %{defined build_version}
|
||||
%define spec_version %{build_version}
|
||||
%else
|
||||
%define spec_version 1.0.9
|
||||
%define spec_version 1.1.0
|
||||
%endif
|
||||
|
||||
Name: ja4sentinel
|
||||
@ -44,6 +44,7 @@ Features:
|
||||
%install
|
||||
mkdir -p %{buildroot}/usr/bin
|
||||
mkdir -p %{buildroot}/etc/ja4sentinel
|
||||
mkdir -p %{buildroot}/etc/logrotate.d
|
||||
mkdir -p %{buildroot}/var/lib/ja4sentinel
|
||||
mkdir -p %{buildroot}/var/log/ja4sentinel
|
||||
mkdir -p %{buildroot}/var/run/logcorrelator
|
||||
@ -56,6 +57,9 @@ install -m 755 %{_sourcedir}/ja4sentinel %{buildroot}/usr/bin/ja4sentinel
|
||||
# Install systemd service
|
||||
install -m 644 %{_sourcedir}/ja4sentinel.service %{buildroot}/usr/lib/systemd/system/ja4sentinel.service
|
||||
|
||||
# Install logrotate configuration
|
||||
install -m 644 %{_sourcedir}/logrotate/ja4sentinel %{buildroot}/etc/logrotate.d/ja4sentinel
|
||||
|
||||
# Install default config
|
||||
install -m 640 %{_sourcedir}/config.yml %{buildroot}/etc/ja4sentinel/config.yml.default
|
||||
install -m 640 %{_sourcedir}/config.yml %{buildroot}/usr/share/ja4sentinel/config.yml
|
||||
@ -109,6 +113,7 @@ fi
|
||||
%files
|
||||
/usr/bin/ja4sentinel
|
||||
/usr/lib/systemd/system/ja4sentinel.service
|
||||
/etc/logrotate.d/ja4sentinel
|
||||
/usr/share/ja4sentinel/config.yml
|
||||
%config(noreplace) /etc/ja4sentinel/config.yml.default
|
||||
%dir /etc/ja4sentinel
|
||||
@ -117,6 +122,18 @@ fi
|
||||
%dir /var/run/logcorrelator
|
||||
|
||||
%changelog
|
||||
* Mon Mar 02 2026 Jacquin Antoine <rpm@arkel.fr> - 1.1.0-1
|
||||
- Add logrotate configuration for automatic log file rotation
|
||||
- Add SIGHUP signal handling for log file reopening (systemctl reload)
|
||||
- Add ExecReload to systemd service for graceful log rotation
|
||||
- Add Reopenable interface for output writers supporting log rotation
|
||||
- Add FileWriter.Reopen() method for log file rotation support
|
||||
- Add MultiWriter.Reopen() method to propagate rotation to all writers
|
||||
- Update main.go to handle SIGHUP signal for log rotation
|
||||
- Add packaging/logrotate/ja4sentinel configuration file
|
||||
- Update architecture.yml with logrotate and reload documentation
|
||||
- Update Dockerfile.package to include logrotate file in RPM build
|
||||
|
||||
* Mon Mar 02 2026 Jacquin Antoine <rpm@arkel.fr> - 1.0.9-1
|
||||
- Add SNI (Server Name Indication) extraction from TLS ClientHello
|
||||
- Add ALPN (Application-Layer Protocol Negotiation) extraction
|
||||
|
||||
@ -10,6 +10,7 @@ User=root
|
||||
Group=root
|
||||
WorkingDirectory=/var/lib/ja4sentinel
|
||||
ExecStart=/usr/bin/ja4sentinel --config /etc/ja4sentinel/config.yml
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
WatchdogSec=30
|
||||
|
||||
Reference in New Issue
Block a user