mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-16 03:31:39 +00:00
appc,ipn/ipnlocal: add App Connector domain configuration from mapcap
The AppConnector is now configured by the mapcap from the control plane. Updates tailscale/corp#15437 Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
parent
e9de59a315
commit
6ad54fed00
@ -11,8 +11,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
xmaps "golang.org/x/exp/maps"
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
|
"tailscale.com/types/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -70,6 +72,15 @@ func (e *EmbeddedAppConnector) UpdateDomains(domains []string) {
|
|||||||
d = strings.ToLower(d)
|
d = strings.ToLower(d)
|
||||||
e.domains[d] = old[d]
|
e.domains[d] = old[d]
|
||||||
}
|
}
|
||||||
|
e.logf("handling domains: %v", xmaps.Keys(e.domains))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domains returns the currently configured domain list.
|
||||||
|
func (e *EmbeddedAppConnector) Domains() views.Slice[string] {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
|
return views.SliceOf(xmaps.Keys(e.domains))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObserveDNSResponse is a callback invoked by the DNS resolver when a DNS
|
// ObserveDNSResponse is a callback invoked by the DNS resolver when a DNS
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
func TestUpdateDomains(t *testing.T) {
|
func TestUpdateDomains(t *testing.T) {
|
||||||
a := NewEmbeddedAppConnector(t.Logf, nil)
|
a := NewEmbeddedAppConnector(t.Logf, nil)
|
||||||
a.UpdateDomains([]string{"example.com"})
|
a.UpdateDomains([]string{"example.com"})
|
||||||
if got, want := xmaps.Keys(a.domains), []string{"example.com"}; !slices.Equal(got, want) {
|
if got, want := a.Domains().AsSlice(), []string{"example.com"}; !slices.Equal(got, want) {
|
||||||
t.Errorf("got %v; want %v", got, want)
|
t.Errorf("got %v; want %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +321,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/tstime/mono from tailscale.com/net/tstun+
|
tailscale.com/tstime/mono from tailscale.com/net/tstun+
|
||||||
tailscale.com/tstime/rate from tailscale.com/wgengine/filter+
|
tailscale.com/tstime/rate from tailscale.com/wgengine/filter+
|
||||||
tailscale.com/tsweb/varz from tailscale.com/cmd/tailscaled
|
tailscale.com/tsweb/varz from tailscale.com/cmd/tailscaled
|
||||||
tailscale.com/types/appctype from tailscale.com/appc
|
tailscale.com/types/appctype from tailscale.com/appc+
|
||||||
tailscale.com/types/dnstype from tailscale.com/ipn/ipnlocal+
|
tailscale.com/types/dnstype from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/types/empty from tailscale.com/ipn+
|
tailscale.com/types/empty from tailscale.com/ipn+
|
||||||
tailscale.com/types/flagtype from tailscale.com/cmd/tailscaled
|
tailscale.com/types/flagtype from tailscale.com/cmd/tailscaled
|
||||||
|
@ -67,6 +67,7 @@ import (
|
|||||||
"tailscale.com/tka"
|
"tailscale.com/tka"
|
||||||
"tailscale.com/tsd"
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/tstime"
|
"tailscale.com/tstime"
|
||||||
|
"tailscale.com/types/appctype"
|
||||||
"tailscale.com/types/dnstype"
|
"tailscale.com/types/dnstype"
|
||||||
"tailscale.com/types/empty"
|
"tailscale.com/types/empty"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@ -3233,6 +3234,49 @@ func (b *LocalBackend) blockEngineUpdates(block bool) {
|
|||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reconfigAppConnectorLocked updates the app connector state based on the
|
||||||
|
// current network map and preferences.
|
||||||
|
// b.mu must be held.
|
||||||
|
func (b *LocalBackend) reconfigAppConnectorLocked(nm *netmap.NetworkMap, prefs ipn.PrefsView) {
|
||||||
|
const appConnectorCapName = "tailscale.com/app-connectors"
|
||||||
|
|
||||||
|
if !prefs.AppConnector().Advertise {
|
||||||
|
b.appConnector = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.appConnector == nil {
|
||||||
|
b.appConnector = appc.NewEmbeddedAppConnector(b.logf, b)
|
||||||
|
}
|
||||||
|
if nm == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(raggi): rework the view infrastructure so the large deep clone is no
|
||||||
|
// longer required
|
||||||
|
sn := nm.SelfNode.AsStruct()
|
||||||
|
attrs, err := tailcfg.UnmarshalNodeCapJSON[appctype.AppConnectorAttr](sn.CapMap, appConnectorCapName)
|
||||||
|
if err != nil {
|
||||||
|
b.logf("[unexpected] error parsing app connector mapcap: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var domains []string
|
||||||
|
for _, attr := range attrs {
|
||||||
|
// Geometric cost, assumes that the number of advertised tags is small
|
||||||
|
if !nm.SelfNode.Tags().ContainsFunc(func(tag string) bool {
|
||||||
|
return slices.Contains(attr.Connectors, tag)
|
||||||
|
}) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
domains = append(domains, attr.Domains...)
|
||||||
|
}
|
||||||
|
slices.Sort(domains)
|
||||||
|
slices.Compact(domains)
|
||||||
|
b.appConnector.UpdateDomains(domains)
|
||||||
|
}
|
||||||
|
|
||||||
// authReconfig pushes a new configuration into wgengine, if engine
|
// authReconfig pushes a new configuration into wgengine, if engine
|
||||||
// updates are not currently blocked, based on the cached netmap and
|
// updates are not currently blocked, based on the cached netmap and
|
||||||
// user prefs.
|
// user prefs.
|
||||||
@ -3246,9 +3290,7 @@ func (b *LocalBackend) authReconfig() {
|
|||||||
dohURL, dohURLOK := exitNodeCanProxyDNS(nm, b.peers, prefs.ExitNodeID())
|
dohURL, dohURLOK := exitNodeCanProxyDNS(nm, b.peers, prefs.ExitNodeID())
|
||||||
dcfg := dnsConfigForNetmap(nm, b.peers, prefs, b.logf, version.OS())
|
dcfg := dnsConfigForNetmap(nm, b.peers, prefs, b.logf, version.OS())
|
||||||
// If the current node is an app connector, ensure the app connector machine is started
|
// If the current node is an app connector, ensure the app connector machine is started
|
||||||
if prefs.AppConnector().Advertise && b.appConnector == nil {
|
b.reconfigAppConnectorLocked(nm, prefs)
|
||||||
b.appConnector = appc.NewEmbeddedAppConnector(b.logf, b)
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
|
@ -1187,6 +1187,49 @@ func TestObserveDNSResponse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReconfigureAppConnector(t *testing.T) {
|
||||||
|
b := newTestBackend(t)
|
||||||
|
b.reconfigAppConnectorLocked(b.netMap, b.pm.prefs)
|
||||||
|
if b.appConnector != nil {
|
||||||
|
t.Fatal("unexpected app connector")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.EditPrefs(&ipn.MaskedPrefs{
|
||||||
|
Prefs: ipn.Prefs{
|
||||||
|
AppConnector: ipn.AppConnectorPrefs{
|
||||||
|
Advertise: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AppConnectorSet: true,
|
||||||
|
})
|
||||||
|
b.reconfigAppConnectorLocked(b.netMap, b.pm.prefs)
|
||||||
|
if b.appConnector == nil {
|
||||||
|
t.Fatal("expected app connector")
|
||||||
|
}
|
||||||
|
|
||||||
|
appCfg := `{
|
||||||
|
"name": "example",
|
||||||
|
"domains": ["example.com"],
|
||||||
|
"connectors": ["tag:example"]
|
||||||
|
}`
|
||||||
|
|
||||||
|
b.netMap.SelfNode = (&tailcfg.Node{
|
||||||
|
Name: "example.ts.net",
|
||||||
|
Tags: []string{"tag:example"},
|
||||||
|
CapMap: (tailcfg.NodeCapMap)(map[tailcfg.NodeCapability][]tailcfg.RawMessage{
|
||||||
|
"tailscale.com/app-connectors": {tailcfg.RawMessage(appCfg)},
|
||||||
|
}),
|
||||||
|
}).View()
|
||||||
|
|
||||||
|
b.reconfigAppConnectorLocked(b.netMap, b.pm.prefs)
|
||||||
|
|
||||||
|
want := []string{"example.com"}
|
||||||
|
if !slices.Equal(b.appConnector.Domains().AsSlice(), want) {
|
||||||
|
t.Fatalf("got domains %v, want %v", b.appConnector.Domains(), want)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func resolversEqual(t *testing.T, a, b []*dnstype.Resolver) bool {
|
func resolversEqual(t *testing.T, a, b []*dnstype.Resolver) bool {
|
||||||
if a == nil && b == nil {
|
if a == nil && b == nil {
|
||||||
return true
|
return true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user