Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b5ea3af

Browse files
committed
chore: support adding dns hosts to tailnet.Conn
1 parent d4131ba commit b5ea3af

File tree

3 files changed

+166
-7
lines changed

3 files changed

+166
-7
lines changed

tailnet/configmaps.go

+50-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"maps"
89
"net/netip"
910
"sync"
1011
"time"
@@ -14,9 +15,11 @@ import (
1415
"tailscale.com/ipn/ipnstate"
1516
"tailscale.com/net/dns"
1617
"tailscale.com/tailcfg"
18+
"tailscale.com/types/dnstype"
1719
"tailscale.com/types/ipproto"
1820
"tailscale.com/types/key"
1921
"tailscale.com/types/netmap"
22+
"tailscale.com/util/dnsname"
2023
"tailscale.com/wgengine"
2124
"tailscale.com/wgengine/filter"
2225
"tailscale.com/wgengine/router"
@@ -30,6 +33,10 @@ import (
3033

3134
const lostTimeout = 15 * time.Minute
3235

36+
// CoderDNSSuffix is the default DNS suffix that we append to Coder DNS
37+
// records.
38+
const CoderDNSSuffix = "coder."
39+
3340
// engineConfigurable is the subset of wgengine.Engine that we use for configuration.
3441
//
3542
// This allows us to test configuration code without faking the whole interface.
@@ -63,6 +70,7 @@ type configMaps struct {
6370

6471
engine engineConfigurable
6572
static netmap.NetworkMap
73+
hosts map[dnsname.FQDN][]netip.Addr
6674
peers map[uuid.UUID]*peerLifecycle
6775
addresses []netip.Prefix
6876
derpMap *tailcfg.DERPMap
@@ -79,6 +87,7 @@ func newConfigMaps(logger slog.Logger, engine engineConfigurable, nodeID tailcfg
7987
phased: phased{Cond: *(sync.NewCond(&sync.Mutex{}))},
8088
logger: logger,
8189
engine: engine,
90+
hosts: make(map[dnsname.FQDN][]netip.Addr),
8291
static: netmap.NetworkMap{
8392
SelfNode: &tailcfg.Node{
8493
ID: nodeID,
@@ -153,10 +162,11 @@ func (c *configMaps) configLoop() {
153162
}
154163
if c.netmapDirty {
155164
nm := c.netMapLocked()
165+
hosts := c.hostsLocked()
156166
actions = append(actions, func() {
157167
c.logger.Debug(context.Background(), "updating engine network map", slog.F("network_map", nm))
158168
c.engine.SetNetworkMap(nm)
159-
c.reconfig(nm)
169+
c.reconfig(nm, hosts)
160170
})
161171
}
162172
if c.filterDirty {
@@ -212,6 +222,11 @@ func (c *configMaps) netMapLocked() *netmap.NetworkMap {
212222
return nm
213223
}
214224

225+
// hostsLocked returns the current DNS hosts mapping. c.L must be held.
226+
func (c *configMaps) hostsLocked() map[dnsname.FQDN][]netip.Addr {
227+
return maps.Clone(c.hosts)
228+
}
229+
215230
// peerConfigLocked returns the set of peer nodes we have. c.L must be held.
216231
func (c *configMaps) peerConfigLocked() []*tailcfg.Node {
217232
out := make([]*tailcfg.Node, 0, len(c.peers))
@@ -261,6 +276,26 @@ func (c *configMaps) setAddresses(ips []netip.Prefix) {
261276
c.Broadcast()
262277
}
263278

279+
func (c *configMaps) addHosts(hosts map[dnsname.FQDN][]netip.Addr) {
280+
c.L.Lock()
281+
defer c.L.Unlock()
282+
for name, addrs := range hosts {
283+
c.hosts[name] = append(c.hosts[name], addrs...)
284+
}
285+
c.netmapDirty = true
286+
c.Broadcast()
287+
}
288+
289+
func (c *configMaps) removeHosts(names []dnsname.FQDN) {
290+
c.L.Lock()
291+
defer c.L.Unlock()
292+
for _, name := range names {
293+
delete(c.hosts, name)
294+
}
295+
c.netmapDirty = true
296+
c.Broadcast()
297+
}
298+
264299
// setBlockEndpoints sets whether we should block configuring endpoints we learn
265300
// from peers. It triggers a configuration of the engine if the value changes.
266301
// nolint: revive
@@ -305,7 +340,15 @@ func (c *configMaps) derpMapLocked() *tailcfg.DERPMap {
305340
// reconfig computes the correct wireguard config and calls the engine.Reconfig
306341
// with the config we have. It is not intended for this to be called outside of
307342
// the updateLoop()
308-
func (c *configMaps) reconfig(nm *netmap.NetworkMap) {
343+
func (c *configMaps) reconfig(nm *netmap.NetworkMap, hosts map[dnsname.FQDN][]netip.Addr) {
344+
dnsCfg := &dns.Config{}
345+
if len(hosts) > 0 {
346+
dnsCfg.Hosts = hosts
347+
dnsCfg.OnlyIPv6 = true
348+
dnsCfg.Routes = map[dnsname.FQDN][]*dnstype.Resolver{
349+
CoderDNSSuffix: nil,
350+
}
351+
}
309352
cfg, err := nmcfg.WGCfg(nm, Logger(c.logger.Named("net.wgconfig")), netmap.AllowSingleHosts, "")
310353
if err != nil {
311354
// WGCfg never returns an error at the time this code was written. If it starts, returning
@@ -314,8 +357,11 @@ func (c *configMaps) reconfig(nm *netmap.NetworkMap) {
314357
return
315358
}
316359

317-
rc := &router.Config{LocalAddrs: nm.Addresses}
318-
err = c.engine.Reconfig(cfg, rc, &dns.Config{}, &tailcfg.Debug{})
360+
rc := &router.Config{
361+
LocalAddrs: nm.Addresses,
362+
Routes: []netip.Prefix{CoderServicePrefixCIDR},
363+
}
364+
err = c.engine.Reconfig(cfg, rc, dnsCfg, &tailcfg.Debug{})
319365
if err != nil {
320366
if errors.Is(err, wgengine.ErrNoChanges) {
321367
return

tailnet/configmaps_internal_test.go

+99-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import (
1010
"github.com/google/uuid"
1111
"github.com/stretchr/testify/assert"
1212
"github.com/stretchr/testify/require"
13+
"golang.org/x/exp/maps"
1314
"tailscale.com/ipn/ipnstate"
1415
"tailscale.com/net/dns"
1516
"tailscale.com/tailcfg"
17+
"tailscale.com/types/dnstype"
1618
"tailscale.com/types/key"
1719
"tailscale.com/types/netmap"
20+
"tailscale.com/util/dnsname"
1821
"tailscale.com/wgengine/filter"
1922
"tailscale.com/wgengine/router"
2023
"tailscale.com/wgengine/wgcfg"
@@ -1157,6 +1160,99 @@ func TestConfigMaps_updatePeers_nonexist(t *testing.T) {
11571160
}
11581161
}
11591162

1163+
func TestConfigMaps_addRemoveHosts(t *testing.T) {
1164+
t.Parallel()
1165+
1166+
ctx := testutil.Context(t, testutil.WaitShort)
1167+
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
1168+
fEng := newFakeEngineConfigurable()
1169+
nodePrivateKey := key.NewNode()
1170+
nodeID := tailcfg.NodeID(5)
1171+
discoKey := key.NewDisco()
1172+
uut := newConfigMaps(logger, fEng, nodeID, nodePrivateKey, discoKey.Public())
1173+
defer uut.close()
1174+
1175+
addr1 := CoderServicePrefix.AddrFromUUID(uuid.UUID{1})
1176+
addr2 := CoderServicePrefix.AddrFromUUID(uuid.UUID{2})
1177+
addr3 := CoderServicePrefix.AddrFromUUID(uuid.UUID{3})
1178+
addr4 := CoderServicePrefix.AddrFromUUID(uuid.UUID{4})
1179+
1180+
// WHEN: we add two hosts
1181+
uut.addHosts(map[dnsname.FQDN][]netip.Addr{
1182+
"agent.myws.me.coder.": {
1183+
addr1,
1184+
},
1185+
"dev.main.me.coder.": {
1186+
addr2,
1187+
addr3,
1188+
},
1189+
})
1190+
1191+
// THEN: the engine is reconfigured with those same hosts
1192+
_ = testutil.RequireRecvCtx(ctx, t, fEng.setNetworkMap)
1193+
req := testutil.RequireRecvCtx(ctx, t, fEng.reconfig)
1194+
require.Equal(t, req.dnsCfg, &dns.Config{
1195+
Routes: map[dnsname.FQDN][]*dnstype.Resolver{
1196+
CoderDNSSuffix: nil,
1197+
},
1198+
Hosts: map[dnsname.FQDN][]netip.Addr{
1199+
"agent.myws.me.coder.": {
1200+
addr1,
1201+
},
1202+
"dev.main.me.coder.": {
1203+
addr2,
1204+
addr3,
1205+
},
1206+
},
1207+
OnlyIPv6: true,
1208+
})
1209+
1210+
// WHEN: we add a new host
1211+
newHost := map[dnsname.FQDN][]netip.Addr{
1212+
"agent2.myws.me.coder.": {
1213+
addr4,
1214+
},
1215+
}
1216+
uut.addHosts(newHost)
1217+
1218+
// THEN: the engine is reconfigured with both the old and new hosts
1219+
_ = testutil.RequireRecvCtx(ctx, t, fEng.setNetworkMap)
1220+
req = testutil.RequireRecvCtx(ctx, t, fEng.reconfig)
1221+
require.Equal(t, req.dnsCfg, &dns.Config{
1222+
Routes: map[dnsname.FQDN][]*dnstype.Resolver{
1223+
CoderDNSSuffix: nil,
1224+
},
1225+
Hosts: map[dnsname.FQDN][]netip.Addr{
1226+
"agent.myws.me.coder.": {
1227+
addr1,
1228+
},
1229+
"dev.main.me.coder.": {
1230+
addr2,
1231+
addr3,
1232+
},
1233+
"agent2.myws.me.coder.": {
1234+
addr4,
1235+
},
1236+
},
1237+
OnlyIPv6: true,
1238+
})
1239+
1240+
// WHEN: we remove all the hosts, and a bad host
1241+
uut.removeHosts(append(maps.Keys(req.dnsCfg.Hosts), "badhostname"))
1242+
_ = testutil.RequireRecvCtx(ctx, t, fEng.setNetworkMap)
1243+
req = testutil.RequireRecvCtx(ctx, t, fEng.reconfig)
1244+
1245+
// THEN: the engine is reconfigured with an empty config
1246+
require.Equal(t, req.dnsCfg, &dns.Config{})
1247+
1248+
done := make(chan struct{})
1249+
go func() {
1250+
defer close(done)
1251+
uut.close()
1252+
}()
1253+
_ = testutil.RequireRecvCtx(ctx, t, done)
1254+
}
1255+
11601256
func newTestNode(id int) *Node {
11611257
return &Node{
11621258
ID: tailcfg.NodeID(id),
@@ -1199,6 +1295,7 @@ func requireNeverConfigures(ctx context.Context, t *testing.T, uut *phased) {
11991295
type reconfigCall struct {
12001296
wg *wgcfg.Config
12011297
router *router.Config
1298+
dnsCfg *dns.Config
12021299
}
12031300

12041301
var _ engineConfigurable = &fakeEngineConfigurable{}
@@ -1235,8 +1332,8 @@ func (f fakeEngineConfigurable) SetNetworkMap(networkMap *netmap.NetworkMap) {
12351332
f.setNetworkMap <- networkMap
12361333
}
12371334

1238-
func (f fakeEngineConfigurable) Reconfig(wg *wgcfg.Config, r *router.Config, _ *dns.Config, _ *tailcfg.Debug) error {
1239-
f.reconfig <- reconfigCall{wg: wg, router: r}
1335+
func (f fakeEngineConfigurable) Reconfig(wg *wgcfg.Config, r *router.Config, dnsCfg *dns.Config, _ *tailcfg.Debug) error {
1336+
f.reconfig <- reconfigCall{wg: wg, router: r, dnsCfg: dnsCfg}
12401337
return nil
12411338
}
12421339

tailnet/conn.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
tslogger "tailscale.com/types/logger"
3434
"tailscale.com/types/netlogtype"
3535
"tailscale.com/types/netmap"
36+
"tailscale.com/util/dnsname"
3637
"tailscale.com/wgengine"
3738
"tailscale.com/wgengine/capture"
3839
"tailscale.com/wgengine/magicsock"
@@ -287,6 +288,7 @@ func NewConn(options *Options) (conn *Conn, err error) {
287288
configMaps: cfgMaps,
288289
nodeUpdater: nodeUp,
289290
telemetrySink: options.TelemetrySink,
291+
dnsConfigurator: options.DNSConfigurator,
290292
telemetryStore: telemetryStore,
291293
createdAt: time.Now(),
292294
watchCtx: ctx,
@@ -347,7 +349,8 @@ var (
347349
// process of migrating to. It allows Coder to run alongside Tailscale without conflicts even
348350
// if both are set up as TUN interfaces into the OS (e.g. CoderVPN).
349351
// fd60:627a:a42b::/48
350-
CoderServicePrefix ServicePrefix = [6]byte{0xfd, 0x60, 0x62, 0x7a, 0xa4, 0x2b}
352+
CoderServicePrefix ServicePrefix = [6]byte{0xfd, 0x60, 0x62, 0x7a, 0xa4, 0x2b}
353+
CoderServicePrefixCIDR = netip.MustParsePrefix("fd60:627a:a42b::/48")
351354
)
352355

353356
// maskUUID returns a new UUID with the first 6 bytes changed to the ServicePrefix
@@ -393,6 +396,7 @@ type Conn struct {
393396
wireguardMonitor *netmon.Monitor
394397
wireguardRouter *router.Config
395398
wireguardEngine wgengine.Engine
399+
dnsConfigurator dns.OSConfigurator
396400
listeners map[listenKey]*listener
397401
clientType proto.TelemetryEvent_ClientType
398402
createdAt time.Time
@@ -439,6 +443,18 @@ func (c *Conn) SetAddresses(ips []netip.Prefix) error {
439443
return nil
440444
}
441445

446+
func (c *Conn) AddDNSHosts(hosts map[dnsname.FQDN][]netip.Addr) {
447+
if c.dnsConfigurator == nil {
448+
c.logger.Warn(context.Background(), "no DNSConfigurator set, rejecting custom DNS hosts")
449+
return
450+
}
451+
c.configMaps.addHosts(hosts)
452+
}
453+
454+
func (c *Conn) RemoveDNSHosts(names []dnsname.FQDN) {
455+
c.configMaps.removeHosts(names)
456+
}
457+
442458
func (c *Conn) SetNodeCallback(callback func(node *Node)) {
443459
c.nodeUpdater.setCallback(callback)
444460
}

0 commit comments

Comments
 (0)