// SPDX-License-Identifier: GPL-2.0-or-later // Copyright (C) 2026 Bryan Joshua Pedini package main import ( "fmt" "os" "strconv" ) type Config struct { PDNSAPIKey string ListenAddr string MySQLDSN string TablePrefix string PDNSServerID string DefaultTTL int LogLevel string PUID int PGID int } // LoadConfig reads configuration exclusively from environment variables. // Missing/invalid required variables are a fatal startup error. func LoadConfig() *Config { c := &Config{ ListenAddr: envOrDefault("LISTEN_ADDR", ":3000"), TablePrefix: envOrDefault("TABLE_PREFIX", "coredns_"), PDNSServerID: envOrDefault("PDNS_SERVER_ID", "localhost"), LogLevel: envOrDefault("LOG_LEVEL", "info"), } c.PDNSAPIKey = requireEnv("PDNS_API_KEY") c.MySQLDSN = requireEnv("MYSQL_DSN") c.DefaultTTL = requirePositiveInt("DEFAULT_TTL", 120) switch c.LogLevel { case "debug", "info", "warn", "error": default: fatalf("invalid LOG_LEVEL %q: must be one of debug, info, warn, error", c.LogLevel) } c.PUID = requireInt("PUID") c.PGID = requireInt("PGID") if c.PUID == 0 || c.PGID == 0 { fatalf("refusing to serve as root: PUID and PGID must both be non-zero") } return c } func envOrDefault(key, def string) string { if v, ok := os.LookupEnv(key); ok && v != "" { return v } return def } func requireEnv(key string) string { v, ok := os.LookupEnv(key) if !ok || v == "" { fatalf("missing required environment variable: %s", key) } return v } func requireInt(key string) int { v := requireEnv(key) n, err := strconv.Atoi(v) if err != nil { fatalf("invalid %s: %q is not an integer", key, v) } return n } func requirePositiveInt(key string, def int) int { v, ok := os.LookupEnv(key) if !ok || v == "" { return def } n, err := strconv.Atoi(v) if err != nil || n <= 0 { fatalf("invalid %s: %q is not a positive integer", key, v) } return n } func fatalf(format string, args ...any) { fmt.Fprintf(os.Stderr, "config error: "+format+"\n", args...) os.Exit(1) }