v1.0.8: Add configurable log level and immediate service stop
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled

- Add log_level config option (debug, info, warn, error)
- Add JA4SENTINEL_LOG_LEVEL environment variable support
- Set TimeoutStopSec=2 for immediate stop on restart/stop
- Consolidate config files into single example (config.yml.example)
- Update RPM changelog

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Jacquin Antoine
2026-03-01 02:51:11 +01:00
parent d89c90dc03
commit fd162982d9
8 changed files with 57 additions and 56 deletions

View File

@ -35,7 +35,7 @@ COPY . .
# Build binary for Linux # Build binary for Linux
# Binary will be dynamically linked but compatible with all RHEL-based distros # Binary will be dynamically linked but compatible with all RHEL-based distros
ARG VERSION=1.0.7 ARG VERSION=1.0.8
ARG BUILD_TIME="" ARG BUILD_TIME=""
ARG GIT_COMMIT="" ARG GIT_COMMIT=""
RUN mkdir -p dist && \ RUN mkdir -p dist && \
@ -53,7 +53,7 @@ FROM rockylinux:9 AS rpm-builder
WORKDIR /package WORKDIR /package
# VERSION must be redeclared for each stage that needs it # VERSION must be redeclared for each stage that needs it
ARG VERSION=1.0.7 ARG VERSION=1.0.8
# Install rpm-build tools (Rocky Linux 9) # Install rpm-build tools (Rocky Linux 9)
RUN dnf install -y \ RUN dnf install -y \
@ -72,7 +72,7 @@ COPY packaging/rpm/ja4sentinel.spec /root/rpmbuild/SPECS/ja4sentinel.spec
# Copy binary from Go builder and other files to SOURCES # Copy binary from Go builder and other files to SOURCES
COPY --from=builder /build/dist/ja4sentinel /root/rpmbuild/SOURCES/ja4sentinel COPY --from=builder /build/dist/ja4sentinel /root/rpmbuild/SOURCES/ja4sentinel
COPY packaging/systemd/ja4sentinel.service /root/rpmbuild/SOURCES/ja4sentinel.service COPY packaging/systemd/ja4sentinel.service /root/rpmbuild/SOURCES/ja4sentinel.service
COPY packaging/systemd/config.yml /root/rpmbuild/SOURCES/config.yml COPY config.yml.example /root/rpmbuild/SOURCES/config.yml
# Set permissions # Set permissions
RUN chmod 755 /root/rpmbuild/SOURCES/ja4sentinel && \ RUN chmod 755 /root/rpmbuild/SOURCES/ja4sentinel && \

View File

@ -23,6 +23,7 @@ type Config struct {
BPFFilter string `json:"bpf_filter,omitempty"` BPFFilter string `json:"bpf_filter,omitempty"`
FlowTimeoutSec int `json:"flow_timeout_sec,omitempty"` // Timeout for TLS handshake extraction (default: 30) FlowTimeoutSec int `json:"flow_timeout_sec,omitempty"` // Timeout for TLS handshake extraction (default: 30)
PacketBufferSize int `json:"packet_buffer_size,omitempty"` // Buffer size for packet channel (default: 1000) PacketBufferSize int `json:"packet_buffer_size,omitempty"` // Buffer size for packet channel (default: 1000)
LogLevel string `json:"log_level,omitempty"` // Log level: debug, info, warn, error (default: info)
} }
// IPMeta contains IP metadata for stack fingerprinting // IPMeta contains IP metadata for stack fingerprinting
@ -241,6 +242,7 @@ const (
DefaultBPFFilter = "" DefaultBPFFilter = ""
DefaultFlowTimeout = 30 // seconds DefaultFlowTimeout = 30 // seconds
DefaultPacketBuffer = 1000 // packet channel buffer size DefaultPacketBuffer = 1000 // packet channel buffer size
DefaultLogLevel = "info"
) )
// DefaultConfig returns an AppConfig with sensible default values. // DefaultConfig returns an AppConfig with sensible default values.
@ -256,6 +258,7 @@ func DefaultConfig() AppConfig {
BPFFilter: DefaultBPFFilter, BPFFilter: DefaultBPFFilter,
FlowTimeoutSec: DefaultFlowTimeout, FlowTimeoutSec: DefaultFlowTimeout,
PacketBufferSize: DefaultPacketBuffer, PacketBufferSize: DefaultPacketBuffer,
LogLevel: DefaultLogLevel,
}, },
Outputs: []OutputConfig{}, Outputs: []OutputConfig{},
} }

