dnsmasq: forward outside names (systemd-resolved stub or resolv.conf)
Debian's dnsmasq looks for /run/dnsmasq/resolv.conf, which only resolvconf writes; without it every name outside the cluster domain was refused, so nodes could not resolve mirrors or site services. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
77894b7d57
commit
eb245b1d51
3 files changed
+73
-1
No files matched your search
@@ -66,6 +66,9 @@ publish a tag that has no section here.
|
||||
- Release notes from `CHANGELOG.md`.
|
||||
|
||||
### Fixed
|
||||
- Rack controllers' dnsmasq had no upstream DNS on Debian (it looks for a resolvconf file that is
|
||||
usually missing) and refused every name outside the cluster domain; the generated configuration
|
||||
now forwards to systemd-resolved's stub (keeping split DNS) or the host's resolv.conf servers.
|
||||
- Virtual BMC addresses are configured in netplan on the lab PXE server (they were lost on a
|
||||
network reconfigure, which made every BMC call fail).
|
||||
- Commissioning console no longer shows a failing autologin (`noautologin`).
|
||||
|
||||
@@ -2,6 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -224,3 +226,20 @@ func TestDnsmasqIPv6(t *testing.T) {
|
||||
t.Errorf("router lifetime 0 although the rack is the gateway:\n%s", conf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDnsmasqUpstream(t *testing.T) {
|
||||
d := t.TempDir()
|
||||
stub, resolv := filepath.Join(d, "stub"), filepath.Join(d, "resolv")
|
||||
if got := dnsmasqUpstream(stub, resolv, nil); got != "" {
|
||||
t.Errorf("no files: %q", got)
|
||||
}
|
||||
_ = os.WriteFile(resolv, []byte("# x\nnameserver 127.0.0.1\nnameserver 10.213.77.1\nnameserver 10.0.0.1\nnameserver 1.1.1.1\nsearch lan\n"), 0o644)
|
||||
got := dnsmasqUpstream(stub, resolv, []string{"10.213.77.1/24"})
|
||||
if !strings.Contains(got, "no-resolv\nserver=10.0.0.1\nserver=1.1.1.1\n") || strings.Contains(got, "127.0.0.1") || strings.Contains(got, "10.213.77.1") {
|
||||
t.Errorf("resolv.conf upstream:\n%s", got)
|
||||
}
|
||||
_ = os.WriteFile(stub, []byte("nameserver 127.0.0.53\n"), 0o644)
|
||||
if got := dnsmasqUpstream(stub, resolv, nil); !strings.Contains(got, "server=127.0.0.53") {
|
||||
t.Errorf("resolved stub: %q", got)
|
||||
}
|
||||
}
|
||||
+51
-1
@@ -590,7 +590,7 @@ func applyRackState(st *rackState, tftp, dnsDir string) (changed []string, err e
|
||||
note("hosts", ch, e)
|
||||
conf := filepath.Join(dnsDir, "conf.d", "rollcall.conf")
|
||||
if st.DnsmasqConf != "" {
|
||||
ch, e = writeIfChanged(conf, st.DnsmasqConf, 0o644)
|
||||
ch, e = writeIfChanged(conf, st.DnsmasqConf+dnsmasqUpstream("/run/systemd/resolve/stub-resolv.conf", "/etc/resolv.conf", localAddrs()), 0o644)
|
||||
note("dnsmasq.conf", ch, e)
|
||||
} else if _, e := os.Stat(conf); e == nil {
|
||||
note("-dnsmasq.conf", true, os.Remove(conf))
|
||||
@@ -873,3 +873,53 @@ func (a *App) handleRackDHCP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
fmt.Fprint(w, st.DnsmasqConf, "\n# --- hosts.dhcp\n", st.HostsDHCP)
|
||||
}
|
||||
|
||||
// dnsmasqUpstream tells dnsmasq where to forward names it does not answer
|
||||
// itself. Without it Debian's dnsmasq looks for a resolvconf-made file that
|
||||
// usually does not exist and refuses every outside name, so nodes could not
|
||||
// resolve mirrors or site services. With systemd-resolved the stub is used, so
|
||||
// its per-link routing (split DNS such as ~int.example.com) keeps working;
|
||||
// otherwise the host's resolv.conf servers, never ourselves.
|
||||
func dnsmasqUpstream(stub, resolv string, own []string) string {
|
||||
if _, err := os.Stat(stub); err == nil {
|
||||
return "# upstream: systemd-resolved on this host (keeps its split DNS)\nno-resolv\nserver=127.0.0.53\n"
|
||||
}
|
||||
b, err := os.ReadFile(resolv)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
mine := map[string]bool{}
|
||||
for _, a := range own {
|
||||
mine[stripPrefix(a)] = true
|
||||
}
|
||||
var servers []string
|
||||
for _, line := range strings.Split(string(b), "\n") {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 2 || f[0] != "nameserver" {
|
||||
continue
|
||||
}
|
||||
ip, ok := parseAddr(f[1])
|
||||
if !ok || mine[ip.String()] || (ip.IsLoopback() && ip.String() != "127.0.0.53") {
|
||||
continue
|
||||
}
|
||||
servers = append(servers, ip.String())
|
||||
}
|
||||
if len(servers) == 0 {
|
||||
return ""
|
||||
}
|
||||
out := "# upstream: this host's resolv.conf\nno-resolv\n"
|
||||
for _, sv := range servers {
|
||||
out += "server=" + sv + "\n"
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// localAddrs: this host's interface addresses (CIDR form).
|
||||
func localAddrs() []string {
|
||||
var out []string
|
||||
addrs, _ := net.InterfaceAddrs()
|
||||
for _, a := range addrs {
|
||||
out = append(out, a.String())
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in new issue
Block a user