mirror of
https://github.com/tailscale/tailscale.git
synced 2026-10-23 03:37:19 +09:00
ipn/ipnlocal,net/dns/resolver: prevent bare name resolution when MagicDNS is disabled
nodeBackend.nodeByName should always contain both FQDNs and short names, as it is used in different contexts, including UserDial DNS resolution, which should be able to resolve unqualified DNS names regardless of the MagicDNS state. However, net/dns/resolver.Resolver and, by extension, MagicDNSHosts implementations should only resolve fully qualified domain names, skipping short names when MagicDNS is disabled for the tailnet. This fixes it in (*nodeBackend).nodeByFQDNLocked, which is only used in the MagicDNS paths, and updates the tests. Fixes #20789 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
1 parent
25877455e7
commit
1ec348784b
4 files changed
+32
-2
No files matched your search
@@ -1282,12 +1282,23 @@ func (nb *nodeBackend) magicDNSSubdomainHost(fqdn dnsname.FQDN) bool {
|
||||
}
|
||||
|
||||
// nodeByFQDNLocked returns the node (peer or self) with the given
|
||||
// MagicDNS FQDN. nb.mu must be held.
|
||||
// MagicDNS FQDN. If fqdn is a short name (has no suffix),
|
||||
// it is resolved only if MagicDNS is enabled.
|
||||
// nb.mu must be held.
|
||||
func (nb *nodeBackend) nodeByFQDNLocked(fqdn dnsname.FQDN) (_ tailcfg.NodeView, ok bool) {
|
||||
nm := nb.netMap
|
||||
if nm == nil {
|
||||
return tailcfg.NodeView{}, false
|
||||
}
|
||||
// The resolver already lowercases query names, but lowercase
|
||||
// again (nearly free when already lowercase) so that no other
|
||||
// caller of the [resolver.MagicDNSHosts] hook can miss on case.
|
||||
nid, ok := nb.nodeByName[strings.ToLower(strings.TrimSuffix(string(fqdn), "."))]
|
||||
canon := strings.ToLower(strings.TrimSuffix(string(fqdn), "."))
|
||||
// Don't resolve bare hostnames (e.g. "foo") if MagicDNS is disabled.
|
||||
if !nm.DNS.Proxied && !strings.Contains(canon, ".") {
|
||||
return tailcfg.NodeView{}, false
|
||||
}
|
||||
nid, ok := nb.nodeByName[canon]
|
||||
if !ok {
|
||||
return tailcfg.NodeView{}, false
|
||||
}
|
||||
|
||||
@@ -570,6 +570,11 @@ func TestNodeBackendRouteManagerExtras(t *testing.T) {
|
||||
// lookups must serve from the node indexes and stay correct across
|
||||
// netmap deltas without any full Hosts map rebuild.
|
||||
func TestNodeBackendMagicDNSHosts(t *testing.T) {
|
||||
t.Run("MagicDNS-enabled", func(t *testing.T) { testNodeBackendMagicDNSHosts(t, true) })
|
||||
t.Run("MagicDNS-disabled", func(t *testing.T) { testNodeBackendMagicDNSHosts(t, false) })
|
||||
}
|
||||
|
||||
func testNodeBackendMagicDNSHosts(t *testing.T, magicDNSEnabled bool) {
|
||||
nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
|
||||
|
||||
self := &tailcfg.Node{
|
||||
@@ -590,6 +595,7 @@ func TestNodeBackendMagicDNSHosts(t *testing.T) {
|
||||
nb.SetNetMap(&netmap.NetworkMap{
|
||||
SelfNode: self.View(),
|
||||
Peers: []tailcfg.NodeView{p1.View()},
|
||||
DNS: tailcfg.DNSConfig{Proxied: magicDNSEnabled},
|
||||
})
|
||||
|
||||
wantHost := func(fqdn dnsname.FQDN, want ...netip.Addr) {
|
||||
@@ -612,6 +618,14 @@ func TestNodeBackendMagicDNSHosts(t *testing.T) {
|
||||
wantHost("self.example.ts.net.", netip.MustParseAddr("100.64.0.1"))
|
||||
wantHost("unknown.example.ts.net.")
|
||||
|
||||
// Short names are only resolved if MagicDNS is enabled.
|
||||
// Otherwise, the resolver should not serve any short names.
|
||||
var shortNameAddr []netip.Addr
|
||||
if magicDNSEnabled {
|
||||
shortNameAddr = []netip.Addr{netip.MustParseAddr("100.64.0.2")}
|
||||
}
|
||||
wantHost("p1.", shortNameAddr...)
|
||||
|
||||
if fqdn, ok := nb.magicDNSPTR(netip.MustParseAddr("100.64.0.2")); !ok || fqdn != "p1.example.ts.net." {
|
||||
t.Errorf("magicDNSPTR(100.64.0.2) = %q, %v; want p1's name", fqdn, ok)
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ func (b *LocalBackend) resolveMagicDNS(hostname, network string) (_ netip.Addr,
|
||||
// pushing a Hosts map of every node into it on every netmap change.
|
||||
// It is installed once at LocalBackend construction; going through
|
||||
// currentNode makes profile switches take effect immediately.
|
||||
// Short names ("foo") are only resolved if MagicDNS is enabled
|
||||
// for the current tailnet.
|
||||
type magicDNSHosts struct{ b *LocalBackend }
|
||||
|
||||
func (m magicDNSHosts) LookupHost(fqdn dnsname.FQDN) (ips []netip.Addr, ok bool) {
|
||||
|
||||
@@ -264,6 +264,9 @@ type Resolver struct {
|
||||
// methods are called on the DNS query serving path. Name lookups are
|
||||
// case-insensitive: the resolver passes lowercase names, but
|
||||
// implementations must not rely on that.
|
||||
//
|
||||
// Short names ("foo") are only resolved if MagicDNS is enabled
|
||||
// for the current tailnet.
|
||||
type MagicDNSHosts interface {
|
||||
// LookupHost returns the IPs to answer for the node with the
|
||||
// given MagicDNS FQDN, and whether the name is known. It returns
|
||||
|
||||
Reference in new issue
Block a user