appc,ipn/ipnlocal: Add split DNS entries for conn25 peers
If conn25 config is sent in the netmap: add split DNS entries to use appropriately tagged peers' PeerAPI to resolve DNS requests for those domains. This will enable future work where we use the peers as connectors for the configured domains. Updates tailscale/corp#34252 Signed-off-by: Fran Bull <fran@tailscale.com>
This commit is contained in:
@@ -10,14 +10,17 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"tailscale.com/appc"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/net/dns"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/types/dnstype"
|
||||
"tailscale.com/types/netmap"
|
||||
"tailscale.com/types/opt"
|
||||
"tailscale.com/util/cloudenv"
|
||||
"tailscale.com/util/dnsname"
|
||||
"tailscale.com/util/set"
|
||||
)
|
||||
|
||||
func ipps(ippStrs ...string) (ipps []netip.Prefix) {
|
||||
@@ -349,6 +352,94 @@ func TestDNSConfigForNetmap(t *testing.T) {
|
||||
prefs: &ipn.Prefs{},
|
||||
want: &dns.Config{},
|
||||
},
|
||||
{
|
||||
name: "conn25-split-dns",
|
||||
nm: &netmap.NetworkMap{
|
||||
SelfNode: (&tailcfg.Node{
|
||||
Name: "a",
|
||||
Addresses: ipps("100.101.101.101"),
|
||||
CapMap: tailcfg.NodeCapMap{
|
||||
tailcfg.NodeCapability(appc.AppConnectorsExperimentalAttrName): []tailcfg.RawMessage{
|
||||
tailcfg.RawMessage(`{"name":"app1","connectors":["tag:woo"],"domains":["example.com"]}`),
|
||||
},
|
||||
},
|
||||
}).View(),
|
||||
AllCaps: set.Of(tailcfg.NodeCapability(appc.AppConnectorsExperimentalAttrName)),
|
||||
},
|
||||
peers: nodeViews([]*tailcfg.Node{
|
||||
{
|
||||
ID: 1,
|
||||
Name: "p1",
|
||||
Addresses: ipps("100.102.0.1"),
|
||||
Tags: []string{"tag:woo"},
|
||||
Hostinfo: (&tailcfg.Hostinfo{
|
||||
Services: []tailcfg.Service{
|
||||
{
|
||||
Proto: tailcfg.PeerAPI4,
|
||||
Port: 1234,
|
||||
},
|
||||
},
|
||||
AppConnector: opt.NewBool(true),
|
||||
}).View(),
|
||||
},
|
||||
}),
|
||||
prefs: &ipn.Prefs{
|
||||
CorpDNS: true,
|
||||
},
|
||||
want: &dns.Config{
|
||||
Hosts: map[dnsname.FQDN][]netip.Addr{
|
||||
"a.": ips("100.101.101.101"),
|
||||
"p1.": ips("100.102.0.1"),
|
||||
},
|
||||
Routes: map[dnsname.FQDN][]*dnstype.Resolver{
|
||||
dnsname.FQDN("example.com."): {
|
||||
{Addr: "http://100.102.0.1:1234/dns-query"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "conn25-split-dns-no-matching-peers",
|
||||
nm: &netmap.NetworkMap{
|
||||
SelfNode: (&tailcfg.Node{
|
||||
Name: "a",
|
||||
Addresses: ipps("100.101.101.101"),
|
||||
CapMap: tailcfg.NodeCapMap{
|
||||
tailcfg.NodeCapability(appc.AppConnectorsExperimentalAttrName): []tailcfg.RawMessage{
|
||||
tailcfg.RawMessage(`{"name":"app1","connectors":["tag:woo"],"domains":["example.com"]}`),
|
||||
},
|
||||
},
|
||||
}).View(),
|
||||
AllCaps: set.Of(tailcfg.NodeCapability(appc.AppConnectorsExperimentalAttrName)),
|
||||
},
|
||||
peers: nodeViews([]*tailcfg.Node{
|
||||
{
|
||||
ID: 1,
|
||||
Name: "p1",
|
||||
Addresses: ipps("100.102.0.1"),
|
||||
Tags: []string{"tag:nomatch"},
|
||||
Hostinfo: (&tailcfg.Hostinfo{
|
||||
Services: []tailcfg.Service{
|
||||
{
|
||||
Proto: tailcfg.PeerAPI4,
|
||||
Port: 1234,
|
||||
},
|
||||
},
|
||||
AppConnector: opt.NewBool(true),
|
||||
}).View(),
|
||||
},
|
||||
}),
|
||||
prefs: &ipn.Prefs{
|
||||
CorpDNS: true,
|
||||
},
|
||||
want: &dns.Config{
|
||||
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
|
||||
Hosts: map[dnsname.FQDN][]netip.Addr{
|
||||
"a.": ips("100.101.101.101"),
|
||||
"p1.": ips("100.102.0.1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -6,12 +6,14 @@ package ipnlocal
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go4.org/netipx"
|
||||
"tailscale.com/appc"
|
||||
"tailscale.com/feature/buildfeatures"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/net/dns"
|
||||
@@ -842,6 +844,25 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.
|
||||
// Add split DNS routes, with no regard to exit node configuration.
|
||||
addSplitDNSRoutes(nm.DNS.Routes)
|
||||
|
||||
// Add split DNS routes for conn25
|
||||
conn25DNSTargets := appc.PickSplitDNSPeers(nm.HasCap, nm.SelfNode, peers)
|
||||
if conn25DNSTargets != nil {
|
||||
var m map[string][]*dnstype.Resolver
|
||||
for domain, candidateSplitDNSPeers := range conn25DNSTargets {
|
||||
for _, peer := range candidateSplitDNSPeers {
|
||||
base := peerAPIBase(nm, peer)
|
||||
if base == "" {
|
||||
continue
|
||||
}
|
||||
mak.Set(&m, domain, []*dnstype.Resolver{{Addr: fmt.Sprintf("%s/dns-query", base)}})
|
||||
break // Just make one resolver for the first peer we can get a peerAPIBase for.
|
||||
}
|
||||
}
|
||||
if m != nil {
|
||||
addSplitDNSRoutes(m)
|
||||
}
|
||||
}
|
||||
|
||||
// Set FallbackResolvers as the default resolvers in the
|
||||
// scenarios that can't handle a purely split-DNS config. See
|
||||
// https://github.com/tailscale/tailscale/issues/1743 for
|
||||
|
||||
Reference in New Issue
Block a user