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

Skip to content

Commit 1917ec0

Browse files
committed
feat: vpn uses WorkspaceHostnameSuffix for DNS names
1 parent 76cb7a7 commit 1917ec0

File tree

6 files changed

+247
-171
lines changed

6 files changed

+247
-171
lines changed

codersdk/workspacesdk/workspacesdk.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ type AgentConnectionInfo struct {
143143
DERPMap *tailcfg.DERPMap `json:"derp_map"`
144144
DERPForceWebSockets bool `json:"derp_force_websockets"`
145145
DisableDirectConnections bool `json:"disable_direct_connections"`
146-
HostnameSuffix string `json:"hostname_suffix"`
146+
HostnameSuffix string `json:"hostname_suffix,omitempty"`
147147
}
148148

149149
func (c *Client) AgentConnectionInfoGeneric(ctx context.Context) (AgentConnectionInfo, error) {

tailnet/conn.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,7 @@ func NewConn(options *Options) (conn *Conn, err error) {
357357
// A FQDN to be mapped to `tsaddr.CoderServiceIPv6`. This address can be used
358358
// when you want to know if Coder Connect is running, but are not trying to
359359
// connect to a specific known workspace.
360-
const IsCoderConnectEnabledFQDNString = "is.coder--connect--enabled--right--now.coder."
361-
362-
var IsCoderConnectEnabledFQDN, _ = dnsname.ToFQDN(IsCoderConnectEnabledFQDNString)
360+
const IsCoderConnectEnabledFmtString = "is.coder--connect--enabled--right--now.%s."
363361

364362
type ServicePrefix [6]byte
365363

tailnet/controllers.go

+33-16
Original file line numberDiff line numberDiff line change
@@ -864,11 +864,12 @@ func (r *basicResumeTokenRefresher) refresh() {
864864
}
865865

866866
type TunnelAllWorkspaceUpdatesController struct {
867-
coordCtrl *TunnelSrcCoordController
868-
dnsHostSetter DNSHostsSetter
869-
updateHandler UpdatesHandler
870-
ownerUsername string
871-
logger slog.Logger
867+
coordCtrl *TunnelSrcCoordController
868+
dnsHostSetter DNSHostsSetter
869+
dnsNameOptions DNSNameOptions
870+
updateHandler UpdatesHandler
871+
ownerUsername string
872+
logger slog.Logger
872873

873874
mu sync.Mutex
874875
updater *tunnelUpdater
@@ -883,37 +884,39 @@ type Workspace struct {
883884
agents map[uuid.UUID]*Agent
884885
}
885886

887+
type DNSNameOptions struct {
888+
Suffix string
889+
}
890+
886891
// updateDNSNames updates the DNS names for all agents in the workspace.
887892
// DNS hosts must be all lowercase, or the resolver won't be able to find them.
888893
// Usernames are globally unique & case-insensitive.
889894
// Workspace names are unique per-user & case-insensitive.
890895
// Agent names are unique per-workspace & case-insensitive.
891-
func (w *Workspace) updateDNSNames() error {
896+
func (w *Workspace) updateDNSNames(options DNSNameOptions) error {
892897
wsName := strings.ToLower(w.Name)
893898
username := strings.ToLower(w.ownerUsername)
894899
for id, a := range w.agents {
895900
agentName := strings.ToLower(a.Name)
896901
names := make(map[dnsname.FQDN][]netip.Addr)
897902
// TODO: technically, DNS labels cannot start with numbers, but the rules are often not
898903
// strictly enforced.
899-
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%s.%s.me.coder.", agentName, wsName))
904+
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%s.%s.me.%s.", agentName, wsName, options.Suffix))
900905
if err != nil {
901906
return err
902907
}
903908
names[fqdn] = []netip.Addr{CoderServicePrefix.AddrFromUUID(a.ID)}
904-
fqdn, err = dnsname.ToFQDN(fmt.Sprintf("%s.%s.%s.coder.", agentName, wsName, username))
909+
fqdn, err = dnsname.ToFQDN(fmt.Sprintf("%s.%s.%s.%s.", agentName, wsName, username, options.Suffix))
905910
if err != nil {
906911
return err
907912
}
908913
names[fqdn] = []netip.Addr{CoderServicePrefix.AddrFromUUID(a.ID)}
909914
if len(w.agents) == 1 {
910-
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%s.coder.", wsName))
915+
fqdn, err = dnsname.ToFQDN(fmt.Sprintf("%s.%s.", wsName, options.Suffix))
911916
if err != nil {
912917
return err
913918
}
914-
for _, a := range w.agents {
915-
names[fqdn] = []netip.Addr{CoderServicePrefix.AddrFromUUID(a.ID)}
916-
}
919+
names[fqdn] = []netip.Addr{CoderServicePrefix.AddrFromUUID(a.ID)}
917920
}
918921
a.Hosts = names
919922
w.agents[id] = a
@@ -950,6 +953,7 @@ func (t *TunnelAllWorkspaceUpdatesController) New(client WorkspaceUpdatesClient)
950953
logger: t.logger,
951954
coordCtrl: t.coordCtrl,
952955
dnsHostsSetter: t.dnsHostSetter,
956+
dnsNameOptions: t.dnsNameOptions,
953957
updateHandler: t.updateHandler,
954958
ownerUsername: t.ownerUsername,
955959
recvLoopDone: make(chan struct{}),
@@ -996,6 +1000,7 @@ type tunnelUpdater struct {
9961000
updateHandler UpdatesHandler
9971001
ownerUsername string
9981002
recvLoopDone chan struct{}
1003+
dnsNameOptions DNSNameOptions
9991004

10001005
sync.Mutex
10011006
workspaces map[uuid.UUID]*Workspace
@@ -1250,14 +1255,15 @@ func (t *tunnelUpdater) allAgentIDsLocked() []uuid.UUID {
12501255
func (t *tunnelUpdater) updateDNSNamesLocked() map[dnsname.FQDN][]netip.Addr {
12511256
names := make(map[dnsname.FQDN][]netip.Addr)
12521257
for _, w := range t.workspaces {
1253-
err := w.updateDNSNames()
1258+
err := w.updateDNSNames(t.dnsNameOptions)
12541259
if err != nil {
12551260
// This should never happen in production, because converting the FQDN only fails
12561261
// if names are too long, and we put strict length limits on agent, workspace, and user
12571262
// names.
12581263
t.logger.Critical(context.Background(),
12591264
"failed to include DNS name(s)",
12601265
slog.F("workspace_id", w.ID),
1266+
slog.F("suffix", t.dnsNameOptions.Suffix),
12611267
slog.Error(err))
12621268
}
12631269
for _, a := range w.agents {
@@ -1266,18 +1272,25 @@ func (t *tunnelUpdater) updateDNSNamesLocked() map[dnsname.FQDN][]netip.Addr {
12661272
}
12671273
}
12681274
}
1269-
names[IsCoderConnectEnabledFQDN] = []netip.Addr{tsaddr.CoderServiceIPv6()}
1275+
isCoderConnectEnabledFQDN, err := dnsname.ToFQDN(fmt.Sprintf(IsCoderConnectEnabledFmtString, t.dnsNameOptions.Suffix))
1276+
if err != nil {
1277+
t.logger.Critical(context.Background(),
1278+
"failed to include Coder Connect enabled DNS name", slog.F("suffix", t.dnsNameOptions.Suffix))
1279+
} else {
1280+
names[isCoderConnectEnabledFQDN] = []netip.Addr{tsaddr.CoderServiceIPv6()}
1281+
}
12701282
return names
12711283
}
12721284

12731285
type TunnelAllOption func(t *TunnelAllWorkspaceUpdatesController)
12741286

12751287
// WithDNS configures the tunnelAllWorkspaceUpdatesController to set DNS names for all workspaces
12761288
// and agents it learns about.
1277-
func WithDNS(d DNSHostsSetter, ownerUsername string) TunnelAllOption {
1289+
func WithDNS(d DNSHostsSetter, ownerUsername string, options DNSNameOptions) TunnelAllOption {
12781290
return func(t *TunnelAllWorkspaceUpdatesController) {
12791291
t.dnsHostSetter = d
12801292
t.ownerUsername = ownerUsername
1293+
t.dnsNameOptions = options
12811294
}
12821295
}
12831296

@@ -1293,7 +1306,11 @@ func WithHandler(h UpdatesHandler) TunnelAllOption {
12931306
func NewTunnelAllWorkspaceUpdatesController(
12941307
logger slog.Logger, c *TunnelSrcCoordController, opts ...TunnelAllOption,
12951308
) *TunnelAllWorkspaceUpdatesController {
1296-
t := &TunnelAllWorkspaceUpdatesController{logger: logger, coordCtrl: c}
1309+
t := &TunnelAllWorkspaceUpdatesController{
1310+
logger: logger,
1311+
coordCtrl: c,
1312+
dnsNameOptions: DNSNameOptions{"coder"},
1313+
}
12971314
for _, opt := range opts {
12981315
opt(t)
12991316
}

tailnet/controllers_test.go

+40-31
Original file line numberDiff line numberDiff line change
@@ -1522,7 +1522,7 @@ func TestTunnelAllWorkspaceUpdatesController_Initial(t *testing.T) {
15221522
fUH := newFakeUpdateHandler(ctx, t)
15231523
fDNS := newFakeDNSSetter(ctx, t)
15241524
coordC, updateC, updateCtrl := setupConnectedAllWorkspaceUpdatesController(ctx, t, logger,
1525-
tailnet.WithDNS(fDNS, "testy"),
1525+
tailnet.WithDNS(fDNS, "testy", tailnet.DNSNameOptions{Suffix: "mctest"}),
15261526
tailnet.WithHandler(fUH),
15271527
)
15281528

@@ -1562,16 +1562,19 @@ func TestTunnelAllWorkspaceUpdatesController_Initial(t *testing.T) {
15621562
w2a1IP := netip.MustParseAddr("fd60:627a:a42b:0201::")
15631563
w2a2IP := netip.MustParseAddr("fd60:627a:a42b:0202::")
15641564

1565+
expectedCoderConnectFQDN, err := dnsname.ToFQDN(fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString, "mctest"))
1566+
require.NoError(t, err)
1567+
15651568
// Also triggers setting DNS hosts
15661569
expectedDNS := map[dnsname.FQDN][]netip.Addr{
1567-
"w1a1.w1.me.coder.": {ws1a1IP},
1568-
"w2a1.w2.me.coder.": {w2a1IP},
1569-
"w2a2.w2.me.coder.": {w2a2IP},
1570-
"w1a1.w1.testy.coder.": {ws1a1IP},
1571-
"w2a1.w2.testy.coder.": {w2a1IP},
1572-
"w2a2.w2.testy.coder.": {w2a2IP},
1573-
"w1.coder.": {ws1a1IP},
1574-
tailnet.IsCoderConnectEnabledFQDNString: {tsaddr.CoderServiceIPv6()},
1570+
"w1a1.w1.me.mctest.": {ws1a1IP},
1571+
"w2a1.w2.me.mctest.": {w2a1IP},
1572+
"w2a2.w2.me.mctest.": {w2a2IP},
1573+
"w1a1.w1.testy.mctest.": {ws1a1IP},
1574+
"w2a1.w2.testy.mctest.": {w2a1IP},
1575+
"w2a2.w2.testy.mctest.": {w2a2IP},
1576+
"w1.mctest.": {ws1a1IP},
1577+
expectedCoderConnectFQDN: {tsaddr.CoderServiceIPv6()},
15751578
}
15761579
dnsCall := testutil.RequireRecvCtx(ctx, t, fDNS.calls)
15771580
require.Equal(t, expectedDNS, dnsCall.hosts)
@@ -1586,23 +1589,23 @@ func TestTunnelAllWorkspaceUpdatesController_Initial(t *testing.T) {
15861589
{
15871590
ID: w1a1ID, Name: "w1a1", WorkspaceID: w1ID,
15881591
Hosts: map[dnsname.FQDN][]netip.Addr{
1589-
"w1.coder.": {ws1a1IP},
1590-
"w1a1.w1.me.coder.": {ws1a1IP},
1591-
"w1a1.w1.testy.coder.": {ws1a1IP},
1592+
"w1.mctest.": {ws1a1IP},
1593+
"w1a1.w1.me.mctest.": {ws1a1IP},
1594+
"w1a1.w1.testy.mctest.": {ws1a1IP},
15921595
},
15931596
},
15941597
{
15951598
ID: w2a1ID, Name: "w2a1", WorkspaceID: w2ID,
15961599
Hosts: map[dnsname.FQDN][]netip.Addr{
1597-
"w2a1.w2.me.coder.": {w2a1IP},
1598-
"w2a1.w2.testy.coder.": {w2a1IP},
1600+
"w2a1.w2.me.mctest.": {w2a1IP},
1601+
"w2a1.w2.testy.mctest.": {w2a1IP},
15991602
},
16001603
},
16011604
{
16021605
ID: w2a2ID, Name: "w2a2", WorkspaceID: w2ID,
16031606
Hosts: map[dnsname.FQDN][]netip.Addr{
1604-
"w2a2.w2.me.coder.": {w2a2IP},
1605-
"w2a2.w2.testy.coder.": {w2a2IP},
1607+
"w2a2.w2.me.mctest.": {w2a2IP},
1608+
"w2a2.w2.testy.mctest.": {w2a2IP},
16061609
},
16071610
},
16081611
},
@@ -1634,7 +1637,7 @@ func TestTunnelAllWorkspaceUpdatesController_DeleteAgent(t *testing.T) {
16341637
fUH := newFakeUpdateHandler(ctx, t)
16351638
fDNS := newFakeDNSSetter(ctx, t)
16361639
coordC, updateC, updateCtrl := setupConnectedAllWorkspaceUpdatesController(ctx, t, logger,
1637-
tailnet.WithDNS(fDNS, "testy"),
1640+
tailnet.WithDNS(fDNS, "testy", tailnet.DNSNameOptions{Suffix: "coder"}),
16381641
tailnet.WithHandler(fUH),
16391642
)
16401643

@@ -1661,12 +1664,15 @@ func TestTunnelAllWorkspaceUpdatesController_DeleteAgent(t *testing.T) {
16611664
require.Equal(t, w1a1ID[:], coordCall.req.GetAddTunnel().GetId())
16621665
testutil.RequireSendCtx(ctx, t, coordCall.err, nil)
16631666

1667+
expectedCoderConnectFQDN, err := dnsname.ToFQDN(fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString, "coder"))
1668+
require.NoError(t, err)
1669+
16641670
// DNS for w1a1
16651671
expectedDNS := map[dnsname.FQDN][]netip.Addr{
1666-
"w1a1.w1.testy.coder.": {ws1a1IP},
1667-
"w1a1.w1.me.coder.": {ws1a1IP},
1668-
"w1.coder.": {ws1a1IP},
1669-
tailnet.IsCoderConnectEnabledFQDNString: {tsaddr.CoderServiceIPv6()},
1672+
"w1a1.w1.testy.coder.": {ws1a1IP},
1673+
"w1a1.w1.me.coder.": {ws1a1IP},
1674+
"w1.coder.": {ws1a1IP},
1675+
expectedCoderConnectFQDN: {tsaddr.CoderServiceIPv6()},
16701676
}
16711677
dnsCall := testutil.RequireRecvCtx(ctx, t, fDNS.calls)
16721678
require.Equal(t, expectedDNS, dnsCall.hosts)
@@ -1719,10 +1725,10 @@ func TestTunnelAllWorkspaceUpdatesController_DeleteAgent(t *testing.T) {
17191725

17201726
// DNS contains only w1a2
17211727
expectedDNS = map[dnsname.FQDN][]netip.Addr{
1722-
"w1a2.w1.testy.coder.": {ws1a2IP},
1723-
"w1a2.w1.me.coder.": {ws1a2IP},
1724-
"w1.coder.": {ws1a2IP},
1725-
tailnet.IsCoderConnectEnabledFQDNString: {tsaddr.CoderServiceIPv6()},
1728+
"w1a2.w1.testy.coder.": {ws1a2IP},
1729+
"w1a2.w1.me.coder.": {ws1a2IP},
1730+
"w1.coder.": {ws1a2IP},
1731+
expectedCoderConnectFQDN: {tsaddr.CoderServiceIPv6()},
17261732
}
17271733
dnsCall = testutil.RequireRecvCtx(ctx, t, fDNS.calls)
17281734
require.Equal(t, expectedDNS, dnsCall.hosts)
@@ -1779,7 +1785,7 @@ func TestTunnelAllWorkspaceUpdatesController_DNSError(t *testing.T) {
17791785
fConn := &fakeCoordinatee{}
17801786
tsc := tailnet.NewTunnelSrcCoordController(logger, fConn)
17811787
uut := tailnet.NewTunnelAllWorkspaceUpdatesController(logger, tsc,
1782-
tailnet.WithDNS(fDNS, "testy"),
1788+
tailnet.WithDNS(fDNS, "testy", tailnet.DNSNameOptions{Suffix: "coder"}),
17831789
)
17841790

17851791
updateC := newFakeWorkspaceUpdateClient(ctx, t)
@@ -1800,12 +1806,15 @@ func TestTunnelAllWorkspaceUpdatesController_DNSError(t *testing.T) {
18001806
upRecvCall := testutil.RequireRecvCtx(ctx, t, updateC.recv)
18011807
testutil.RequireSendCtx(ctx, t, upRecvCall.resp, initUp)
18021808

1809+
expectedCoderConnectFQDN, err := dnsname.ToFQDN(fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString, "coder"))
1810+
require.NoError(t, err)
1811+
18031812
// DNS for w1a1
18041813
expectedDNS := map[dnsname.FQDN][]netip.Addr{
1805-
"w1a1.w1.me.coder.": {ws1a1IP},
1806-
"w1a1.w1.testy.coder.": {ws1a1IP},
1807-
"w1.coder.": {ws1a1IP},
1808-
tailnet.IsCoderConnectEnabledFQDNString: {tsaddr.CoderServiceIPv6()},
1814+
"w1a1.w1.me.coder.": {ws1a1IP},
1815+
"w1a1.w1.testy.coder.": {ws1a1IP},
1816+
"w1.coder.": {ws1a1IP},
1817+
expectedCoderConnectFQDN: {tsaddr.CoderServiceIPv6()},
18091818
}
18101819
dnsCall := testutil.RequireRecvCtx(ctx, t, fDNS.calls)
18111820
require.Equal(t, expectedDNS, dnsCall.hosts)
@@ -1816,7 +1825,7 @@ func TestTunnelAllWorkspaceUpdatesController_DNSError(t *testing.T) {
18161825
testutil.RequireSendCtx(ctx, t, closeCall, io.EOF)
18171826

18181827
// error should be our initial DNS error
1819-
err := testutil.RequireRecvCtx(ctx, t, updateCW.Wait())
1828+
err = testutil.RequireRecvCtx(ctx, t, updateCW.Wait())
18201829
require.ErrorIs(t, err, dnsError)
18211830
}
18221831

vpn/client.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ func (*client) NewConn(initCtx context.Context, serverURL *url.URL, token string
107107
if err != nil {
108108
return nil, xerrors.Errorf("get connection info: %w", err)
109109
}
110+
// default to DNS suffix of "coder" if the server hasn't set it (might be too old).
111+
dnsNameOptions := tailnet.DNSNameOptions{Suffix: "coder"}
112+
if connInfo.HostnameSuffix != "" {
113+
dnsNameOptions.Suffix = connInfo.HostnameSuffix
114+
}
110115

111116
headers.Set(codersdk.SessionTokenHeader, token)
112117
dialer := workspacesdk.NewWebsocketDialer(options.Logger, rpcURL, &websocket.DialOptions{
@@ -148,7 +153,7 @@ func (*client) NewConn(initCtx context.Context, serverURL *url.URL, token string
148153
updatesCtrl := tailnet.NewTunnelAllWorkspaceUpdatesController(
149154
options.Logger,
150155
coordCtrl,
151-
tailnet.WithDNS(conn, me.Username),
156+
tailnet.WithDNS(conn, me.Username, dnsNameOptions),
152157
tailnet.WithHandler(options.UpdateHandler),
153158
)
154159
controller.WorkspaceUpdatesCtrl = updatesCtrl

0 commit comments

Comments
 (0)