tailcfg, etc: plumb OnDemandDomains from the server

This lets the server tell macOS/iOS that tailscale should be started
to handle any "ts.net" domains.

Updates #1534

Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
This commit is contained in:
David Crawshaw 2023-01-28 16:44:53 -08:00
parent e51cf1b09d
commit a795ea8cee
7 changed files with 63 additions and 3 deletions

View File

@ -305,6 +305,22 @@ func TestDNSConfigForNetmap(t *testing.T) {
Routes: map[dnsname.FQDN][]*dnstype.Resolver{}, Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
}, },
}, },
{
name: "on_demand_domains",
nm: &netmap.NetworkMap{
DNS: tailcfg.DNSConfig{
OnDemandDomains: []string{"ts.net"},
},
},
prefs: &ipn.Prefs{
CorpDNS: true,
},
want: &dns.Config{
Hosts: map[dnsname.FQDN][]netip.Addr{},
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
OnDemandDomains: []string{"ts.net"},
},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View File

@ -2965,8 +2965,9 @@ func shouldUseOneCGNATRoute(nm *netmap.NetworkMap, logf logger.Logf, versionOS s
// a runtime.GOOS. // a runtime.GOOS.
func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs ipn.PrefsView, logf logger.Logf, versionOS string) *dns.Config { func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs ipn.PrefsView, logf logger.Logf, versionOS string) *dns.Config {
dcfg := &dns.Config{ dcfg := &dns.Config{
Routes: map[dnsname.FQDN][]*dnstype.Resolver{}, Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
Hosts: map[dnsname.FQDN][]netip.Addr{}, Hosts: map[dnsname.FQDN][]netip.Addr{},
OnDemandDomains: append([]string(nil), nm.DNS.OnDemandDomains...),
} }
// selfV6Only is whether we only have IPv6 addresses ourselves. // selfV6Only is whether we only have IPv6 addresses ourselves.

View File

@ -44,6 +44,14 @@ type Config struct {
// OnlyIPv6, if true, uses the IPv6 service IP (for MagicDNS) // OnlyIPv6, if true, uses the IPv6 service IP (for MagicDNS)
// instead of the IPv4 version (100.100.100.100). // instead of the IPv4 version (100.100.100.100).
OnlyIPv6 bool OnlyIPv6 bool
// OnDemandDomains are the set of domain names for which the OS
// should enable the tailscale client, if it is not already running.
//
// This is plumbed through to the onDemand rules of
// NETunnelProviderManager on macOS/iOS.
//
// The typical OnDemandDomains is ["ts.net"].
OnDemandDomains []string `json:",omitempty"`
} }
func (c *Config) serviceIP() netip.Addr { func (c *Config) serviceIP() netip.Addr {

View File

@ -63,10 +63,18 @@ type OSConfig struct {
// from the OS, which will only work with OSConfigurators that // from the OS, which will only work with OSConfigurators that
// report SupportsSplitDNS()=true. // report SupportsSplitDNS()=true.
MatchDomains []dnsname.FQDN MatchDomains []dnsname.FQDN
// OnDemandDomains are the set of domain names for which the OS
// should enable the tailscale client, if it is not already running.
//
// This is plumbed through to the onDemand rules of
// NETunnelProviderManager on macOS/iOS.
//
// The typical OnDemandDomains is ["ts.net"].
OnDemandDomains []string
} }
func (o OSConfig) IsZero() bool { func (o OSConfig) IsZero() bool {
return len(o.Nameservers) == 0 && len(o.SearchDomains) == 0 && len(o.MatchDomains) == 0 return len(o.Nameservers) == 0 && len(o.SearchDomains) == 0 && len(o.MatchDomains) == 0 && len(o.OnDemandDomains) == 0
} }
func (a OSConfig) Equal(b OSConfig) bool { func (a OSConfig) Equal(b OSConfig) bool {
@ -95,6 +103,11 @@ func (a OSConfig) Equal(b OSConfig) bool {
return false return false
} }
} }
for i := range a.OnDemandDomains {
if a.OnDemandDomains[i] != b.OnDemandDomains[i] {
return false
}
}
return true return true
} }
@ -126,6 +139,13 @@ func (a OSConfig) Format(f fmt.State, verb rune) {
} }
fmt.Fprintf(w, "%+v", domain) fmt.Fprintf(w, "%+v", domain)
} }
w.WriteString(`] OnDemandDomains:[`)
for i, domain := range a.OnDemandDomains {
if i != 0 {
w.WriteString(" ")
}
fmt.Fprintf(w, "%+v", domain)
}
w.WriteString(`] Hosts:[`) w.WriteString(`] Hosts:[`)
for i, host := range a.Hosts { for i, host := range a.Hosts {
if i != 0 { if i != 0 {

View File

@ -1190,6 +1190,15 @@ type DNSConfig struct {
// //
// Matches are case insensitive. // Matches are case insensitive.
ExitNodeFilteredSet []string ExitNodeFilteredSet []string
// OnDemandDomains are the set of domain names for which the OS
// should enable the tailscale client, if it is not already running.
//
// This is plumbed through to the onDemand rules of
// NETunnelProviderManager on macOS/iOS.
//
// The typical OnDemandDomains is ["ts.net"].
OnDemandDomains []string `json:",omitempty"`
} }
// DNSRecord is an extra DNS record to add to MagicDNS. // DNSRecord is an extra DNS record to add to MagicDNS.

View File

@ -234,6 +234,7 @@ func (src *DNSConfig) Clone() *DNSConfig {
dst.CertDomains = append(src.CertDomains[:0:0], src.CertDomains...) dst.CertDomains = append(src.CertDomains[:0:0], src.CertDomains...)
dst.ExtraRecords = append(src.ExtraRecords[:0:0], src.ExtraRecords...) dst.ExtraRecords = append(src.ExtraRecords[:0:0], src.ExtraRecords...)
dst.ExitNodeFilteredSet = append(src.ExitNodeFilteredSet[:0:0], src.ExitNodeFilteredSet...) dst.ExitNodeFilteredSet = append(src.ExitNodeFilteredSet[:0:0], src.ExitNodeFilteredSet...)
dst.OnDemandDomains = append(src.OnDemandDomains[:0:0], src.OnDemandDomains...)
return dst return dst
} }
@ -248,6 +249,7 @@ func (src *DNSConfig) Clone() *DNSConfig {
CertDomains []string CertDomains []string
ExtraRecords []DNSRecord ExtraRecords []DNSRecord
ExitNodeFilteredSet []string ExitNodeFilteredSet []string
OnDemandDomains []string
}{}) }{})
// Clone makes a deep copy of RegisterResponse. // Clone makes a deep copy of RegisterResponse.

View File

@ -533,6 +533,9 @@ func (v DNSConfigView) ExtraRecords() views.Slice[DNSRecord] { return views.Slic
func (v DNSConfigView) ExitNodeFilteredSet() views.Slice[string] { func (v DNSConfigView) ExitNodeFilteredSet() views.Slice[string] {
return views.SliceOf(v.ж.ExitNodeFilteredSet) return views.SliceOf(v.ж.ExitNodeFilteredSet)
} }
func (v DNSConfigView) OnDemandDomains() views.Slice[string] {
return views.SliceOf(v.ж.OnDemandDomains)
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file. // A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _DNSConfigViewNeedsRegeneration = DNSConfig(struct { var _DNSConfigViewNeedsRegeneration = DNSConfig(struct {
@ -545,6 +548,7 @@ func (v DNSConfigView) ExitNodeFilteredSet() views.Slice[string] {
CertDomains []string CertDomains []string
ExtraRecords []DNSRecord ExtraRecords []DNSRecord
ExitNodeFilteredSet []string ExitNodeFilteredSet []string
OnDemandDomains []string
}{}) }{})
// View returns a readonly view of RegisterResponse. // View returns a readonly view of RegisterResponse.