mirror of
https://github.com/tailscale/tailscale.git
synced 2026-11-09 02:53:09 -05:00
58 lines
1.9 KiB
Go
58 lines
1.9 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package appc
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/tailcfg/nodecap"
|
|
"tailscale.com/types/appctype"
|
|
"tailscale.com/types/dnstype"
|
|
)
|
|
|
|
const AppConnectorAttrName = "tailscale.com/app-connectors"
|
|
|
|
// DNSAddrScheme is the custom URI scheme used for conn25-managed split DNS
|
|
// entries to determine the destination at query time rather than configuration
|
|
// time.
|
|
const DNSAddrScheme = "tailscale-app"
|
|
|
|
// AppDNSRoutes returns split DNS routes, keyed by domain suffix, for the app
|
|
// domains configured in self's capability map. Each route resolves via the
|
|
// [DNSAddrScheme] scheme naming the app, so the connector that serves it is
|
|
// chosen at query time. It returns nil if the node lacks Connectors 2025 apps.
|
|
func AppDNSRoutes(hasCap func(c nodecap.Cap) bool, self tailcfg.NodeView) map[string][]*dnstype.Resolver {
|
|
if !hasCap(AppConnectorAttrName) {
|
|
return nil
|
|
}
|
|
|
|
apps, err := tailcfg.UnmarshalNodeCapViewJSON[appctype.AppConnectorAttr](self.CapMap(), AppConnectorAttrName)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
appNamesByDomain := map[string]string{}
|
|
for _, app := range apps {
|
|
// conn25 apps are provided under [AppConnectorAttrName] with the Mode
|
|
// field set to [appctype.AppConnectorModeMagicIP].
|
|
if app.Mode != appctype.MagicIP {
|
|
continue
|
|
}
|
|
for _, domain := range app.Domains {
|
|
domain, _ = strings.CutPrefix(domain, "*.")
|
|
domain = strings.ToLower(domain)
|
|
// in the case of multiple apps specifying the same domain (which is misconfiguration
|
|
// that should be validated at point of input) last write wins.
|
|
appNamesByDomain[domain] = app.Name
|
|
}
|
|
}
|
|
m := make(map[string][]*dnstype.Resolver, len(appNamesByDomain))
|
|
for domain, appName := range appNamesByDomain {
|
|
m[domain] = []*dnstype.Resolver{{Addr: fmt.Sprintf("%s:%s", DNSAddrScheme, appName), UseWithExitNode: true}}
|
|
}
|
|
return m
|
|
}
|