From 4f502ff6f53c7165f7064d30ed7d28d1a09ed328 Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Thu, 24 Oct 2024 15:26:34 +0300 Subject: [PATCH] feat: config rearchitect, lower qName early --- internal/components/manage.templ | 2 +- internal/config/config.go | 92 ++++++++++++++++++++++++-------- internal/dns/query.go | 26 +++++---- internal/dns/server.go | 2 +- 4 files changed, 88 insertions(+), 34 deletions(-) diff --git a/internal/components/manage.templ b/internal/components/manage.templ index dd1fab5..5be9bb2 100644 --- a/internal/components/manage.templ +++ b/internal/components/manage.templ @@ -4,7 +4,7 @@ import "git.src.quest/~liljamo/felu/internal/config" import "fmt" func getDomainPattern() string { - return fmt.Sprintf(".%s", config.FeluConfig.DNSPattern) + return fmt.Sprintf(".%s", config.FeluConfig.Domain) } func getUpdateACommand() string { diff --git a/internal/config/config.go b/internal/config/config.go index 5322804..e0593eb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -8,21 +8,39 @@ // Package config implements the global program config. package config -import "git.src.quest/~liljamo/erya-go/util" +import ( + "fmt" + "log/slog" + "net" + "os" + "strconv" + + "github.com/miekg/dns" +) + +// TODO: Switch back to erya-go once I've rearchitected it. +//import "git.src.quest/~liljamo/erya-go/util" // FeluConfig is a global for accessing the global config. var FeluConfig *config type config struct { + // Domain, is fqdn'd when read. + // + // Used for NS records and as DNS pattern. + Domain string + // IPv4 address to return on A queries for Domain. + IPv4 string + + // The name displayed in the frontend. ServiceName string - DDNSDomain string - + // The email to return in SOAs (e.g. admin.felu.arpa.), is fqdn'd when read. SOAEmail string - // Initial email for the admin user, only used if no admin account (e.g. first boot) + // Initial email for the admin user, used if no admin account exists (e.g. first boot). InitialAdminEmail string - // Initial password for the admin user, only used if no admin account (e.g. first boot) + // Initial password for the admin user, used if no admin account exists (e.g. first boot). InitialAdminPwd string LogLevel string @@ -38,34 +56,66 @@ type config struct { DNSBindIP string DNSBindPort int32 - // Domain pattern, no leading dot, but with trailing dot - DNSPattern string } // InitConfig initializes the global program config from environment variables. func InitConfig() { + ipv4 := net.ParseIP(loadEnvStr("FELU_IPV4")).To4() + if ipv4 == nil { + panic("Invalid IPv4") + } + FeluConfig = &config{ - ServiceName: util.LoadEnvStr("FELU_SERVICE_NAME", "FeluDDNS"), + Domain: dns.Fqdn(loadEnvStr("FELU_DOMAIN")), + IPv4: ipv4.String(), + + ServiceName: loadEnvStrDef("FELU_SERVICE_NAME", "FeluDDNS"), + + SOAEmail: dns.Fqdn(loadEnvStr("FELU_SOA_EMAIL")), - // FIXME: panic without this, should do same for many of these - DDNSDomain: util.LoadEnvStr("FELU_DDNS_DOMAIN", "ddns.staging.fuckdns.org"), - APIUrl: util.LoadEnvStr("FELU_API_URL", "MUST_SET"), + InitialAdminEmail: loadEnvStrDef("FELU_INITIAL_ADMIN_EMAIL", "admin@example.com"), + InitialAdminPwd: loadEnvStrDef("FELU_INITIAL_ADMIN_PWD", "feluadmin"), - SOAEmail: util.LoadEnvStr("FELU_SOA_EMAIL", "admin.felu.arpa"), + LogLevel: loadEnvStrDef("FELU_LOG_LEVEL", "info"), - InitialAdminEmail: util.LoadEnvStr("FELU_INITIAL_ADMIN_EMAIL", "admin@example.com"), - InitialAdminPwd: util.LoadEnvStr("FELU_INITIAL_ADMIN_PWD", "feluadmin"), + DataDir: loadEnvStrDef("FELU_DB_PATH", "/var/felu/"), - LogLevel: util.LoadEnvStr("FELU_LOG_LEVEL", "info"), + FrontendBindAddr: loadEnvStrDef("FELU_FRONTEND_BIND_ADDR", "0.0.0.0:8080"), - DataDir: util.LoadEnvStr("FELU_DB_PATH", "/var/felu/"), + APIUrl: loadEnvStr("FELU_API_URL"), + APIBindAddr: loadEnvStrDef("FELU_API_BIND_ADDR", "0.0.0.0:8081"), - FrontendBindAddr: util.LoadEnvStr("FELU_FRONTEND_BIND_ADDR", "0.0.0.0:8080"), + DNSBindIP: loadEnvStrDef("FELU_DNS_BIND_IP", "0.0.0.0"), + DNSBindPort: loadEnvInt32Def("FELU_DNS_BIND_PORT", 53), + } +} - APIBindAddr: util.LoadEnvStr("FELU_API_BIND_ADDR", "0.0.0.0:8081"), +func loadEnvStr(key string) string { + value, found := os.LookupEnv(key) + if found { + return value + } + panic(fmt.Sprintf("Env var '%s' not found", key)) +} + +func loadEnvStrDef(key string, def string) string { + value, found := os.LookupEnv(key) + if found { + return value + } + slog.Warn("Env var not set, using default", slog.String("key", key), slog.String("def", def)) + return def +} - DNSBindIP: util.LoadEnvStr("FELU_DNS_BIND_IP", "0.0.0.0"), - DNSBindPort: util.LoadEnvInt32("FELU_DNS_BIND_PORT", 53), - DNSPattern: util.LoadEnvStr("FELU_DNS_PATTERN", "."), +func loadEnvInt32Def(key string, def int32) int32 { + value, found := os.LookupEnv(key) + if found { + res, err := strconv.ParseInt(value, 10, 32) + if err != nil { + panic(fmt.Sprintf("Couldn't parse env var '%s' into an integer", key)) + } + return int32(res) } + slog.Warn("Env var not set, using default", slog.String("key", key), slog.Int("def", int(def))) + return def } diff --git a/internal/dns/query.go b/internal/dns/query.go index 9c9579f..f3e0420 100644 --- a/internal/dns/query.go +++ b/internal/dns/query.go @@ -65,20 +65,22 @@ func handleARecord(q *dns.Question, m *dns.Msg, r *dns.Msg) { } func handleNSRecord(q *dns.Question, m *dns.Msg, r *dns.Msg) { + qName := strings.ToLower(q.Name) + ns := &dns.NS{ Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: 86400}, - Ns: dns.Fqdn(config.FeluConfig.DDNSDomain), + Ns: config.FeluConfig.Domain, } - // "Root" DDNS domain NS. - if q.Name == dns.Fqdn(config.FeluConfig.DDNSDomain) { + // "Root" Domain NS. + if qName == config.FeluConfig.Domain { m.Answer = append(m.Answer, ns) return } - if index := strings.IndexByte(q.Name, '.'); index >= 0 { + if index := strings.IndexByte(qName, '.'); index >= 0 { // FIXME: other way of checking that the domain exists - _, err := db.FetchDomainARecord(strings.ToLower(q.Name[:index])) + _, err := db.FetchDomainARecord(qName[:index]) if err != nil { m.SetRcode(r, dns.RcodeNameError) } else { @@ -90,10 +92,12 @@ func handleNSRecord(q *dns.Question, m *dns.Msg, r *dns.Msg) { } func handleSOARecord(q *dns.Question, m *dns.Msg, r *dns.Msg) { + qName := strings.ToLower(q.Name) + soa := &dns.SOA{ Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 0}, - Ns: dns.Fqdn(config.FeluConfig.DDNSDomain), - Mbox: dns.Fqdn(config.FeluConfig.SOAEmail), + Ns: config.FeluConfig.Domain, + Mbox: config.FeluConfig.SOAEmail, Serial: 2024100301, Refresh: 86400, Retry: 7200, @@ -101,15 +105,15 @@ func handleSOARecord(q *dns.Question, m *dns.Msg, r *dns.Msg) { Minttl: 172800, } - // "Root" DDNS domain SOA. - if q.Name == dns.Fqdn(config.FeluConfig.DDNSDomain) { + // "Root" Domain SOA. + if qName == config.FeluConfig.Domain { m.Answer = append(m.Answer, soa) return } - if index := strings.IndexByte(q.Name, '.'); index >= 0 { + if index := strings.IndexByte(qName, '.'); index >= 0 { // FIXME: other way of checking that the domain exists - _, err := db.FetchDomainARecord(strings.ToLower(q.Name[:index])) + _, err := db.FetchDomainARecord(qName[:index]) if err != nil { m.SetRcode(r, dns.RcodeNameError) } else { diff --git a/internal/dns/server.go b/internal/dns/server.go index ea554f9..79bd295 100644 --- a/internal/dns/server.go +++ b/internal/dns/server.go @@ -15,7 +15,7 @@ import ( // Run starts the DNS server. func Run(addr string, net string) error { - dns.HandleFunc(config.FeluConfig.DNSPattern, handleDNSRequest) + dns.HandleFunc(config.FeluConfig.Domain, handleDNSRequest) server := &dns.Server{ Addr: addr, -- 2.44.1