Generate RFC 9432 DNS catalog zone files from a declarative input file. Parses zone-to-catalog assignments with optional group and coo properties, produces deterministic BIND-format output with automatic SOA serial management and change detection.
79 lines
1.7 KiB
Go
79 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// CatalogConfig holds the configuration for a single catalog zone.
|
|
type CatalogConfig struct {
|
|
Zone string `yaml:"zone"`
|
|
}
|
|
|
|
// SOAConfig holds the configurable SOA fields.
|
|
type SOAConfig struct {
|
|
Mname string `yaml:"mname"`
|
|
Rname string `yaml:"rname"`
|
|
}
|
|
|
|
// Config holds the parsed catz.yaml configuration.
|
|
type Config struct {
|
|
Catalogs map[string]CatalogConfig `yaml:"catalogs"`
|
|
SOA SOAConfig `yaml:"soa"`
|
|
}
|
|
|
|
func loadConfig(path string) (*Config, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading config: %w", err)
|
|
}
|
|
|
|
var cfg Config
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parsing config: %w", err)
|
|
}
|
|
|
|
if err := validateConfig(&cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Normalize zone FQDNs
|
|
for name, cat := range cfg.Catalogs {
|
|
cat.Zone = normalizeFQDN(cat.Zone)
|
|
cfg.Catalogs[name] = cat
|
|
}
|
|
cfg.SOA.Mname = normalizeFQDN(cfg.SOA.Mname)
|
|
cfg.SOA.Rname = normalizeFQDN(cfg.SOA.Rname)
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
func validateConfig(cfg *Config) error {
|
|
if len(cfg.Catalogs) == 0 {
|
|
return fmt.Errorf("config: no catalogs defined")
|
|
}
|
|
for name, cat := range cfg.Catalogs {
|
|
if cat.Zone == "" {
|
|
return fmt.Errorf("config: catalog %q has no zone defined", name)
|
|
}
|
|
}
|
|
if cfg.SOA.Mname == "" {
|
|
return fmt.Errorf("config: soa.mname is required")
|
|
}
|
|
if cfg.SOA.Rname == "" {
|
|
return fmt.Errorf("config: soa.rname is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func normalizeFQDN(name string) string {
|
|
name = strings.ToLower(strings.TrimSpace(name))
|
|
if name != "" && !strings.HasSuffix(name, ".") {
|
|
name += "."
|
|
}
|
|
return name
|
|
}
|