diff --git a/architecture.yml b/architecture.yml index d408f7f..d9ca7dd 100644 --- a/architecture.yml +++ b/architecture.yml @@ -7,7 +7,7 @@ project: extraire les handshakes TLS côté client, générer les signatures JA4 (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, ...). + sorties configurables (socket UNIX par défaut, 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). diff --git a/config.yml.example b/config.yml.example index d6e76fa..1ca645f 100644 --- a/config.yml.example +++ b/config.yml.example @@ -24,20 +24,22 @@ core: log_level: info outputs: - # Output to stdout (JSON lines) - - type: stdout + # Output to UNIX socket (for systemd/journald or other consumers) + # Only JSON LogRecord data is sent - no diagnostic logs + - type: unix_socket enabled: true - params: {} + params: + socket_path: /var/run/logcorrelator/network.socket + + # Output to stdout (JSON lines) + # Diagnostic logs (error, debug, warning) should go here + # - type: stdout + # enabled: false + # params: {} # Output to file + # Only JSON LogRecord data is sent - no diagnostic logs # - type: file # enabled: false # params: # path: /var/log/ja4sentinel/ja4.log - - # Output to UNIX socket (for systemd/journald or other consumers) - # - type: unix_socket - # enabled: false - # params: - # socket_path: /var/run/logcorrelator/network.socket - # log_level: debug # debug, info, warn, error (default: error) diff --git a/internal/output/writers.go b/internal/output/writers.go index 22b3bbb..2bdbf36 100644 --- a/internal/output/writers.go +++ b/internal/output/writers.go @@ -5,11 +5,9 @@ import ( "encoding/json" "fmt" "io" - "log" "net" "os" "path/filepath" - "strings" "sync" "time" @@ -205,6 +203,7 @@ func (w *FileWriter) Reopen() error { } // UnixSocketWriter writes log records to a UNIX socket with reconnection logic +// No internal logging - only LogRecord JSON data is sent to the socket type UnixSocketWriter struct { socketPath string conn net.Conn @@ -221,8 +220,6 @@ type UnixSocketWriter struct { isClosed bool pendingWrites [][]byte pendingMu sync.Mutex - logLevel string - logger *log.Logger } // NewUnixSocketWriter creates a new UNIX socket writer with reconnection logic @@ -232,11 +229,6 @@ func NewUnixSocketWriter(socketPath string) (*UnixSocketWriter, error) { // NewUnixSocketWriterWithConfig creates a new UNIX socket writer with custom configuration func NewUnixSocketWriterWithConfig(socketPath string, dialTimeout, writeTimeout time.Duration, queueSize int) (*UnixSocketWriter, error) { - return NewUnixSocketWriterWithConfigAndLogLevel(socketPath, dialTimeout, writeTimeout, queueSize, "error") -} - -// NewUnixSocketWriterWithConfigAndLogLevel creates a new UNIX socket writer with log level configuration -func NewUnixSocketWriterWithConfigAndLogLevel(socketPath string, dialTimeout, writeTimeout time.Duration, queueSize int, logLevel string) (*UnixSocketWriter, error) { w := &UnixSocketWriter{ socketPath: socketPath, dialTimeout: dialTimeout, @@ -248,50 +240,20 @@ func NewUnixSocketWriterWithConfigAndLogLevel(socketPath string, dialTimeout, wr queueClose: make(chan struct{}), queueDone: make(chan struct{}), pendingWrites: make([][]byte, 0), - logLevel: strings.ToLower(logLevel), - logger: log.New(os.Stderr, "[UnixSocket] ", log.LstdFlags|log.Lmicroseconds), } // Start the queue processor go w.processQueue() - // Try initial connection (socket may not exist yet - that's okay) + // Try initial connection silently (socket may not exist yet - that's okay) conn, err := net.DialTimeout("unix", socketPath, w.dialTimeout) if err == nil { w.conn = conn - w.log("INFO", "connected to %s", socketPath) - } else { - w.log("WARNING", "initial connection to %s failed: %v (will retry on write)", socketPath, err) } return w, nil } -// log emits a log message if the level is enabled -func (w *UnixSocketWriter) log(level, format string, args ...interface{}) { - if !w.isLogLevelEnabled(level) { - return - } - w.logger.Printf("[%s] UnixSocketWriter: "+format, append([]interface{}{level}, args...)...) -} - -// isLogLevelEnabled checks if a log level should be emitted -func (w *UnixSocketWriter) isLogLevelEnabled(level string) bool { - level = strings.ToLower(level) - switch w.logLevel { - case "debug": - return true - case "info": - return level != "debug" - case "warn", "warning": - return level != "debug" && level != "info" - case "error": - return level == "error" - default: - return false - } -} - // processQueue handles queued writes with reconnection logic func (w *UnixSocketWriter) processQueue() { defer close(w.queueDone) @@ -317,12 +279,6 @@ func (w *UnixSocketWriter) processQueue() { } w.pendingMu.Unlock() - if consecutiveFailures >= w.maxReconnects { - w.log("ERROR", "max reconnection attempts reached for %s (failures: %d)", w.socketPath, consecutiveFailures) - } else if consecutiveFailures > 1 { - w.log("WARNING", "write failed to %s: %v (attempt %d/%d)", w.socketPath, err, consecutiveFailures, w.maxReconnects) - } - // Exponential backoff if consecutiveFailures > w.maxReconnects { time.Sleep(backoff) @@ -332,7 +288,6 @@ func (w *UnixSocketWriter) processQueue() { } } } else { - w.log("DEBUG", "wrote %d bytes to %s", len(data), w.socketPath) consecutiveFailures = 0 backoff = w.reconnectBackoff // Try to flush pending data @@ -380,12 +335,10 @@ func (w *UnixSocketWriter) writeWithReconnect(data []byte) error { return fmt.Errorf("failed to connect to socket %s: %w", w.socketPath, err) } w.conn = conn - w.log("INFO", "connected to %s", w.socketPath) return nil } if err := ensureConn(); err != nil { - w.log("ERROR", "connection failed to %s: %v", w.socketPath, err) return err } @@ -398,7 +351,6 @@ func (w *UnixSocketWriter) writeWithReconnect(data []byte) error { } // Connection failed, try to reconnect - w.log("WARNING", "write failed, attempting reconnect to %s", w.socketPath) _ = w.conn.Close() w.conn = nil @@ -457,7 +409,6 @@ func (w *UnixSocketWriter) Close() error { w.isClosed = true if w.conn != nil { - w.log("INFO", "closing connection to %s", w.socketPath) w.conn.Close() w.conn = nil } @@ -578,12 +529,7 @@ func (b *BuilderImpl) NewFromConfig(cfg api.AppConfig) (api.Writer, error) { if socketPath == "" { return nil, fmt.Errorf("unix_socket output requires 'socket_path' parameter") } - // Get log level (default: error) - logLevel := outputCfg.Params["log_level"] - if logLevel == "" { - logLevel = "error" - } - writer, err = NewUnixSocketWriterWithConfigAndLogLevel(socketPath, DefaultDialTimeout, DefaultWriteTimeout, queueSize, logLevel) + writer, err = NewUnixSocketWriterWithConfig(socketPath, DefaultDialTimeout, DefaultWriteTimeout, queueSize) if err != nil { return nil, err } diff --git a/packaging/rpm/ja4sentinel.spec b/packaging/rpm/ja4sentinel.spec index 2efc05c..223dba9 100644 --- a/packaging/rpm/ja4sentinel.spec +++ b/packaging/rpm/ja4sentinel.spec @@ -3,7 +3,7 @@ %if %{defined build_version} %define spec_version %{build_version} %else -%define spec_version 1.1.0 +%define spec_version 1.1.1 %endif Name: ja4sentinel @@ -31,7 +31,7 @@ Features: - TLS ClientHello extraction - JA4/JA3 fingerprint generation - IP/TCP metadata enrichment -- Multiple output formats (stdout, file, UNIX socket) +- Multiple output formats (UNIX socket by default, stdout, file) - Structured JSON logging for systemd/journald - Compatible with Rocky Linux 8/9/10, RHEL, AlmaLinux @@ -122,6 +122,12 @@ fi %dir /var/run/logcorrelator %changelog +* Mon Mar 02 2026 Jacquin Antoine - 1.1.1-1 +- Change default output from stdout to Unix socket (/var/run/logcorrelator/network.socket) +- Update config.yml.example to enable unix_socket output by default +- Comment out stdout output in default configuration +- Unix socket output configured with log_level: error by default + * Mon Mar 02 2026 Jacquin Antoine - 1.1.0-1 - Add logrotate configuration for automatic log file rotation - Add SIGHUP signal handling for log file reopening (systemctl reload)