Improves configuration handling and system settings APIs to support v4.24.0 features including runtime logging controls, adaptive polling configuration, and enhanced config export/persistence. Changes: - Add config override system for discovery service - Enhance system settings API with runtime logging controls - Improve config persistence and export functionality - Update security setup handling - Refine monitoring and discovery service integration These changes provide the backend support for the configuration features documented in the v4.24.0 release.
180 lines
5.7 KiB
Go
180 lines
5.7 KiB
Go
package discovery
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
pkgdiscovery "github.com/rcourtman/pulse-go-rewrite/pkg/discovery"
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/discovery/envdetect"
|
|
)
|
|
|
|
// BuildScanner creates a discovery scanner configured using the supplied discovery config.
|
|
func BuildScanner(cfg config.DiscoveryConfig) (*pkgdiscovery.Scanner, error) {
|
|
profile, err := envdetect.DetectEnvironment()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ApplyConfigToProfile(profile, cfg)
|
|
return pkgdiscovery.NewScannerWithProfile(profile), nil
|
|
}
|
|
|
|
// ApplyConfigToProfile mutates the supplied environment profile according to the discovery config.
|
|
func ApplyConfigToProfile(profile *envdetect.EnvironmentProfile, cfg config.DiscoveryConfig) {
|
|
if profile == nil {
|
|
return
|
|
}
|
|
|
|
// Environment override
|
|
if env, ok := environmentFromOverride(cfg.EnvironmentOverride); ok {
|
|
profile.Type = env
|
|
filterPhasesForEnvironment(profile, env)
|
|
} else if cfg.EnvironmentOverride != "" && strings.ToLower(cfg.EnvironmentOverride) != "auto" {
|
|
profile.Warnings = append(profile.Warnings, fmt.Sprintf("Unknown environment override: %s", cfg.EnvironmentOverride))
|
|
}
|
|
|
|
// Apply subnet blocklist
|
|
blocked := parseCIDRMap(cfg.SubnetBlocklist, &profile.Warnings)
|
|
if len(blocked) > 0 {
|
|
var filtered []envdetect.SubnetPhase
|
|
for _, phase := range profile.Phases {
|
|
var kept []net.IPNet
|
|
for _, subnet := range phase.Subnets {
|
|
if _, blocked := blocked[subnet.String()]; blocked {
|
|
continue
|
|
}
|
|
kept = append(kept, subnet)
|
|
}
|
|
if len(kept) > 0 {
|
|
phase.Subnets = kept
|
|
filtered = append(filtered, phase)
|
|
}
|
|
}
|
|
profile.Phases = filtered
|
|
}
|
|
|
|
// Apply subnet allowlist as highest priority phase
|
|
if len(cfg.SubnetAllowlist) > 0 {
|
|
allowlist := parseCIDRs(cfg.SubnetAllowlist, &profile.Warnings)
|
|
if len(allowlist) > 0 {
|
|
if len(blocked) > 0 {
|
|
filtered := allowlist[:0]
|
|
for _, subnet := range allowlist {
|
|
if _, skip := blocked[subnet.String()]; skip {
|
|
continue
|
|
}
|
|
filtered = append(filtered, subnet)
|
|
}
|
|
allowlist = filtered
|
|
}
|
|
}
|
|
if len(allowlist) > 0 {
|
|
allowPhase := envdetect.SubnetPhase{
|
|
Name: "config_allowlist",
|
|
Subnets: allowlist,
|
|
Confidence: 1.0,
|
|
Priority: 0,
|
|
}
|
|
profile.Phases = append([]envdetect.SubnetPhase{allowPhase}, profile.Phases...)
|
|
}
|
|
}
|
|
|
|
// Override scan policy
|
|
if cfg.MaxHostsPerScan > 0 {
|
|
profile.Policy.MaxHostsPerScan = cfg.MaxHostsPerScan
|
|
}
|
|
if cfg.MaxConcurrent > 0 {
|
|
profile.Policy.MaxConcurrent = cfg.MaxConcurrent
|
|
}
|
|
profile.Policy.EnableReverseDNS = cfg.EnableReverseDNS
|
|
profile.Policy.ScanGateways = cfg.ScanGateways
|
|
|
|
if cfg.DialTimeout > 0 {
|
|
profile.Policy.DialTimeout = time.Duration(cfg.DialTimeout) * time.Millisecond
|
|
}
|
|
if cfg.HTTPTimeout > 0 {
|
|
profile.Policy.HTTPTimeout = time.Duration(cfg.HTTPTimeout) * time.Millisecond
|
|
}
|
|
}
|
|
|
|
func parseCIDRs(values []string, warnings *[]string) []net.IPNet {
|
|
var subnets []net.IPNet
|
|
for _, value := range values {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
continue
|
|
}
|
|
_, ipNet, err := net.ParseCIDR(value)
|
|
if err != nil {
|
|
if warnings != nil {
|
|
*warnings = append(*warnings, fmt.Sprintf("Invalid CIDR '%s' ignored", value))
|
|
}
|
|
continue
|
|
}
|
|
subnets = append(subnets, *ipNet)
|
|
}
|
|
return subnets
|
|
}
|
|
|
|
func parseCIDRMap(values []string, warnings *[]string) map[string]struct{} {
|
|
cidrs := parseCIDRs(values, warnings)
|
|
result := make(map[string]struct{}, len(cidrs))
|
|
for _, cidr := range cidrs {
|
|
result[cidr.String()] = struct{}{}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func environmentFromOverride(value string) (envdetect.Environment, bool) {
|
|
normalized := strings.ToLower(strings.TrimSpace(value))
|
|
switch normalized {
|
|
case "", "auto":
|
|
return envdetect.Unknown, false
|
|
case "native":
|
|
return envdetect.Native, true
|
|
case "docker_host":
|
|
return envdetect.DockerHost, true
|
|
case "docker_bridge":
|
|
return envdetect.DockerBridge, true
|
|
case "lxc_privileged":
|
|
return envdetect.LXCPrivileged, true
|
|
case "lxc_unprivileged":
|
|
return envdetect.LXCUnprivileged, true
|
|
default:
|
|
return envdetect.Unknown, false
|
|
}
|
|
}
|
|
|
|
func filterPhasesForEnvironment(profile *envdetect.EnvironmentProfile, env envdetect.Environment) {
|
|
if len(profile.Phases) == 0 {
|
|
return
|
|
}
|
|
|
|
var keep []envdetect.SubnetPhase
|
|
for _, phase := range profile.Phases {
|
|
name := strings.ToLower(phase.Name)
|
|
switch env {
|
|
case envdetect.Native, envdetect.DockerHost, envdetect.LXCPrivileged:
|
|
if strings.Contains(name, "local") || strings.Contains(name, "host") {
|
|
keep = append(keep, phase)
|
|
}
|
|
case envdetect.DockerBridge:
|
|
if strings.Contains(name, "container") || strings.Contains(name, "inferred") || strings.Contains(name, "host") {
|
|
keep = append(keep, phase)
|
|
}
|
|
case envdetect.LXCUnprivileged:
|
|
if strings.Contains(name, "lxc") || strings.Contains(name, "container") || strings.Contains(name, "parent") {
|
|
keep = append(keep, phase)
|
|
}
|
|
default:
|
|
keep = append(keep, phase)
|
|
}
|
|
}
|
|
|
|
if len(keep) > 0 {
|
|
profile.Phases = keep
|
|
}
|
|
}
|