Skip to content

Commit a1d5db7

Browse files
committed
feat: vpn uses WorkspaceHostnameSuffix for DNS names
1 parent 12dc086 commit a1d5db7

File tree

6 files changed

+247
-171
lines changed

6 files changed

+247
-171
lines changed

codersdk/workspacesdk/workspacesdk.go

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 3 deletions
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

Lines changed: 33 additions & 16 deletions
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

Lines changed: 40 additions & 31 deletions
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

Lines changed: 6 additions & 1 deletion
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy