appc,feature/conn25: use custom scheme resolvers for conn25

Currently we are picking a peer for the split dns routes when we get a
netmap. Use the new custom scheme resolvers, installed per app in the
config in the netmap, to allow us to choose which connector peer should
handle a DNS request at the time the request is made.

Fixes tailscale/corp#39858

Signed-off-by: Fran Bull <fran@tailscale.com>
This commit is contained in:
Fran Bull
2026-05-29 12:23:47 -07:00
committed by franbull
parent 5d935c8900
commit c9333854fb
6 changed files with 145 additions and 312 deletions
+45 -11
View File
@@ -147,6 +147,36 @@ func (e *extension) installHooks(dph *datapathHandler) error {
if !ok {
return errors.New("could not access system tun")
}
resolver := dnsManager.Resolver()
if resolver == nil {
return errors.New("dns manager resolver not ready")
}
if err := resolver.RegisterCustomScheme(appc.DNSAddrScheme, func(addr string) (string, error) {
scheme, appName, ok := strings.Cut(addr, ":")
if !ok || scheme != appc.DNSAddrScheme {
return "", fmt.Errorf("unexpected conn25 scheme %q", scheme)
}
if !e.conn25.isConfigured() {
return "", errors.New("conn25 not configured")
}
cfg, ok := e.conn25.getConfig()
if !ok {
return "", errors.New("conn25 no config found")
}
app, ok := cfg.appsByName[appName]
if !ok {
return "", errors.New("no app found for app name")
}
_, urlBase := e.pickConnectorURLBase(app)
if urlBase == "" {
return "", nil
}
return urlBase + "/dns-query", nil
}); err != nil {
return fmt.Errorf("could not register DNS resolver scheme: %w", err)
}
// Set up the DNS manager to rewrite responses for app domains
// to answer with Magic IPs.
@@ -925,17 +955,7 @@ func makePeerAPIReq(ctx context.Context, httpClient *http.Client, urlBase string
return nil
}
func (e *extension) sendAddressAssignment(ctx context.Context, as addrs) (tailcfg.NodeView, error) {
cfg, ok := e.conn25.getConfig()
if !ok {
return tailcfg.NodeView{}, errors.New("not configured")
}
app, ok := cfg.appsByName[as.app]
if !ok {
e.conn25.logf("App not found for app: %s (domain: %s)", as.app, as.domain)
return tailcfg.NodeView{}, errors.New("app not found")
}
func (e *extension) pickConnectorURLBase(app appctype.Conn25Attr) (tailcfg.NodeView, string) {
nb := e.host.NodeBackend()
peers := appc.PickConnector(nb, app)
var urlBase string
@@ -947,6 +967,20 @@ func (e *extension) sendAddressAssignment(ctx context.Context, as addrs) (tailcf
break
}
}
return conn, urlBase
}
func (e *extension) sendAddressAssignment(ctx context.Context, as addrs) (tailcfg.NodeView, error) {
cfg, ok := e.conn25.getConfig()
if !ok {
return tailcfg.NodeView{}, errors.New("not configured")
}
app, ok := cfg.appsByName[as.app]
if !ok {
e.conn25.client.logf("App not found for app: %s (domain: %s)", as.app, as.domain)
return tailcfg.NodeView{}, errors.New("app not found")
}
conn, urlBase := e.pickConnectorURLBase(app)
if urlBase == "" {
return tailcfg.NodeView{}, errors.New("no connector peer found to handle address assignment")
}
+11 -9
View File
@@ -21,6 +21,7 @@ import (
"tailscale.com/ipn"
"tailscale.com/ipn/ipnext"
"tailscale.com/net/dns"
"tailscale.com/net/netmon"
"tailscale.com/net/packet"
"tailscale.com/net/tsdial"
"tailscale.com/net/tstun"
@@ -32,6 +33,7 @@ import (
"tailscale.com/types/logger"
"tailscale.com/types/opt"
"tailscale.com/util/dnsname"
"tailscale.com/util/eventbus/eventbustest"
"tailscale.com/util/must"
"tailscale.com/util/set"
)
@@ -1393,14 +1395,14 @@ type testSafeBackend struct {
sys *tsd.System
}
func newTestSafeBackend() *testSafeBackend {
sb := &testSafeBackend{}
sys := &tsd.System{}
sys.Dialer.Set(&tsdial.Dialer{Logf: logger.Discard})
sys.DNSManager.Set(&dns.Manager{})
func newTestSafeBackend(t *testing.T) *testSafeBackend {
sys := tsd.NewSystemWithBus(eventbustest.NewBus(t))
dialer := tsdial.NewDialer(netmon.NewStatic())
sys.Dialer.Set(dialer)
ht := sys.HealthTracker.Get()
sys.DNSManager.Set(dns.NewManager(logger.Discard, nil, ht, dialer, nil, nil, "", sys.Bus.Get()))
sys.Tun.Set(&tstun.Wrapper{})
sb.sys = sys
return sb
return &testSafeBackend{sys: sys}
}
func (b *testSafeBackend) Sys() *tsd.System { return b.sys }
@@ -1439,7 +1441,7 @@ func TestAddressAssignmentIsHandled(t *testing.T) {
ext := &extension{
conn25: newConn25(logger.Discard),
backend: newTestSafeBackend(),
backend: newTestSafeBackend(t),
}
authReconfigAsyncCalled := make(chan struct{}, 1)
if err := ext.Init(&testHost{
@@ -2034,7 +2036,7 @@ func TestHandleAddressAssignmentStoresTransitIPs(t *testing.T) {
ext := &extension{
conn25: newConn25(logger.Discard),
backend: newTestSafeBackend(),
backend: newTestSafeBackend(t),
}
authReconfigAsyncCalled := make(chan struct{}, 1)
if err := ext.Init(&testHost{