pxe: move dhcp-host allowlist into a SIGHUP-reloadable file
dnsmasq's SIGHUP re-reads /etc/ethers and any --dhcp-hostsfile= paths,
but NOT dhcp-host= lines from the main conf. Reload() was faithfully
rewriting dnsmasq.conf with the new MAC, sending SIGHUP, and then
dnsmasq kept serving its startup view — so a freshly-registered host
still showed up as "proxy-ignored, tags: eth0" with no "known" tag.
Split the allowlist into ${RuntimeDir}/dhcp-hosts, referenced from the
main conf via dhcp-hostsfile=. writeConf() is static-ish now; Reload
just rewrites the hosts file and SIGHUPs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+41
-12
@@ -120,7 +120,10 @@ func (s *Supervisor) Start(ctx context.Context, hosts []model.Host) error {
|
||||
if err := os.MkdirAll(s.cfg.RuntimeDir, 0o755); err != nil {
|
||||
return fmt.Errorf("mkdir runtime: %w", err)
|
||||
}
|
||||
if err := s.writeConf(hosts); err != nil {
|
||||
if err := s.writeHosts(hosts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.writeConf(); err != nil {
|
||||
return err
|
||||
}
|
||||
subCtx, cancel := context.WithCancel(ctx)
|
||||
@@ -152,14 +155,14 @@ func (s *Supervisor) Start(ctx context.Context, hosts []model.Host) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reload rewrites the conf with the latest host registry and sends
|
||||
// SIGHUP. It will restart the subprocess if SIGHUP is unsupported
|
||||
// (e.g. when running behind an OS that doesn't support it).
|
||||
// Reload rewrites the dhcp-hosts allowlist with the latest host
|
||||
// registry and SIGHUPs dnsmasq to pick it up. The main dnsmasq.conf
|
||||
// is unchanged — it only references the hosts file by path.
|
||||
func (s *Supervisor) Reload(hosts []model.Host) error {
|
||||
if !s.cfg.Enabled {
|
||||
return nil
|
||||
}
|
||||
if err := s.writeConf(hosts); err != nil {
|
||||
if err := s.writeHosts(hosts); err != nil {
|
||||
return err
|
||||
}
|
||||
s.mu.Lock()
|
||||
@@ -201,7 +204,7 @@ func (s *Supervisor) Shutdown(timeout time.Duration) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Supervisor) writeConf(hosts []model.Host) error {
|
||||
func (s *Supervisor) writeConf() error {
|
||||
tmpl, err := template.New("dnsmasq").Parse(dnsmasqTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -219,10 +222,9 @@ func (s *Supervisor) writeConf(hosts []model.Host) error {
|
||||
}
|
||||
data := struct {
|
||||
Cfg SupervisorConfig
|
||||
Hosts []model.Host
|
||||
Network string
|
||||
Netmask string
|
||||
}{s.cfg, hosts, ipnet.IP.String(), net.IP(ipnet.Mask).String()}
|
||||
}{s.cfg, ipnet.IP.String(), net.IP(ipnet.Mask).String()}
|
||||
if err := tmpl.Execute(f, data); err != nil {
|
||||
_ = f.Close()
|
||||
return fmt.Errorf("render conf: %w", err)
|
||||
@@ -240,6 +242,32 @@ func (s *Supervisor) writeConf(hosts []model.Host) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeHosts renders the dhcp-hostsfile referenced by dnsmasq.conf.
|
||||
// Each registered host contributes one line:
|
||||
//
|
||||
// <mac>,set:known
|
||||
//
|
||||
// dnsmasq re-reads this file on SIGHUP — that's the whole point of
|
||||
// keeping it separate from the main conf.
|
||||
func (s *Supervisor) writeHosts(hosts []model.Host) error {
|
||||
if err := os.MkdirAll(s.cfg.RuntimeDir, 0o755); err != nil {
|
||||
return fmt.Errorf("mkdir runtime: %w", err)
|
||||
}
|
||||
path := filepath.Join(s.cfg.RuntimeDir, "dhcp-hosts")
|
||||
tmp := path + ".new"
|
||||
var b strings.Builder
|
||||
for _, h := range hosts {
|
||||
fmt.Fprintf(&b, "%s,set:known\n", h.MAC)
|
||||
}
|
||||
if err := os.WriteFile(tmp, []byte(b.String()), 0o644); err != nil {
|
||||
return fmt.Errorf("write dhcp-hosts: %w", err)
|
||||
}
|
||||
if err := os.Rename(tmp, path); err != nil {
|
||||
return fmt.Errorf("rename dhcp-hosts: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exposed for the UI handlers to show operators what config is live.
|
||||
func (s *Supervisor) ConfPath() string {
|
||||
return filepath.Join(s.cfg.RuntimeDir, "dnsmasq.conf")
|
||||
@@ -275,11 +303,12 @@ no-resolv
|
||||
# CIDR — we split Subnet upstream in writeConf().
|
||||
dhcp-range={{ .Network }},proxy,{{ .Netmask }}
|
||||
|
||||
# MAC allowlist: dnsmasq only answers DHCP for MACs with a dhcp-host= below.
|
||||
# MAC allowlist: dnsmasq only answers DHCP for MACs tagged "known".
|
||||
# The per-MAC dhcp-host= entries live in a separate file so SIGHUP
|
||||
# can reload them — dnsmasq does NOT re-read dhcp-host= from the
|
||||
# main conf on SIGHUP, only from dhcp-hostsfile=.
|
||||
dhcp-ignore=tag:!known
|
||||
{{- range .Hosts }}
|
||||
dhcp-host={{ .MAC }},set:known
|
||||
{{- end }}
|
||||
dhcp-hostsfile={{ .Cfg.RuntimeDir }}/dhcp-hosts
|
||||
|
||||
# Keep runtime state inside RuntimeDir so the systemd sandbox
|
||||
# (ReadWritePaths=/var/lib/vetting ...) doesn't block writes to the
|
||||
|
||||
Reference in New Issue
Block a user