Files
pdns-admin/internal/config/config.go
2026-06-18 22:32:42 -03:00

154 lines
4.5 KiB
Go

package config
import (
"errors"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"github.com/ilyakaznacheev/cleanenv"
)
type Config struct {
Addr string `yaml:"addr" env:"ADDR" env-default:":8080"`
PDNSAPIURL string `yaml:"pdns_api_url" env:"PDNS_API_URL" env-default:"http://localhost:8081"`
PDNSAPIKey string `yaml:"pdns_api_key" env:"PDNS_API_KEY"`
PDNSServerID string `yaml:"pdns_server_id" env:"PDNS_SERVER_ID" env-default:"localhost"`
Auth AuthConfig `yaml:"auth"`
}
type AuthConfig struct {
Disabled bool `yaml:"disabled" env:"AUTH_DISABLED"`
LDAP LDAPConfig `yaml:"ldap"`
}
type LDAPConfig struct {
URL string `yaml:"url" env:"AUTH_LDAP_URL"`
StartTLS bool `yaml:"start_tls" env:"AUTH_LDAP_START_TLS"`
InsecureSkipVerify bool `yaml:"insecure_skip_verify" env:"AUTH_LDAP_INSECURE_SKIP_VERIFY"`
BindDN string `yaml:"bind_dn" env:"AUTH_LDAP_BIND_DN"`
BindPassword string `yaml:"bind_password" env:"AUTH_LDAP_BIND_PASSWORD"`
UserBaseDN string `yaml:"user_base_dn" env:"AUTH_LDAP_USER_BASE_DN"`
UsernameAttribute string `yaml:"username_attribute" env:"AUTH_LDAP_USERNAME_ATTRIBUTE" env-default:"uid"`
UserFilter string `yaml:"user_filter" env:"AUTH_LDAP_USER_FILTER" env-default:"({username_attribute}={username})"`
GroupBaseDN string `yaml:"group_base_dn" env:"AUTH_LDAP_GROUP_BASE_DN"`
GroupFilter string `yaml:"group_filter" env:"AUTH_LDAP_GROUP_FILTER"`
}
func Load() (Config, error) {
path := strings.TrimSpace(os.Getenv("CONFIG_FILE"))
if path == "" {
path = defaultConfigPath()
}
return LoadFile(path)
}
func LoadFile(path string) (Config, error) {
var cfg Config
var err error
if strings.TrimSpace(path) == "" {
err = cleanenv.ReadEnv(&cfg)
} else {
err = cleanenv.ReadConfig(path, &cfg)
}
if err != nil {
return Config{}, err
}
normalize(&cfg)
if cfg.PDNSAPIKey == "" {
return Config{}, errors.New("PDNS_API_KEY is required")
}
if err := validateAuth(cfg.Auth); err != nil {
return Config{}, err
}
return cfg, nil
}
func defaultConfigPath() string {
homeDir, err := os.UserHomeDir()
if err != nil {
homeDir = ""
}
return firstExistingConfigPath(configSearchPaths(homeDir, runtime.GOOS))
}
func configSearchPaths(homeDir, goos string) []string {
paths := make([]string, 0, 3)
if isUnixLike(goos) {
paths = append(paths, "/etc/pdns_admin/config.yaml")
}
if homeDir != "" {
paths = append(paths, filepath.Join(homeDir, "config.yaml"))
}
return append(paths, "config.yaml")
}
func isUnixLike(goos string) bool {
switch goos {
case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "linux", "netbsd", "openbsd", "solaris":
return true
default:
return false
}
}
func firstExistingConfigPath(paths []string) string {
for _, path := range paths {
if _, err := os.Stat(path); err == nil {
return path
}
}
return ""
}
func normalize(cfg *Config) {
cfg.Addr = strings.TrimSpace(cfg.Addr)
cfg.PDNSAPIURL = strings.TrimSpace(cfg.PDNSAPIURL)
cfg.PDNSAPIKey = strings.TrimSpace(cfg.PDNSAPIKey)
cfg.PDNSServerID = strings.TrimSpace(cfg.PDNSServerID)
cfg.Auth.LDAP.URL = strings.TrimSpace(cfg.Auth.LDAP.URL)
cfg.Auth.LDAP.BindDN = strings.TrimSpace(cfg.Auth.LDAP.BindDN)
cfg.Auth.LDAP.BindPassword = strings.TrimSpace(cfg.Auth.LDAP.BindPassword)
cfg.Auth.LDAP.UserBaseDN = strings.TrimSpace(cfg.Auth.LDAP.UserBaseDN)
cfg.Auth.LDAP.UsernameAttribute = strings.TrimSpace(cfg.Auth.LDAP.UsernameAttribute)
cfg.Auth.LDAP.UserFilter = strings.TrimSpace(cfg.Auth.LDAP.UserFilter)
cfg.Auth.LDAP.GroupBaseDN = strings.TrimSpace(cfg.Auth.LDAP.GroupBaseDN)
cfg.Auth.LDAP.GroupFilter = strings.TrimSpace(cfg.Auth.LDAP.GroupFilter)
}
func validateAuth(auth AuthConfig) error {
if auth.Disabled {
return nil
}
missing := make([]string, 0)
for field, value := range map[string]string{
"AUTH_LDAP_URL": auth.LDAP.URL,
"AUTH_LDAP_BIND_DN": auth.LDAP.BindDN,
"AUTH_LDAP_BIND_PASSWORD": auth.LDAP.BindPassword,
"AUTH_LDAP_USER_BASE_DN": auth.LDAP.UserBaseDN,
"AUTH_LDAP_USERNAME_ATTRIBUTE": auth.LDAP.UsernameAttribute,
"AUTH_LDAP_USER_FILTER": auth.LDAP.UserFilter,
} {
if value == "" {
missing = append(missing, field)
}
}
if auth.LDAP.GroupFilter != "" && auth.LDAP.GroupBaseDN == "" {
missing = append(missing, "AUTH_LDAP_GROUP_BASE_DN")
}
if len(missing) > 0 {
sort.Strings(missing)
return errors.New("LDAP auth is enabled; missing required configuration: " + strings.Join(missing, ", "))
}
return nil
}