// Package config lädt und validiert die Portal-Konfiguration. package config import ( "fmt" "time" "gopkg.in/yaml.v3" ) // Duration erlaubt Dauerangaben als String ("10m") in YAML. type Duration time.Duration func (d *Duration) UnmarshalYAML(node *yaml.Node) error { var s string if err := node.Decode(&s); err != nil { return fmt.Errorf("Dauer muss eine Zeichenkette sein (z. B. \"10m\"): %w", err) } parsed, err := time.ParseDuration(s) if err != nil { return fmt.Errorf("ungültige Dauer %q (erwartet z. B. \"10m\", \"90s\"): %w", s, err) } if parsed <= 0 { return fmt.Errorf("Dauer %q muss positiv sein", s) } *d = Duration(parsed) return nil } func (d Duration) String() string { return time.Duration(d).String() } type Config struct { Portal PortalConfig `yaml:"portal"` OPNsense OPNsenseConfig `yaml:"opnsense"` AD ADConfig `yaml:"ad"` Matching MatchingConfig `yaml:"matching"` Logging LoggingConfig `yaml:"logging"` } type PortalConfig struct { Listen string `yaml:"listen"` TLSCert string `yaml:"tls_cert"` TLSKey string `yaml:"tls_key"` SessionTTL Duration `yaml:"session_ttl"` Title string `yaml:"title"` LogoFile string `yaml:"logo_file"` SupportContact string `yaml:"support_contact"` UpdateCheck bool `yaml:"update_check"` } type OPNsenseConfig struct { URL string `yaml:"url"` APIKey string `yaml:"api_key"` APIKeyFile string `yaml:"api_key_file"` APISecret string `yaml:"api_secret"` APISecretFile string `yaml:"api_secret_file"` CAFile string `yaml:"ca_file"` InsecureSkipVerify bool `yaml:"insecure_skip_verify"` } type ADConfig struct { Domain string `yaml:"domain"` BaseDN string `yaml:"base_dn"` Servers []string `yaml:"servers"` Port int `yaml:"port"` TLSMode string `yaml:"tls_mode"` // "ldaps" (Default) oder "starttls" // MinTLSVersion ist die niedrigste akzeptierte TLS-Version zum // Verzeichnisdienst: "1.0", "1.1", "1.2" (Default) oder "1.3". // Unter 1.2 nur für Altsysteme, die nichts Besseres können. MinTLSVersion string `yaml:"min_tls_version"` // MaxTLSVersion deckelt die angebotene TLS-Version, Default "1.3". // Auf "1.2" setzen, wenn ein alter Schannel-Stack den Verbindungsaufbau // bereits am TLS-1.3-ClientHello abbricht. MaxTLSVersion string `yaml:"max_tls_version"` BindUser string `yaml:"bind_user"` BindPassword string `yaml:"bind_password"` BindPasswordFile string `yaml:"bind_password_file"` VPNGroup string `yaml:"vpn_group"` CAFile string `yaml:"ca_file"` Timeout Duration `yaml:"timeout"` } type MatchingConfig struct { CNPattern string `yaml:"cn_pattern"` CNRegex string `yaml:"cn_regex"` } type LoggingConfig struct { Level string `yaml:"level"` AuditLog string `yaml:"audit_log"` MaxSizeMB int `yaml:"max_size_mb"` MaxBackups int `yaml:"max_backups"` Compress bool `yaml:"compress"` } // Defaults liefert eine Config mit allen Vorgabewerten. func Defaults() *Config { return &Config{ Portal: PortalConfig{ Listen: "0.0.0.0:8443", SessionTTL: Duration(10 * time.Minute), Title: "VPN-Portal", }, AD: ADConfig{ Port: 636, TLSMode: "ldaps", MinTLSVersion: "1.2", MaxTLSVersion: "1.3", Timeout: Duration(8 * time.Second), }, Matching: MatchingConfig{CNPattern: "{username}"}, Logging: LoggingConfig{ Level: "info", MaxSizeMB: 50, MaxBackups: 5, Compress: true, }, } }