v1.1.11: Fix exclude_source_ips config loading and debug logging
Major fixes: - Add exclude_source_ips to mergeConfigs() - config file values now properly loaded - Add validation for exclude_source_ips (IP/CIDR format validation) - Remove JA4SENTINEL_LOG_LEVEL env var from systemd service - Config file log_level now respected without env override Debug logging improvements: - Log IP filter entries at startup (debug mode) - Track filtered packet count with atomic counter - Display filter statistics at shutdown via GetFilterStats() - New debug logs in tlsparse component Testing: - Add 6 new unit tests for exclude_source_ips and log_level config loading - Test mergeConfigs() behavior with empty/override values - Test validation of invalid IPs and CIDR ranges Documentation: - Update architecture.yml with ipfilter module - Document config loading priority and notes - Update api.Config fields (LocalIPs, ExcludeSourceIPs, LogLevel) Files changed: - internal/config/loader.go (merge, validation, helpers) - internal/config/loader_test.go (6 new tests) - internal/tlsparse/parser.go (GetFilterStats, counter) - cmd/ja4sentinel/main.go (debug logging) - packaging/systemd/ja4sentinel.service (remove env var) - architecture.yml (ipfilter module, config_loading section) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@ -175,6 +175,11 @@ func mergeConfigs(base, override api.AppConfig) api.AppConfig {
|
||||
result.Core.LogLevel = override.Core.LogLevel
|
||||
}
|
||||
|
||||
// Merge exclude_source_ips (override takes precedence)
|
||||
if len(override.Core.ExcludeSourceIPs) > 0 {
|
||||
result.Core.ExcludeSourceIPs = override.Core.ExcludeSourceIPs
|
||||
}
|
||||
|
||||
if len(override.Outputs) > 0 {
|
||||
result.Outputs = override.Outputs
|
||||
}
|
||||
@ -218,6 +223,27 @@ func (l *LoaderImpl) validate(config api.AppConfig) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate exclude_source_ips (if provided)
|
||||
if len(config.Core.ExcludeSourceIPs) > 0 {
|
||||
for i, ip := range config.Core.ExcludeSourceIPs {
|
||||
if ip == "" {
|
||||
return fmt.Errorf("exclude_source_ips[%d]: entry cannot be empty", i)
|
||||
}
|
||||
// Basic validation: check if it looks like an IP or CIDR
|
||||
if !strings.Contains(ip, "/") {
|
||||
// Single IP - basic check
|
||||
if !isValidIP(ip) {
|
||||
return fmt.Errorf("exclude_source_ips[%d]: invalid IP address %q", i, ip)
|
||||
}
|
||||
} else {
|
||||
// CIDR - basic check
|
||||
if !isValidCIDR(ip) {
|
||||
return fmt.Errorf("exclude_source_ips[%d]: invalid CIDR %q", i, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allowedTypes := map[string]struct{}{
|
||||
"stdout": {},
|
||||
"file": {},
|
||||
@ -257,3 +283,47 @@ func ToJSON(config api.AppConfig) string {
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// isValidIP checks if a string is a valid IP address
|
||||
func isValidIP(ip string) bool {
|
||||
if ip == "" {
|
||||
return false
|
||||
}
|
||||
// Simple validation: check if it contains only valid IP characters
|
||||
for _, ch := range ip {
|
||||
if !((ch >= '0' && ch <= '9') || ch == '.') {
|
||||
// Could be IPv6
|
||||
if ch == ':' {
|
||||
return true // Accept IPv6 without detailed validation
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isValidCIDR checks if a string is a valid CIDR notation
|
||||
func isValidCIDR(cidr string) bool {
|
||||
if cidr == "" {
|
||||
return false
|
||||
}
|
||||
parts := strings.Split(cidr, "/")
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
// Check IP part
|
||||
if !isValidIP(parts[0]) {
|
||||
return false
|
||||
}
|
||||
// Check prefix length
|
||||
prefix, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(parts[0], ":") {
|
||||
// IPv6
|
||||
return prefix >= 0 && prefix <= 128
|
||||
}
|
||||
// IPv4
|
||||
return prefix >= 0 && prefix <= 32
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user