Skip to content

Commit 9e2af3e

Browse files
authored
feat: add configurable DNS match domain for tailnet connections (#17336)
Use the hostname suffix to set the DNS match domain when creating a Tailnet as part of the vpn `Tunnel`. part of: #16828
1 parent 69aa365 commit 9e2af3e

File tree

6 files changed

+65
-34
lines changed

6 files changed

+65
-34
lines changed

tailnet/configmaps.go

+17-7
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ const lostTimeout = 15 * time.Minute
3636

3737
// CoderDNSSuffix is the default DNS suffix that we append to Coder DNS
3838
// records.
39-
const CoderDNSSuffix = "coder."
39+
const (
40+
CoderDNSSuffix = "coder"
41+
CoderDNSSuffixFQDN = dnsname.FQDN(CoderDNSSuffix + ".")
42+
)
4043

4144
// engineConfigurable is the subset of wgengine.Engine that we use for configuration.
4245
//
@@ -69,20 +72,26 @@ type configMaps struct {
6972
filterDirty bool
7073
closing bool
7174

72-
engine engineConfigurable
73-
static netmap.NetworkMap
75+
engine engineConfigurable
76+
static netmap.NetworkMap
77+
7478
hosts map[dnsname.FQDN][]netip.Addr
7579
peers map[uuid.UUID]*peerLifecycle
7680
addresses []netip.Prefix
7781
derpMap *tailcfg.DERPMap
7882
logger slog.Logger
7983
blockEndpoints bool
84+
matchDomain dnsname.FQDN
8085

8186
// for testing
8287
clock quartz.Clock
8388
}
8489

85-
func newConfigMaps(logger slog.Logger, engine engineConfigurable, nodeID tailcfg.NodeID, nodeKey key.NodePrivate, discoKey key.DiscoPublic) *configMaps {
90+
func newConfigMaps(
91+
logger slog.Logger, engine engineConfigurable,
92+
nodeID tailcfg.NodeID, nodeKey key.NodePrivate, discoKey key.DiscoPublic,
93+
matchDomain dnsname.FQDN,
94+
) *configMaps {
8695
pubKey := nodeKey.Public()
8796
c := &configMaps{
8897
phased: phased{Cond: *(sync.NewCond(&sync.Mutex{}))},
@@ -125,8 +134,9 @@ func newConfigMaps(logger slog.Logger, engine engineConfigurable, nodeID tailcfg
125134
Caps: []filter.CapMatch{},
126135
}},
127136
},
128-
peers: make(map[uuid.UUID]*peerLifecycle),
129-
clock: quartz.NewReal(),
137+
peers: make(map[uuid.UUID]*peerLifecycle),
138+
matchDomain: matchDomain,
139+
clock: quartz.NewReal(),
130140
}
131141
go c.configLoop()
132142
return c
@@ -338,7 +348,7 @@ func (c *configMaps) reconfig(nm *netmap.NetworkMap, hosts map[dnsname.FQDN][]ne
338348
dnsCfg.Hosts = hosts
339349
dnsCfg.OnlyIPv6 = true
340350
dnsCfg.Routes = map[dnsname.FQDN][]*dnstype.Resolver{
341-
CoderDNSSuffix: nil,
351+
c.matchDomain: nil,
342352
}
343353
}
344354
cfg, err := nmcfg.WGCfg(nm, Logger(c.logger.Named("net.wgconfig")), netmap.AllowSingleHosts, "")

tailnet/configmaps_internal_test.go

+24-21
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestConfigMaps_setAddresses_different(t *testing.T) {
3434
nodePrivateKey := key.NewNode()
3535
nodeID := tailcfg.NodeID(5)
3636
discoKey := key.NewDisco()
37-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
37+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
3838
defer uut.close()
3939

4040
addrs := []netip.Prefix{netip.MustParsePrefix("192.168.0.200/32")}
@@ -93,7 +93,7 @@ func TestConfigMaps_setAddresses_same(t *testing.T) {
9393
nodeID := tailcfg.NodeID(5)
9494
discoKey := key.NewDisco()
9595
addrs := []netip.Prefix{netip.MustParsePrefix("192.168.0.200/32")}
96-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
96+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
9797
defer uut.close()
9898

9999
// Given: addresses already set
@@ -123,7 +123,7 @@ func TestConfigMaps_updatePeers_new(t *testing.T) {
123123
nodePrivateKey := key.NewNode()
124124
nodeID := tailcfg.NodeID(5)
125125
discoKey := key.NewDisco()
126-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
126+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
127127
defer uut.close()
128128

129129
p1ID := uuid.UUID{1}
@@ -193,7 +193,7 @@ func TestConfigMaps_updatePeers_new_waitForHandshake_neverConfigures(t *testing.
193193
nodePrivateKey := key.NewNode()
194194
nodeID := tailcfg.NodeID(5)
195195
discoKey := key.NewDisco()
196-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
196+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
197197
defer uut.close()
198198
mClock := quartz.NewMock(t)
199199
uut.clock = mClock
@@ -237,7 +237,7 @@ func TestConfigMaps_updatePeers_new_waitForHandshake_outOfOrder(t *testing.T) {
237237
nodePrivateKey := key.NewNode()
238238
nodeID := tailcfg.NodeID(5)
239239
discoKey := key.NewDisco()
240-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
240+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
241241
defer uut.close()
242242
mClock := quartz.NewMock(t)
243243
uut.clock = mClock
@@ -308,7 +308,7 @@ func TestConfigMaps_updatePeers_new_waitForHandshake(t *testing.T) {
308308
nodePrivateKey := key.NewNode()
309309
nodeID := tailcfg.NodeID(5)
310310
discoKey := key.NewDisco()
311-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
311+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
312312
defer uut.close()
313313
mClock := quartz.NewMock(t)
314314
uut.clock = mClock
@@ -379,7 +379,7 @@ func TestConfigMaps_updatePeers_new_waitForHandshake_timeout(t *testing.T) {
379379
nodePrivateKey := key.NewNode()
380380
nodeID := tailcfg.NodeID(5)
381381
discoKey := key.NewDisco()
382-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
382+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
383383
defer uut.close()
384384
mClock := quartz.NewMock(t)
385385
uut.clock = mClock
@@ -437,7 +437,7 @@ func TestConfigMaps_updatePeers_same(t *testing.T) {
437437
nodePrivateKey := key.NewNode()
438438
nodeID := tailcfg.NodeID(5)
439439
discoKey := key.NewDisco()
440-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
440+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
441441
defer uut.close()
442442

443443
// Then: we don't configure
@@ -496,7 +496,7 @@ func TestConfigMaps_updatePeers_disconnect(t *testing.T) {
496496
nodePrivateKey := key.NewNode()
497497
nodeID := tailcfg.NodeID(5)
498498
discoKey := key.NewDisco()
499-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
499+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
500500
defer uut.close()
501501

502502
p1ID := uuid.UUID{1}
@@ -564,7 +564,7 @@ func TestConfigMaps_updatePeers_lost(t *testing.T) {
564564
nodePrivateKey := key.NewNode()
565565
nodeID := tailcfg.NodeID(5)
566566
discoKey := key.NewDisco()
567-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
567+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
568568
defer uut.close()
569569
mClock := quartz.NewMock(t)
570570
start := mClock.Now()
@@ -649,7 +649,7 @@ func TestConfigMaps_updatePeers_lost_and_found(t *testing.T) {
649649
nodePrivateKey := key.NewNode()
650650
nodeID := tailcfg.NodeID(5)
651651
discoKey := key.NewDisco()
652-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
652+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
653653
defer uut.close()
654654
mClock := quartz.NewMock(t)
655655
start := mClock.Now()
@@ -734,7 +734,7 @@ func TestConfigMaps_setAllPeersLost(t *testing.T) {
734734
nodePrivateKey := key.NewNode()
735735
nodeID := tailcfg.NodeID(5)
736736
discoKey := key.NewDisco()
737-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
737+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
738738
defer uut.close()
739739
mClock := quartz.NewMock(t)
740740
start := mClock.Now()
@@ -820,7 +820,7 @@ func TestConfigMaps_setBlockEndpoints_different(t *testing.T) {
820820
nodePrivateKey := key.NewNode()
821821
nodeID := tailcfg.NodeID(5)
822822
discoKey := key.NewDisco()
823-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
823+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
824824
defer uut.close()
825825

826826
p1ID := uuid.MustParse("10000000-0000-0000-0000-000000000000")
@@ -864,7 +864,7 @@ func TestConfigMaps_setBlockEndpoints_same(t *testing.T) {
864864
nodePrivateKey := key.NewNode()
865865
nodeID := tailcfg.NodeID(5)
866866
discoKey := key.NewDisco()
867-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
867+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
868868
defer uut.close()
869869

870870
p1ID := uuid.MustParse("10000000-0000-0000-0000-000000000000")
@@ -907,7 +907,7 @@ func TestConfigMaps_setDERPMap_different(t *testing.T) {
907907
nodePrivateKey := key.NewNode()
908908
nodeID := tailcfg.NodeID(5)
909909
discoKey := key.NewDisco()
910-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
910+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
911911
defer uut.close()
912912

913913
derpMap := &tailcfg.DERPMap{
@@ -948,7 +948,7 @@ func TestConfigMaps_setDERPMap_same(t *testing.T) {
948948
nodePrivateKey := key.NewNode()
949949
nodeID := tailcfg.NodeID(5)
950950
discoKey := key.NewDisco()
951-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
951+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
952952
defer uut.close()
953953

954954
// Given: DERP Map already set
@@ -1017,7 +1017,7 @@ func TestConfigMaps_fillPeerDiagnostics(t *testing.T) {
10171017
nodePrivateKey := key.NewNode()
10181018
nodeID := tailcfg.NodeID(5)
10191019
discoKey := key.NewDisco()
1020-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
1020+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
10211021
defer uut.close()
10221022

10231023
// Given: DERP Map and peer already set
@@ -1125,7 +1125,7 @@ func TestConfigMaps_updatePeers_nonexist(t *testing.T) {
11251125
nodePrivateKey := key.NewNode()
11261126
nodeID := tailcfg.NodeID(5)
11271127
discoKey := key.NewDisco()
1128-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
1128+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), CoderDNSSuffixFQDN)
11291129
defer uut.close()
11301130

11311131
// Then: we don't configure
@@ -1166,7 +1166,8 @@ func TestConfigMaps_addRemoveHosts(t *testing.T) {
11661166
nodePrivateKey := key.NewNode()
11671167
nodeID := tailcfg.NodeID(5)
11681168
discoKey := key.NewDisco()
1169-
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
1169+
suffix := dnsname.FQDN("test.")
1170+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public(), suffix)
11701171
defer uut.close()
11711172

11721173
addr1 := CoderServicePrefix.AddrFromUUID(uuid.New())
@@ -1190,8 +1191,10 @@ func TestConfigMaps_addRemoveHosts(t *testing.T) {
11901191
req := testutil.RequireRecvCtx(ctx, t, fEng.reconfig)
11911192
require.Equal(t, req.dnsCfg, &dns.Config{
11921193
Routes: map[dnsname.FQDN][]*dnstype.Resolver{
1193-
CoderDNSSuffix: nil,
1194+
suffix: nil,
11941195
},
1196+
// Note that host names and Routes are independent --- so we faithfully reproduce the hosts, even though
1197+
// they don't match the route.
11951198
Hosts: map[dnsname.FQDN][]netip.Addr{
11961199
"agent.myws.me.coder.": {
11971200
addr1,
@@ -1219,7 +1222,7 @@ func TestConfigMaps_addRemoveHosts(t *testing.T) {
12191222
req = testutil.RequireRecvCtx(ctx, t, fEng.reconfig)
12201223
require.Equal(t, req.dnsCfg, &dns.Config{
12211224
Routes: map[dnsname.FQDN][]*dnstype.Resolver{
1222-
CoderDNSSuffix: nil,
1225+
suffix: nil,
12231226
},
12241227
Hosts: map[dnsname.FQDN][]netip.Addr{
12251228
"newagent.myws.me.coder.": {

tailnet/conn.go

+12
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ type Options struct {
120120
// WireguardMonitor is optional, and is passed to the underlying wireguard
121121
// engine.
122122
WireguardMonitor *netmon.Monitor
123+
// DNSMatchDomain is the DNS suffix to use as a match domain. Only relevant for TUN connections that configure the
124+
// OS DNS resolver.
125+
DNSMatchDomain string
123126
}
124127

125128
// TelemetrySink allows tailnet.Conn to send network telemetry to the Coder
@@ -267,12 +270,21 @@ func NewConn(options *Options) (conn *Conn, err error) {
267270
netStack.ProcessLocalIPs = true
268271
}
269272

273+
if options.DNSMatchDomain == "" {
274+
options.DNSMatchDomain = CoderDNSSuffix
275+
}
276+
matchDomain, err := dnsname.ToFQDN(options.DNSMatchDomain + ".")
277+
if err != nil {
278+
return nil, xerrors.Errorf("convert hostname suffix (%s) to fully-qualified domain: %w",
279+
options.DNSMatchDomain, err)
280+
}
270281
cfgMaps := newConfigMaps(
271282
options.Logger,
272283
wireguardEngine,
273284
nodeID,
274285
nodePrivateKey,
275286
magicConn.DiscoPublicKey(),
287+
matchDomain,
276288
)
277289
cfgMaps.setAddresses(options.Addresses)
278290
if options.DERPMap != nil {

tailnet/controllers.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1309,7 +1309,7 @@ func NewTunnelAllWorkspaceUpdatesController(
13091309
t := &TunnelAllWorkspaceUpdatesController{
13101310
logger: logger,
13111311
coordCtrl: c,
1312-
dnsNameOptions: DNSNameOptions{"coder"},
1312+
dnsNameOptions: DNSNameOptions{CoderDNSSuffix},
13131313
}
13141314
for _, opt := range opts {
13151315
opt(t)

tailnet/controllers_test.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -1637,7 +1637,7 @@ func TestTunnelAllWorkspaceUpdatesController_DeleteAgent(t *testing.T) {
16371637
fUH := newFakeUpdateHandler(ctx, t)
16381638
fDNS := newFakeDNSSetter(ctx, t)
16391639
coordC, updateC, updateCtrl := setupConnectedAllWorkspaceUpdatesController(ctx, t, logger,
1640-
tailnet.WithDNS(fDNS, "testy", tailnet.DNSNameOptions{Suffix: "coder"}),
1640+
tailnet.WithDNS(fDNS, "testy", tailnet.DNSNameOptions{Suffix: tailnet.CoderDNSSuffix}),
16411641
tailnet.WithHandler(fUH),
16421642
)
16431643

@@ -1664,7 +1664,8 @@ func TestTunnelAllWorkspaceUpdatesController_DeleteAgent(t *testing.T) {
16641664
require.Equal(t, w1a1ID[:], coordCall.req.GetAddTunnel().GetId())
16651665
testutil.RequireSendCtx(ctx, t, coordCall.err, nil)
16661666

1667-
expectedCoderConnectFQDN, err := dnsname.ToFQDN(fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString, "coder"))
1667+
expectedCoderConnectFQDN, err := dnsname.ToFQDN(
1668+
fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString, tailnet.CoderDNSSuffix))
16681669
require.NoError(t, err)
16691670

16701671
// DNS for w1a1
@@ -1785,7 +1786,7 @@ func TestTunnelAllWorkspaceUpdatesController_DNSError(t *testing.T) {
17851786
fConn := &fakeCoordinatee{}
17861787
tsc := tailnet.NewTunnelSrcCoordController(logger, fConn)
17871788
uut := tailnet.NewTunnelAllWorkspaceUpdatesController(logger, tsc,
1788-
tailnet.WithDNS(fDNS, "testy", tailnet.DNSNameOptions{Suffix: "coder"}),
1789+
tailnet.WithDNS(fDNS, "testy", tailnet.DNSNameOptions{Suffix: tailnet.CoderDNSSuffix}),
17891790
)
17901791

17911792
updateC := newFakeWorkspaceUpdateClient(ctx, t)
@@ -1806,7 +1807,8 @@ func TestTunnelAllWorkspaceUpdatesController_DNSError(t *testing.T) {
18061807
upRecvCall := testutil.RequireRecvCtx(ctx, t, updateC.recv)
18071808
testutil.RequireSendCtx(ctx, t, upRecvCall.resp, initUp)
18081809

1809-
expectedCoderConnectFQDN, err := dnsname.ToFQDN(fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString, "coder"))
1810+
expectedCoderConnectFQDN, err := dnsname.ToFQDN(
1811+
fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString, tailnet.CoderDNSSuffix))
18101812
require.NoError(t, err)
18111813

18121814
// DNS for w1a1

vpn/client.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/url"
88

99
"golang.org/x/xerrors"
10+
1011
"tailscale.com/net/dns"
1112
"tailscale.com/net/netmon"
1213
"tailscale.com/wgengine/router"
@@ -108,9 +109,11 @@ func (*client) NewConn(initCtx context.Context, serverURL *url.URL, token string
108109
return nil, xerrors.Errorf("get connection info: %w", err)
109110
}
110111
// default to DNS suffix of "coder" if the server hasn't set it (might be too old).
111-
dnsNameOptions := tailnet.DNSNameOptions{Suffix: "coder"}
112+
dnsNameOptions := tailnet.DNSNameOptions{Suffix: tailnet.CoderDNSSuffix}
113+
dnsMatch := tailnet.CoderDNSSuffix
112114
if connInfo.HostnameSuffix != "" {
113115
dnsNameOptions.Suffix = connInfo.HostnameSuffix
116+
dnsMatch = connInfo.HostnameSuffix
114117
}
115118

116119
headers.Set(codersdk.SessionTokenHeader, token)
@@ -134,6 +137,7 @@ func (*client) NewConn(initCtx context.Context, serverURL *url.URL, token string
134137
Router: options.Router,
135138
TUNDev: options.TUNDevice,
136139
WireguardMonitor: options.WireguardMonitor,
140+
DNSMatchDomain: dnsMatch,
137141
})
138142
if err != nil {
139143
return nil, xerrors.Errorf("create tailnet: %w", err)

0 commit comments

Comments
 (0)