View File

@ -22,7 +22,7 @@ import (
var ( var (
// Version information (set via ldflags) // Version information (set via ldflags)
Version = "1.0.7" Version = "1.0.8"
BuildTime = "unknown" BuildTime = "unknown"
GitCommit = "unknown" GitCommit = "unknown"
) )
@ -38,9 +38,22 @@ func main() {
os.Exit(0) os.Exit(0)
} }
// Create logger factory // Load configuration
cfgLoader := config.NewLoader(*configPath)
appConfig, err := cfgLoader.Load()
if err != nil {
// Create logger with default level for error reporting
loggerFactory := &logging.LoggerFactory{}
appLogger := loggerFactory.NewDefaultLogger()
appLogger.Error("main", "Failed to load configuration", map[string]string{
"error": err.Error(),
})
os.Exit(1)
}
// Create logger factory with configured log level
loggerFactory := &logging.LoggerFactory{} loggerFactory := &logging.LoggerFactory{}
appLogger := loggerFactory.NewDefaultLogger() appLogger := loggerFactory.NewLogger(appConfig.Core.LogLevel)
appLogger.Info("main", "Starting ja4sentinel", map[string]string{ appLogger.Info("main", "Starting ja4sentinel", map[string]string{
"version": Version, "version": Version,
@ -48,19 +61,10 @@ func main() {
"git_commit": GitCommit, "git_commit": GitCommit,
}) })
// Load configuration
cfgLoader := config.NewLoader(*configPath)
appConfig, err := cfgLoader.Load()
if err != nil {
appLogger.Error("main", "Failed to load configuration", map[string]string{
"error": err.Error(),
})
os.Exit(1)
}
appLogger.Info("main", "Configuration loaded", map[string]string{ appLogger.Info("main", "Configuration loaded", map[string]string{
"interface": appConfig.Core.Interface, "interface": appConfig.Core.Interface,
"listen_ports": formatPorts(appConfig.Core.ListenPorts), "listen_ports": formatPorts(appConfig.Core.ListenPorts),
"log_level": appConfig.Core.LogLevel,
}) })
// Create context with cancellation for graceful shutdown // Create context with cancellation for graceful shutdown

View File

@ -19,6 +19,10 @@ core:
# Buffer size for packet channel (default: 1000, increase for high-traffic environments) # Buffer size for packet channel (default: 1000, increase for high-traffic environments)
packet_buffer_size: 1000 packet_buffer_size: 1000
# Log level: debug, info, warn, error (default: info)
# Can be overridden by JA4SENTINEL_LOG_LEVEL environment variable
log_level: info
outputs: outputs:
# Output to stdout (JSON lines) # Output to stdout (JSON lines)
- type: stdout - type: stdout

View File

@ -104,6 +104,11 @@ func (l *LoaderImpl) loadFromEnv(config api.AppConfig) api.AppConfig {
} }
} }
// JA4SENTINEL_LOG_LEVEL
if val := os.Getenv("JA4SENTINEL_LOG_LEVEL"); val != "" {
config.Core.LogLevel = val
}
return config return config
} }
@ -166,6 +171,10 @@ func mergeConfigs(base, override api.AppConfig) api.AppConfig {
result.Core.PacketBufferSize = override.Core.PacketBufferSize result.Core.PacketBufferSize = override.Core.PacketBufferSize
} }
if override.Core.LogLevel != "" {
result.Core.LogLevel = override.Core.LogLevel
}
if len(override.Outputs) > 0 { if len(override.Outputs) > 0 {
result.Outputs = override.Outputs result.Outputs = override.Outputs
} }
@ -196,6 +205,19 @@ func (l *LoaderImpl) validate(config api.AppConfig) error {
return fmt.Errorf("packet_buffer_size must be between 1 and 1000000") return fmt.Errorf("packet_buffer_size must be between 1 and 1000000")
} }
// Validate log level
validLogLevels := map[string]struct{}{
"debug": {},
"info": {},
"warn": {},
"error": {},
}
if config.Core.LogLevel != "" {
if _, ok := validLogLevels[config.Core.LogLevel]; !ok {
return fmt.Errorf("log_level must be one of: debug, info, warn, error")
}
}
allowedTypes := map[string]struct{}{ allowedTypes := map[string]struct{}{
"stdout": {}, "stdout": {},
"file": {}, "file": {},

View File

@ -3,7 +3,7 @@
%if %{defined build_version} %if %{defined build_version}
%define spec_version %{build_version} %define spec_version %{build_version}
%else %else
%define spec_version 1.0.7 %define spec_version 1.0.8
%endif %endif
Name: ja4sentinel Name: ja4sentinel
@ -117,6 +117,12 @@ fi
%dir /var/run/logcorrelator %dir /var/run/logcorrelator
%changelog %changelog
* Sun Mar 01 2026 Jacquin Antoine <rpm@arkel.fr> - 1.0.8-1
- Add configurable log level (debug, info, warn, error) via config.yml
- Add JA4SENTINEL_LOG_LEVEL environment variable support
- Set TimeoutStopSec=2 for immediate service stop on restart/stop
- Consolidate config files into single example (config.yml.example)
* Sat Feb 28 2026 Jacquin Antoine <rpm@arkel.fr> - 1.0.4-1 * Sat Feb 28 2026 Jacquin Antoine <rpm@arkel.fr> - 1.0.4-1
- Add systemd sdnotify support (READY, WATCHDOG, STOPPING signals) - Add systemd sdnotify support (READY, WATCHDOG, STOPPING signals)
- Enable systemd watchdog with 30s timeout - Enable systemd watchdog with 30s timeout

View File

@ -1,39 +0,0 @@
# Default configuration file for ja4sentinel
# This file is installed as /etc/ja4sentinel/config.yml.default
core:
# Network interface to capture traffic from
# Will be overridden by JA4SENTINEL_INTERFACE env var if set
interface: eth0
# TCP ports to monitor for TLS handshakes
listen_ports:
- 443
- 8443
# Optional BPF filter (leave empty for auto-generated filter based on listen_ports)
bpf_filter: ""
# Timeout in seconds for TLS handshake extraction (default: 30)
flow_timeout_sec: 30
# Buffer size for packet channel (default: 1000, increase for high-traffic environments)
packet_buffer_size: 1000
outputs:
# Output to stdout (JSON lines) - disabled by default for production
- type: stdout
enabled: false
params: {}
# Output to file
- type: file
enabled: true
params:
path: /var/log/ja4sentinel/ja4.log
# Output to UNIX socket (for systemd/journald or other consumers)
- type: unix_socket
enabled: true
params:
socket_path: /var/run/ja4sentinel.sock

View File

@ -13,6 +13,7 @@ ExecStart=/usr/bin/ja4sentinel --config /etc/ja4sentinel/config.yml
Restart=on-failure Restart=on-failure
RestartSec=5 RestartSec=5
WatchdogSec=30 WatchdogSec=30
TimeoutStopSec=2
NotifyAccess=main NotifyAccess=main
Environment=JA4SENTINEL_LOG_LEVEL=info Environment=JA4SENTINEL_LOG_LEVEL=info