Skip to content

Commit 85c3c4c

Browse files
authored
feat(tailnet): add alias with username and short alias to DNS (coder#15585)
Adds DNS aliases of the form `<agent>.<workspace>.<username>.coder.` and `<workspace>.coder.`
1 parent c3c23ed commit 85c3c4c

File tree

2 files changed

+83
-48
lines changed

2 files changed

+83
-48
lines changed

tailnet/controllers.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ func (r *basicResumeTokenRefresher) refresh() {
858858
type tunnelAllWorkspaceUpdatesController struct {
859859
coordCtrl *TunnelSrcCoordController
860860
dnsHostSetter DNSHostsSetter
861+
ownerUsername string
861862
logger slog.Logger
862863
}
863864

@@ -868,18 +869,30 @@ type workspace struct {
868869
}
869870

870871
// addAllDNSNames adds names for all of its agents to the given map of names
871-
func (w workspace) addAllDNSNames(names map[dnsname.FQDN][]netip.Addr) error {
872+
func (w workspace) addAllDNSNames(names map[dnsname.FQDN][]netip.Addr, owner string) error {
872873
for _, a := range w.agents {
873874
// TODO: technically, DNS labels cannot start with numbers, but the rules are often not
874875
// strictly enforced.
875-
// TODO: support <agent>.<workspace>.<username>.coder
876876
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%s.%s.me.coder.", a.name, w.name))
877877
if err != nil {
878878
return err
879879
}
880880
names[fqdn] = []netip.Addr{CoderServicePrefix.AddrFromUUID(a.id)}
881+
fqdn, err = dnsname.ToFQDN(fmt.Sprintf("%s.%s.%s.coder.", a.name, w.name, owner))
882+
if err != nil {
883+
return err
884+
}
885+
names[fqdn] = []netip.Addr{CoderServicePrefix.AddrFromUUID(a.id)}
886+
}
887+
if len(w.agents) == 1 {
888+
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%s.coder.", w.name))
889+
if err != nil {
890+
return err
891+
}
892+
for _, a := range w.agents {
893+
names[fqdn] = []netip.Addr{CoderServicePrefix.AddrFromUUID(a.id)}
894+
}
881895
}
882-
// TODO: Possibly support <workspace>.coder. alias if there is only one agent
883896
return nil
884897
}
885898

@@ -895,6 +908,7 @@ func (t *tunnelAllWorkspaceUpdatesController) New(client WorkspaceUpdatesClient)
895908
logger: t.logger,
896909
coordCtrl: t.coordCtrl,
897910
dnsHostsSetter: t.dnsHostSetter,
911+
ownerUsername: t.ownerUsername,
898912
recvLoopDone: make(chan struct{}),
899913
workspaces: make(map[uuid.UUID]*workspace),
900914
}
@@ -908,6 +922,7 @@ type tunnelUpdater struct {
908922
client WorkspaceUpdatesClient
909923
coordCtrl *TunnelSrcCoordController
910924
dnsHostsSetter DNSHostsSetter
925+
ownerUsername string
911926
recvLoopDone chan struct{}
912927

913928
// don't need the mutex since only manipulated by the recvLoop
@@ -1088,7 +1103,7 @@ func (t *tunnelUpdater) allAgentIDs() []uuid.UUID {
10881103
func (t *tunnelUpdater) allDNSNames() map[dnsname.FQDN][]netip.Addr {
10891104
names := make(map[dnsname.FQDN][]netip.Addr)
10901105
for _, w := range t.workspaces {
1091-
err := w.addAllDNSNames(names)
1106+
err := w.addAllDNSNames(names, t.ownerUsername)
10921107
if err != nil {
10931108
// This should never happen in production, because converting the FQDN only fails
10941109
// if names are too long, and we put strict length limits on agent, workspace, and user
@@ -1102,13 +1117,28 @@ func (t *tunnelUpdater) allDNSNames() map[dnsname.FQDN][]netip.Addr {
11021117
return names
11031118
}
11041119

1120+
type TunnelAllOption func(t *tunnelAllWorkspaceUpdatesController)
1121+
1122+
// WithDNS configures the tunnelAllWorkspaceUpdatesController to set DNS names for all workspaces
1123+
// and agents it learns about.
1124+
func WithDNS(d DNSHostsSetter, ownerUsername string) TunnelAllOption {
1125+
return func(t *tunnelAllWorkspaceUpdatesController) {
1126+
t.dnsHostSetter = d
1127+
t.ownerUsername = ownerUsername
1128+
}
1129+
}
1130+
11051131
// NewTunnelAllWorkspaceUpdatesController creates a WorkspaceUpdatesController that creates tunnels
11061132
// (via the TunnelSrcCoordController) to all agents received over the WorkspaceUpdates RPC. If a
11071133
// DNSHostSetter is provided, it also programs DNS hosts based on the agent and workspace names.
11081134
func NewTunnelAllWorkspaceUpdatesController(
1109-
logger slog.Logger, c *TunnelSrcCoordController, d DNSHostsSetter,
1135+
logger slog.Logger, c *TunnelSrcCoordController, opts ...TunnelAllOption,
11101136
) WorkspaceUpdatesController {
1111-
return &tunnelAllWorkspaceUpdatesController{logger: logger, coordCtrl: c, dnsHostSetter: d}
1137+
t := &tunnelAllWorkspaceUpdatesController{logger: logger, coordCtrl: c}
1138+
for _, opt := range opts {
1139+
opt(t)
1140+
}
1141+
return t
11121142
}
11131143

11141144
// NewController creates a new Controller without running it

tailnet/controllers_test.go

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -974,13 +974,13 @@ func (f *fakeResumeTokenClient) RefreshResumeToken(_ context.Context, _ *proto.R
974974
}
975975
select {
976976
case <-f.ctx.Done():
977-
return nil, f.ctx.Err()
977+
return nil, timeoutOnFakeErr
978978
case f.calls <- call:
979979
// OK
980980
}
981981
select {
982982
case <-f.ctx.Done():
983-
return nil, f.ctx.Err()
983+
return nil, timeoutOnFakeErr
984984
case err := <-call.errCh:
985985
return nil, err
986986
case resp := <-call.resp:
@@ -1240,6 +1240,11 @@ func (p *pipeDialer) Dial(_ context.Context, _ tailnet.ResumeTokenController) (t
12401240
}, nil
12411241
}
12421242

1243+
// timeoutOnFakeErr is the error we send when fakes fail to send calls or receive responses before
1244+
// their context times out. We don't want to send the context error since that often doesn't trigger
1245+
// test failures or logging.
1246+
var timeoutOnFakeErr = xerrors.New("test timeout")
1247+
12431248
type fakeCoordinatorClient struct {
12441249
ctx context.Context
12451250
t testing.TB
@@ -1253,15 +1258,13 @@ func (f fakeCoordinatorClient) Close() error {
12531258
errs := make(chan error)
12541259
select {
12551260
case <-f.ctx.Done():
1256-
f.t.Error("timed out waiting to send close call")
1257-
return f.ctx.Err()
1261+
return timeoutOnFakeErr
12581262
case f.close <- errs:
12591263
// OK
12601264
}
12611265
select {
12621266
case <-f.ctx.Done():
1263-
f.t.Error("timed out waiting for close call response")
1264-
return f.ctx.Err()
1267+
return timeoutOnFakeErr
12651268
case err := <-errs:
12661269
return err
12671270
}
@@ -1276,15 +1279,13 @@ func (f fakeCoordinatorClient) Send(request *proto.CoordinateRequest) error {
12761279
}
12771280
select {
12781281
case <-f.ctx.Done():
1279-
f.t.Error("timed out waiting to send call")
1280-
return f.ctx.Err()
1282+
return timeoutOnFakeErr
12811283
case f.reqs <- call:
12821284
// OK
12831285
}
12841286
select {
12851287
case <-f.ctx.Done():
1286-
f.t.Error("timed out waiting for send call response")
1287-
return f.ctx.Err()
1288+
return timeoutOnFakeErr
12881289
case err := <-errs:
12891290
return err
12901291
}
@@ -1300,15 +1301,13 @@ func (f fakeCoordinatorClient) Recv() (*proto.CoordinateResponse, error) {
13001301
}
13011302
select {
13021303
case <-f.ctx.Done():
1303-
f.t.Error("timed out waiting to send Recv() call")
1304-
return nil, f.ctx.Err()
1304+
return nil, timeoutOnFakeErr
13051305
case f.resps <- call:
13061306
// OK
13071307
}
13081308
select {
13091309
case <-f.ctx.Done():
1310-
f.t.Error("timed out waiting for Recv() call response")
1311-
return nil, f.ctx.Err()
1310+
return nil, timeoutOnFakeErr
13121311
case err := <-errs:
13131312
return nil, err
13141313
case resp := <-resps:
@@ -1348,15 +1347,13 @@ func (f *fakeWorkspaceUpdateClient) Close() error {
13481347
errs := make(chan error)
13491348
select {
13501349
case <-f.ctx.Done():
1351-
f.t.Error("timed out waiting to send close call")
1352-
return f.ctx.Err()
1350+
return timeoutOnFakeErr
13531351
case f.close <- errs:
13541352
// OK
13551353
}
13561354
select {
13571355
case <-f.ctx.Done():
1358-
f.t.Error("timed out waiting for close call response")
1359-
return f.ctx.Err()
1356+
return timeoutOnFakeErr
13601357
case err := <-errs:
13611358
return err
13621359
}
@@ -1372,15 +1369,13 @@ func (f *fakeWorkspaceUpdateClient) Recv() (*proto.WorkspaceUpdate, error) {
13721369
}
13731370
select {
13741371
case <-f.ctx.Done():
1375-
f.t.Error("timed out waiting to send Recv() call")
1376-
return nil, f.ctx.Err()
1372+
return nil, timeoutOnFakeErr
13771373
case f.recv <- call:
13781374
// OK
13791375
}
13801376
select {
13811377
case <-f.ctx.Done():
1382-
f.t.Error("timed out waiting for Recv() call response")
1383-
return nil, f.ctx.Err()
1378+
return nil, timeoutOnFakeErr
13841379
case err := <-errs:
13851380
return nil, err
13861381
case resp := <-resps:
@@ -1440,28 +1435,26 @@ func (f *fakeDNSSetter) SetDNSHosts(hosts map[dnsname.FQDN][]netip.Addr) error {
14401435
}
14411436
select {
14421437
case <-f.ctx.Done():
1443-
f.t.Error("timed out waiting to send SetDNSHosts() call")
1444-
return f.ctx.Err()
1438+
return timeoutOnFakeErr
14451439
case f.calls <- call:
14461440
// OK
14471441
}
14481442
select {
14491443
case <-f.ctx.Done():
1450-
f.t.Error("timed out waiting for SetDNSHosts() call response")
1451-
return f.ctx.Err()
1444+
return timeoutOnFakeErr
14521445
case err := <-errs:
14531446
return err
14541447
}
14551448
}
14561449

14571450
func setupConnectedAllWorkspaceUpdatesController(
1458-
ctx context.Context, t testing.TB, logger slog.Logger, dnsSetter tailnet.DNSHostsSetter,
1451+
ctx context.Context, t testing.TB, logger slog.Logger, opts ...tailnet.TunnelAllOption,
14591452
) (
14601453
*fakeCoordinatorClient, *fakeWorkspaceUpdateClient,
14611454
) {
14621455
fConn := &fakeCoordinatee{}
14631456
tsc := tailnet.NewTunnelSrcCoordController(logger, fConn)
1464-
uut := tailnet.NewTunnelAllWorkspaceUpdatesController(logger, tsc, dnsSetter)
1457+
uut := tailnet.NewTunnelAllWorkspaceUpdatesController(logger, tsc, opts...)
14651458

14661459
// connect up a coordinator client, to track adding and removing tunnels
14671460
coordC := newFakeCoordinatorClient(ctx, t)
@@ -1496,7 +1489,8 @@ func TestTunnelAllWorkspaceUpdatesController_Initial(t *testing.T) {
14961489
logger := testutil.Logger(t)
14971490

14981491
fDNS := newFakeDNSSetter(ctx, t)
1499-
coordC, updateC := setupConnectedAllWorkspaceUpdatesController(ctx, t, logger, fDNS)
1492+
coordC, updateC := setupConnectedAllWorkspaceUpdatesController(ctx, t, logger,
1493+
tailnet.WithDNS(fDNS, "testy"))
15001494

15011495
// Initial update contains 2 workspaces with 1 & 2 agents, respectively
15021496
w1ID := testUUID(1)
@@ -1532,9 +1526,13 @@ func TestTunnelAllWorkspaceUpdatesController_Initial(t *testing.T) {
15321526

15331527
// Also triggers setting DNS hosts
15341528
expectedDNS := map[dnsname.FQDN][]netip.Addr{
1535-
"w1a1.w1.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1536-
"w2a1.w2.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0201::")},
1537-
"w2a2.w2.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0202::")},
1529+
"w1a1.w1.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1530+
"w2a1.w2.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0201::")},
1531+
"w2a2.w2.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0202::")},
1532+
"w1a1.w1.testy.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1533+
"w2a1.w2.testy.coder.": {netip.MustParseAddr("fd60:627a:a42b:0201::")},
1534+
"w2a2.w2.testy.coder.": {netip.MustParseAddr("fd60:627a:a42b:0202::")},
1535+
"w1.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
15381536
}
15391537
dnsCall := testutil.RequireRecvCtx(ctx, t, fDNS.calls)
15401538
require.Equal(t, expectedDNS, dnsCall.hosts)
@@ -1547,7 +1545,8 @@ func TestTunnelAllWorkspaceUpdatesController_DeleteAgent(t *testing.T) {
15471545
logger := testutil.Logger(t)
15481546

15491547
fDNS := newFakeDNSSetter(ctx, t)
1550-
coordC, updateC := setupConnectedAllWorkspaceUpdatesController(ctx, t, logger, fDNS)
1548+
coordC, updateC := setupConnectedAllWorkspaceUpdatesController(ctx, t, logger,
1549+
tailnet.WithDNS(fDNS, "testy"))
15511550

15521551
w1ID := testUUID(1)
15531552
w1a1ID := testUUID(1, 1)
@@ -1571,7 +1570,9 @@ func TestTunnelAllWorkspaceUpdatesController_DeleteAgent(t *testing.T) {
15711570

15721571
// DNS for w1a1
15731572
expectedDNS := map[dnsname.FQDN][]netip.Addr{
1574-
"w1a1.w1.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1573+
"w1a1.w1.testy.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1574+
"w1a1.w1.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1575+
"w1.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
15751576
}
15761577
dnsCall := testutil.RequireRecvCtx(ctx, t, fDNS.calls)
15771578
require.Equal(t, expectedDNS, dnsCall.hosts)
@@ -1601,7 +1602,9 @@ func TestTunnelAllWorkspaceUpdatesController_DeleteAgent(t *testing.T) {
16011602

16021603
// DNS contains only w1a2
16031604
expectedDNS = map[dnsname.FQDN][]netip.Addr{
1604-
"w1a2.w1.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0102::")},
1605+
"w1a2.w1.testy.coder.": {netip.MustParseAddr("fd60:627a:a42b:0102::")},
1606+
"w1a2.w1.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0102::")},
1607+
"w1.coder.": {netip.MustParseAddr("fd60:627a:a42b:0102::")},
16051608
}
16061609
dnsCall = testutil.RequireRecvCtx(ctx, t, fDNS.calls)
16071610
require.Equal(t, expectedDNS, dnsCall.hosts)
@@ -1619,7 +1622,9 @@ func TestTunnelAllWorkspaceUpdatesController_DNSError(t *testing.T) {
16191622
fDNS := newFakeDNSSetter(ctx, t)
16201623
fConn := &fakeCoordinatee{}
16211624
tsc := tailnet.NewTunnelSrcCoordController(logger, fConn)
1622-
uut := tailnet.NewTunnelAllWorkspaceUpdatesController(logger, tsc, fDNS)
1625+
uut := tailnet.NewTunnelAllWorkspaceUpdatesController(logger, tsc,
1626+
tailnet.WithDNS(fDNS, "testy"),
1627+
)
16231628

16241629
updateC := newFakeWorkspaceUpdateClient(ctx, t)
16251630
updateCW := uut.New(updateC)
@@ -1639,7 +1644,9 @@ func TestTunnelAllWorkspaceUpdatesController_DNSError(t *testing.T) {
16391644

16401645
// DNS for w1a1
16411646
expectedDNS := map[dnsname.FQDN][]netip.Addr{
1642-
"w1a1.w1.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1647+
"w1a1.w1.me.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1648+
"w1a1.w1.testy.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
1649+
"w1.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
16431650
}
16441651
dnsCall := testutil.RequireRecvCtx(ctx, t, fDNS.calls)
16451652
require.Equal(t, expectedDNS, dnsCall.hosts)
@@ -1746,7 +1753,7 @@ func TestTunnelAllWorkspaceUpdatesController_HandleErrors(t *testing.T) {
17461753

17471754
fConn := &fakeCoordinatee{}
17481755
tsc := tailnet.NewTunnelSrcCoordController(logger, fConn)
1749-
uut := tailnet.NewTunnelAllWorkspaceUpdatesController(logger, tsc, nil)
1756+
uut := tailnet.NewTunnelAllWorkspaceUpdatesController(logger, tsc)
17501757
updateC := newFakeWorkspaceUpdateClient(ctx, t)
17511758
updateCW := uut.New(updateC)
17521759

@@ -1780,18 +1787,16 @@ func (f fakeWorkspaceUpdatesController) New(client tailnet.WorkspaceUpdatesClien
17801787
}
17811788
select {
17821789
case <-f.ctx.Done():
1783-
f.t.Error("timed out waiting to send New call")
17841790
cw := newFakeCloserWaiter()
1785-
cw.errCh <- f.ctx.Err()
1791+
cw.errCh <- timeoutOnFakeErr
17861792
return cw
17871793
case f.calls <- call:
17881794
// OK
17891795
}
17901796
select {
17911797
case <-f.ctx.Done():
1792-
f.t.Error("timed out waiting to get New call response")
17931798
cw := newFakeCloserWaiter()
1794-
cw.errCh <- f.ctx.Err()
1799+
cw.errCh <- timeoutOnFakeErr
17951800
return cw
17961801
case resp := <-resps:
17971802
return resp

0 commit comments

Comments
 (0)