net/dns/resolver: skip DNS health warning when doing split DNS (#19959)
When MagicDNS is enabled but no global upstream resolvers are configured, the forwarder only handles specific suffixes and defers other names to the system resolver. A query it has no resolver for is expected in that case, so don't raise the dns-forward-failing warning unless a default "." route makes Tailscale the default resolver. Fixes #19931 Signed-off-by: Brendan Creane <bcreane@gmail.com>
This commit is contained in:
@@ -1199,10 +1199,11 @@ func (f *forwarder) forwardWithDestChan(ctx context.Context, query packet, respo
|
|||||||
if len(resolvers) == 0 {
|
if len(resolvers) == 0 {
|
||||||
resolvers = f.resolvers(domain)
|
resolvers = f.resolvers(domain)
|
||||||
if len(resolvers) == 0 {
|
if len(resolvers) == 0 {
|
||||||
|
// No upstream resolver for this name isn't a forwarder failure:
|
||||||
|
// it's split DNS / a name we weren't asked to handle. Count it
|
||||||
|
// rather than raising dnsForwarderFailing, which is reserved for
|
||||||
|
// resolvers we found but couldn't reach. See tailscale/tailscale#19931.
|
||||||
metricDNSFwdErrorNoUpstream.Add(1)
|
metricDNSFwdErrorNoUpstream.Add(1)
|
||||||
if f.acceptDNS {
|
|
||||||
f.health.SetUnhealthy(dnsForwarderFailing, health.Args{health.ArgDNSServers: ""})
|
|
||||||
}
|
|
||||||
f.logf("no upstream resolvers set, returning SERVFAIL")
|
f.logf("no upstream resolvers set, returning SERVFAIL")
|
||||||
|
|
||||||
res, err := servfailResponse(query)
|
res, err := servfailResponse(query)
|
||||||
|
|||||||
@@ -1387,6 +1387,52 @@ func TestForwarderHealthOnContextExpiry(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestForwarderHealthNoUpstreamResolvers verifies that a query with no upstream
|
||||||
|
// resolver never raises dnsForwarderFailing, regardless of acceptDNS; that
|
||||||
|
// warning is reserved for resolvers we found but couldn't reach (see
|
||||||
|
// TestForwarderHealthOnContextExpiry). See tailscale/tailscale#19931.
|
||||||
|
func TestForwarderHealthNoUpstreamResolvers(t *testing.T) {
|
||||||
|
const domain = "no-resolver.example.com."
|
||||||
|
|
||||||
|
for _, acceptDNS := range []bool{true, false} {
|
||||||
|
t.Run(fmt.Sprintf("acceptDNS=%v", acceptDNS), func(t *testing.T) {
|
||||||
|
request := makeTestRequest(t, domain, dns.TypeA, 0)
|
||||||
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
|
bus := eventbustest.NewBus(t)
|
||||||
|
netMon, err := netmon.New(bus, logf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dialer tsdial.Dialer
|
||||||
|
dialer.SetNetMon(netMon)
|
||||||
|
dialer.SetBus(bus)
|
||||||
|
|
||||||
|
ht := health.NewTracker(bus)
|
||||||
|
fwd := newForwarder(logf, netMon, nil, &dialer, ht, nil)
|
||||||
|
fwd.acceptDNS = acceptDNS
|
||||||
|
// No routes are configured, so the forwarder has no upstream
|
||||||
|
// resolver for the query and returns SERVFAIL.
|
||||||
|
|
||||||
|
rpkt := packet{
|
||||||
|
bs: request,
|
||||||
|
family: "udp",
|
||||||
|
addr: netip.MustParseAddrPort("127.0.0.1:12345"),
|
||||||
|
}
|
||||||
|
// Buffered so the SERVFAIL response can be sent without a reader.
|
||||||
|
responseChan := make(chan packet, 1)
|
||||||
|
|
||||||
|
if err := fwd.forwardWithDestChan(context.Background(), rpkt, responseChan); err != nil {
|
||||||
|
t.Fatalf("forwardWithDestChan: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ht.IsUnhealthy(dnsForwarderFailing) {
|
||||||
|
t.Error("dnsForwarderFailing was raised for a query with no upstream resolver; want healthy")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestResolversCustomScheme(t *testing.T) {
|
func TestResolversCustomScheme(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user