package config import ( "fmt" "os" "gopkg.in/yaml.v3" ) type Config struct { Server Server `yaml:"server"` Database Database `yaml:"database"` Artifacts Artifacts `yaml:"artifacts"` Logs Logs `yaml:"logs"` Dispatcher Dispatcher `yaml:"dispatcher"` Janitor Janitor `yaml:"janitor"` PXE PXE `yaml:"pxe"` Network Network `yaml:"network"` Agent Agent `yaml:"agent"` Notifiers []Notifier `yaml:"notifiers"` Routes []Route `yaml:"routes"` } type Server struct { Bind string `yaml:"bind"` PublicURL string `yaml:"public_url"` // user-visible base URL, e.g. https://vetting.lan:8443; used in notification click-throughs TLS TLS `yaml:"tls"` } type TLS struct { Enabled bool `yaml:"enabled"` CertFile string `yaml:"cert_file"` KeyFile string `yaml:"key_file"` } type Database struct { Path string `yaml:"path"` } type Artifacts struct { Dir string `yaml:"dir"` RetentionDays int `yaml:"retention_days"` // 0 = keep forever } type Logs struct { Dir string `yaml:"dir"` RetentionDays int `yaml:"retention_days"` // 0 = keep forever } type Janitor struct { IntervalMinutes int `yaml:"interval_minutes"` // 0 = 60 } type Dispatcher struct { MaxConcurrentRuns int `yaml:"max_concurrent_runs"` } type Network struct { IperfPort int `yaml:"iperf_port"` } // PXE / Notifier / Route are declared up front so the config file is // forward-compatible across phases. Phase 1 does not act on these. type PXE struct { Enabled bool `yaml:"enabled"` Interface string `yaml:"interface"` DHCPRange string `yaml:"dhcp_range"` OrchestratorURL string `yaml:"orchestrator_url"` TFTPRoot string `yaml:"tftp_root"` // holds ipxe.efi + undionly.kpxe LiveDir string `yaml:"live_dir"` // holds vmlinuz + initrd.img; served at /live } // Agent holds settings related to the host-mode vetting-agent binary // that operators install on their hosts. AssetDir is served at // /assets/*, which is where the quick-register script downloads // `vetting-agent-linux-amd64` from. type Agent struct { AssetDir string `yaml:"asset_dir"` // directory containing vetting-agent-linux-amd64; "" disables /assets } type Notifier struct { Name string `yaml:"name"` Type string `yaml:"type"` Topic string `yaml:"topic,omitempty"` Server string `yaml:"server,omitempty"` WebhookURL string `yaml:"webhook_url,omitempty"` SMTP SMTP `yaml:"smtp,omitempty"` } type SMTP struct { Host string `yaml:"host,omitempty"` Port int `yaml:"port,omitempty"` From string `yaml:"from,omitempty"` To []string `yaml:"to,omitempty"` } type Route struct { MatchKind []string `yaml:"match_kind"` MatchSeverity []string `yaml:"match_severity,omitempty"` Notifier string `yaml:"notifier"` } func Load(path string) (*Config, error) { b, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read config: %w", err) } var c Config if err := yaml.Unmarshal(b, &c); err != nil { return nil, fmt.Errorf("parse config: %w", err) } if c.Server.Bind == "" { c.Server.Bind = "127.0.0.1:8080" } if c.Database.Path == "" { c.Database.Path = "./var/vetting.db" } if c.Artifacts.Dir == "" { c.Artifacts.Dir = "./var/artifacts" } if c.Logs.Dir == "" { c.Logs.Dir = "./var/logs" } if c.Dispatcher.MaxConcurrentRuns == 0 { c.Dispatcher.MaxConcurrentRuns = 3 } return &c, nil }