package config import ( "os" "strconv" "strings" "time" ) type Config struct { ServiceAddr string PostgresURL string ClickHouseURL string ClickHouseDB string ClickHouseUser string ClickHousePassword string KafkaBrokers []string KafkaTopic string KafkaGroup string EnrollmentKey string TenantID string MaxBodyBytes int64 MaxBatchEvents int RawArchiveEnabled bool RawSpoolDir string RawRetention time.Duration DetectorInterval time.Duration DetectorLookback time.Duration UIQueryLimit int UIUsername string UIPassword string } func Load() Config { return Config{ ServiceAddr: env("SERVICE_ADDR", ":8080"), PostgresURL: env("POSTGRES_URL", "postgres://siem:siem@postgres:5432/siem?sslmode=disable"), ClickHouseURL: strings.TrimRight(env("CLICKHOUSE_URL", "http://clickhouse:8123"), "/"), ClickHouseDB: env("CLICKHOUSE_DB", "siem"), ClickHouseUser: env("CLICKHOUSE_USER", "siem"), ClickHousePassword: env("CLICKHOUSE_PASSWORD", "siem"), KafkaBrokers: splitCSV(env("KAFKA_BROKERS", "redpanda:9092")), KafkaTopic: env("KAFKA_TOPIC", "siem-events"), KafkaGroup: env("KAFKA_GROUP", "siem-processor-v1"), EnrollmentKey: env("ENROLLMENT_KEY", "change-me"), TenantID: env("TENANT_ID", "default"), MaxBodyBytes: int64(envInt("MAX_BODY_BYTES", 8*1024*1024)), MaxBatchEvents: envInt("MAX_BATCH_EVENTS", 1000), RawArchiveEnabled: envBool("RAW_ARCHIVE_ENABLED", true), RawSpoolDir: env("RAW_SPOOL_DIR", "/var/spool/siem-raw"), RawRetention: envDuration("RAW_RETENTION", 30*24*time.Hour), DetectorInterval: envDuration("DETECTOR_INTERVAL", 30*time.Second), DetectorLookback: envDuration("DETECTOR_LOOKBACK", 20*time.Minute), UIQueryLimit: envInt("UI_QUERY_LIMIT", 500), UIUsername: env("UI_USERNAME", "admin"), UIPassword: env("UI_PASSWORD", "change-me"), } } func env(k, d string) string { if v := strings.TrimSpace(os.Getenv(k)); v != "" { return v } return d } func envInt(k string, d int) int { v := strings.TrimSpace(os.Getenv(k)) if v == "" { return d } n, e := strconv.Atoi(v) if e != nil { return d } return n } func envBool(k string, d bool) bool { v := strings.ToLower(strings.TrimSpace(os.Getenv(k))) if v == "" { return d } return v == "1" || v == "true" || v == "yes" || v == "on" } func envDuration(k string, d time.Duration) time.Duration { v := strings.TrimSpace(os.Getenv(k)) if v == "" { return d } x, e := time.ParseDuration(v) if e != nil { return d } return x } func splitCSV(v string) []string { var out []string for _, p := range strings.Split(v, ",") { if p = strings.TrimSpace(p); p != "" { out = append(out, p) } } return out }