Skip to content

Commit 7d9f5ab

Browse files
authored
chore: add Coder service prefix to tailnet (#14943)
re: #14715 This PR introduces the Coder service prefix: `fd60:627a:a42b::/48` and refactors our existing code as calling the Tailscale service prefix explicitly (rather than implicitly). Removes the unused `Addresses` agent option. All clients today assume they can compute the Agent's IP address based on its UUID, so an agent started with a custom address would break things.
1 parent 68ec532 commit 7d9f5ab

16 files changed

+120
-75
lines changed

agent/agent.go

+4-11
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ type Options struct {
8282
SSHMaxTimeout time.Duration
8383
TailnetListenPort uint16
8484
Subsystems []codersdk.AgentSubsystem
85-
Addresses []netip.Prefix
8685
PrometheusRegistry *prometheus.Registry
8786
ReportMetadataInterval time.Duration
8887
ServiceBannerRefreshInterval time.Duration
@@ -180,7 +179,6 @@ func New(options Options) Agent {
180179
announcementBannersRefreshInterval: options.ServiceBannerRefreshInterval,
181180
sshMaxTimeout: options.SSHMaxTimeout,
182181
subsystems: options.Subsystems,
183-
addresses: options.Addresses,
184182
syscaller: options.Syscaller,
185183
modifiedProcs: options.ModifiedProcesses,
186184
processManagementTick: options.ProcessManagementTick,
@@ -250,7 +248,6 @@ type agent struct {
250248
lifecycleLastReportedIndex int // Keeps track of the last lifecycle state we successfully reported.
251249

252250
network *tailnet.Conn
253-
addresses []netip.Prefix
254251
statsReporter *statsReporter
255252
logSender *agentsdk.LogSender
256253

@@ -1112,15 +1109,11 @@ func (a *agent) updateCommandEnv(current []string) (updated []string, err error)
11121109
return updated, nil
11131110
}
11141111

1115-
func (a *agent) wireguardAddresses(agentID uuid.UUID) []netip.Prefix {
1116-
if len(a.addresses) == 0 {
1117-
return []netip.Prefix{
1118-
// This is the IP that should be used primarily.
1119-
netip.PrefixFrom(tailnet.IPFromUUID(agentID), 128),
1120-
}
1112+
func (*agent) wireguardAddresses(agentID uuid.UUID) []netip.Prefix {
1113+
return []netip.Prefix{
1114+
// This is the IP that should be used primarily.
1115+
tailnet.TailscaleServicePrefix.PrefixFromUUID(agentID),
11211116
}
1122-
1123-
return a.addresses
11241117
}
11251118

11261119
func (a *agent) trackGoroutine(fn func()) error {

agent/agent_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1880,7 +1880,7 @@ func TestAgent_UpdatedDERP(t *testing.T) {
18801880
// Setup a client connection.
18811881
newClientConn := func(derpMap *tailcfg.DERPMap, name string) *workspacesdk.AgentConn {
18821882
conn, err := tailnet.NewConn(&tailnet.Options{
1883-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
1883+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
18841884
DERPMap: derpMap,
18851885
Logger: logger.Named(name),
18861886
})
@@ -2372,7 +2372,7 @@ func setupAgent(t *testing.T, metadata agentsdk.Manifest, ptyTimeout time.Durati
23722372
_ = agnt.Close()
23732373
})
23742374
conn, err := tailnet.NewConn(&tailnet.Options{
2375-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
2375+
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.TailscaleServicePrefix.RandomAddr(), 128)},
23762376
DERPMap: metadata.DERPMap,
23772377
Logger: logger.Named("client"),
23782378
})

coderd/coderd_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func TestDERP(t *testing.T) {
8383
},
8484
},
8585
}
86-
w1IP := tailnet.IP()
86+
w1IP := tailnet.TailscaleServicePrefix.RandomAddr()
8787
w1, err := tailnet.NewConn(&tailnet.Options{
8888
Addresses: []netip.Prefix{netip.PrefixFrom(w1IP, 128)},
8989
Logger: logger.Named("w1"),
@@ -92,7 +92,7 @@ func TestDERP(t *testing.T) {
9292
require.NoError(t, err)
9393

9494
w2, err := tailnet.NewConn(&tailnet.Options{
95-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
95+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
9696
Logger: logger.Named("w2"),
9797
DERPMap: derpMap,
9898
})

coderd/tailnet.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func NewServerTailnet(
6161
) (*ServerTailnet, error) {
6262
logger = logger.Named("servertailnet")
6363
conn, err := tailnet.NewConn(&tailnet.Options{
64-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
64+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
6565
DERPForceWebSockets: derpForceWebSockets,
6666
Logger: logger,
6767
BlockEndpoints: blockEndpoints,
@@ -352,7 +352,7 @@ func (s *ServerTailnet) ReverseProxy(targetURL, dashboardURL *url.URL, agentID u
352352
// "localhost:port", causing connections to be shared across agents.
353353
tgt := *targetURL
354354
_, port, _ := net.SplitHostPort(tgt.Host)
355-
tgt.Host = net.JoinHostPort(tailnet.IPFromUUID(agentID).String(), port)
355+
tgt.Host = net.JoinHostPort(tailnet.TailscaleServicePrefix.AddrFromUUID(agentID).String(), port)
356356

357357
proxy := httputil.NewSingleHostReverseProxy(&tgt)
358358
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, theErr error) {

coderd/tailnet_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ func TestServerTailnet_ReverseProxy(t *testing.T) {
186186
// Ensure the reverse proxy director rewrites the url host to the agent's IP.
187187
rp.Director(req)
188188
assert.Equal(t,
189-
fmt.Sprintf("[%s]:%d", tailnet.IPFromUUID(a.id).String(), workspacesdk.AgentHTTPAPIServerPort),
189+
fmt.Sprintf("[%s]:%d",
190+
tailnet.TailscaleServicePrefix.AddrFromUUID(a.id).String(),
191+
workspacesdk.AgentHTTPAPIServerPort),
190192
req.URL.Host,
191193
)
192194
})

codersdk/workspacesdk/agentconn.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ type AgentConnOptions struct {
5151
}
5252

5353
func (c *AgentConn) agentAddress() netip.Addr {
54-
return tailnet.IPFromUUID(c.opts.AgentID)
54+
return tailnet.TailscaleServicePrefix.AddrFromUUID(c.opts.AgentID)
5555
}
5656

5757
// AwaitReachable waits for the agent to be reachable.

codersdk/workspacesdk/workspacesdk.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ func (c *Client) DialAgent(dialCtx context.Context, agentID uuid.UUID, options *
236236
CompressionMode: websocket.CompressionDisabled,
237237
})
238238

239-
ip := tailnet.IP()
239+
ip := tailnet.TailscaleServicePrefix.RandomAddr()
240240
var header http.Header
241241
if headerTransport, ok := c.client.HTTPClient.Transport.(*codersdk.HeaderTransport); ok {
242242
header = headerTransport.Header

enterprise/tailnet/pgcoord_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func TestPGCoordinatorSingle_AgentInvalidIP(t *testing.T) {
120120
defer agent.Close(ctx)
121121
agent.UpdateNode(&proto.Node{
122122
Addresses: []string{
123-
netip.PrefixFrom(agpl.IP(), 128).String(),
123+
agpl.TailscaleServicePrefix.RandomPrefix().String(),
124124
},
125125
PreferredDerp: 10,
126126
})
@@ -147,7 +147,7 @@ func TestPGCoordinatorSingle_AgentInvalidIPBits(t *testing.T) {
147147
defer agent.Close(ctx)
148148
agent.UpdateNode(&proto.Node{
149149
Addresses: []string{
150-
netip.PrefixFrom(agpl.IPFromUUID(agent.ID), 64).String(),
150+
netip.PrefixFrom(agpl.TailscaleServicePrefix.AddrFromUUID(agent.ID), 64).String(),
151151
},
152152
PreferredDerp: 10,
153153
})
@@ -174,7 +174,7 @@ func TestPGCoordinatorSingle_AgentValidIP(t *testing.T) {
174174
defer agent.Close(ctx)
175175
agent.UpdateNode(&proto.Node{
176176
Addresses: []string{
177-
netip.PrefixFrom(agpl.IPFromUUID(agent.ID), 128).String(),
177+
agpl.TailscaleServicePrefix.PrefixFromUUID(agent.ID).String(),
178178
},
179179
PreferredDerp: 10,
180180
})

tailnet/conn.go

+37-17
Original file line numberDiff line numberDiff line change
@@ -327,28 +327,48 @@ func NewConn(options *Options) (conn *Conn, err error) {
327327
return server, nil
328328
}
329329

330-
func maskUUID(uid uuid.UUID) uuid.UUID {
331-
// This is Tailscale's ephemeral service prefix. This can be changed easily
332-
// later-on, because all of our nodes are ephemeral.
333-
// fd7a:115c:a1e0
334-
uid[0] = 0xfd
335-
uid[1] = 0x7a
336-
uid[2] = 0x11
337-
uid[3] = 0x5c
338-
uid[4] = 0xa1
339-
uid[5] = 0xe0
330+
type ServicePrefix [6]byte
331+
332+
var (
333+
// TailscaleServicePrefix is the IPv6 prefix for all tailnet nodes since it was first added to
334+
// Coder. It is identical to the service prefix Tailscale.com uses. With the introduction of
335+
// CoderVPN, we would like to stop using the Tailscale prefix so that we don't conflict with
336+
// Tailscale if both are installed at the same time. However, there are a large number of agents
337+
// and clients using this prefix, so we need to carefully manage deprecation and eventual
338+
// removal.
339+
// fd7a:115c:a1e0:://48
340+
TailscaleServicePrefix ServicePrefix = [6]byte{0xfd, 0x7a, 0x11, 0x5c, 0xa1, 0xe0}
341+
// CoderServicePrefix is the Coder-specific IPv6 prefix for tailnet nodes, which we are in the
342+
// process of migrating to. It allows Coder to run alongside Tailscale without conflicts even
343+
// if both are set up as TUN interfaces into the OS (e.g. CoderVPN).
344+
// fd60:627a:a42b::/48
345+
CoderServicePrefix ServicePrefix = [6]byte{0xfd, 0x60, 0x62, 0x7a, 0xa4, 0x2b}
346+
)
347+
348+
// maskUUID returns a new UUID with the first 6 bytes changed to the ServicePrefix
349+
func (p ServicePrefix) maskUUID(uid uuid.UUID) uuid.UUID {
350+
copy(uid[:], p[:])
340351
return uid
341352
}
342353

343-
// IP generates a random IP with a static service prefix.
344-
func IP() netip.Addr {
345-
uid := maskUUID(uuid.New())
346-
return netip.AddrFrom16(uid)
354+
// RandomAddr returns a random IP address in the service prefix.
355+
func (p ServicePrefix) RandomAddr() netip.Addr {
356+
return netip.AddrFrom16(p.maskUUID(uuid.New()))
357+
}
358+
359+
// AddrFromUUID returns an IPv6 address corresponding to the given UUID in the service prefix.
360+
func (p ServicePrefix) AddrFromUUID(uid uuid.UUID) netip.Addr {
361+
return netip.AddrFrom16(p.maskUUID(uid))
362+
}
363+
364+
// PrefixFromUUID returns a single IPv6 /128 prefix corresponding to the given UUID.
365+
func (p ServicePrefix) PrefixFromUUID(uid uuid.UUID) netip.Prefix {
366+
return netip.PrefixFrom(p.AddrFromUUID(uid), 128)
347367
}
348368

349-
// IP generates a new IP from a UUID.
350-
func IPFromUUID(uid uuid.UUID) netip.Addr {
351-
return netip.AddrFrom16(maskUUID(uid))
369+
// RandomPrefix returns a single IPv6 /128 prefix within the service prefix.
370+
func (p ServicePrefix) RandomPrefix() netip.Prefix {
371+
return netip.PrefixFrom(p.RandomAddr(), 128)
352372
}
353373

354374
// Conn is an actively listening Wireguard connection.

tailnet/conn_test.go

+44-15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tailnet_test
33
import (
44
"context"
55
"net/netip"
6+
"strings"
67
"testing"
78
"time"
89

@@ -30,7 +31,7 @@ func TestTailnet(t *testing.T) {
3031
t.Parallel()
3132
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
3233
conn, err := tailnet.NewConn(&tailnet.Options{
33-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
34+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
3435
Logger: logger.Named("w1"),
3536
DERPMap: derpMap,
3637
})
@@ -42,7 +43,7 @@ func TestTailnet(t *testing.T) {
4243
t.Parallel()
4344
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
4445
ctx := testutil.Context(t, testutil.WaitLong)
45-
w1IP := tailnet.IP()
46+
w1IP := tailnet.TailscaleServicePrefix.RandomAddr()
4647
w1, err := tailnet.NewConn(&tailnet.Options{
4748
Addresses: []netip.Prefix{netip.PrefixFrom(w1IP, 128)},
4849
Logger: logger.Named("w1"),
@@ -51,7 +52,7 @@ func TestTailnet(t *testing.T) {
5152
require.NoError(t, err)
5253

5354
w2, err := tailnet.NewConn(&tailnet.Options{
54-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
55+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
5556
Logger: logger.Named("w2"),
5657
DERPMap: derpMap,
5758
})
@@ -106,7 +107,7 @@ func TestTailnet(t *testing.T) {
106107
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
107108
ctx := testutil.Context(t, testutil.WaitMedium)
108109

109-
w1IP := tailnet.IP()
110+
w1IP := tailnet.TailscaleServicePrefix.RandomAddr()
110111
derpMap := tailnettest.RunDERPOnlyWebSockets(t)
111112
w1, err := tailnet.NewConn(&tailnet.Options{
112113
Addresses: []netip.Prefix{netip.PrefixFrom(w1IP, 128)},
@@ -117,7 +118,7 @@ func TestTailnet(t *testing.T) {
117118
require.NoError(t, err)
118119

119120
w2, err := tailnet.NewConn(&tailnet.Options{
120-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
121+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
121122
Logger: logger.Named("w2"),
122123
DERPMap: derpMap,
123124
BlockEndpoints: true,
@@ -168,7 +169,7 @@ func TestTailnet(t *testing.T) {
168169
t.Parallel()
169170
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
170171
ctx := testutil.Context(t, testutil.WaitLong)
171-
w1IP := tailnet.IP()
172+
w1IP := tailnet.TailscaleServicePrefix.RandomAddr()
172173
w1, err := tailnet.NewConn(&tailnet.Options{
173174
Addresses: []netip.Prefix{netip.PrefixFrom(w1IP, 128)},
174175
Logger: logger.Named("w1"),
@@ -177,7 +178,7 @@ func TestTailnet(t *testing.T) {
177178
require.NoError(t, err)
178179

179180
w2, err := tailnet.NewConn(&tailnet.Options{
180-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
181+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
181182
Logger: logger.Named("w2"),
182183
DERPMap: derpMap,
183184
})
@@ -211,7 +212,7 @@ func TestTailnet(t *testing.T) {
211212
t.Parallel()
212213
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
213214
ctx := testutil.Context(t, testutil.WaitLong)
214-
w1IP := tailnet.IP()
215+
w1IP := tailnet.TailscaleServicePrefix.RandomAddr()
215216
w1, err := tailnet.NewConn(&tailnet.Options{
216217
Addresses: []netip.Prefix{netip.PrefixFrom(w1IP, 128)},
217218
Logger: logger.Named("w1"),
@@ -221,7 +222,7 @@ func TestTailnet(t *testing.T) {
221222
require.NoError(t, err)
222223

223224
w2, err := tailnet.NewConn(&tailnet.Options{
224-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
225+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
225226
Logger: logger.Named("w2"),
226227
DERPMap: derpMap,
227228
BlockEndpoints: true,
@@ -261,7 +262,7 @@ func TestConn_PreferredDERP(t *testing.T) {
261262
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
262263
derpMap, _ := tailnettest.RunDERPAndSTUN(t)
263264
conn, err := tailnet.NewConn(&tailnet.Options{
264-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
265+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
265266
Logger: logger.Named("w1"),
266267
DERPMap: derpMap,
267268
})
@@ -290,7 +291,7 @@ func TestConn_UpdateDERP(t *testing.T) {
290291
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
291292

292293
derpMap1, _ := tailnettest.RunDERPAndSTUN(t)
293-
ip := tailnet.IP()
294+
ip := tailnet.TailscaleServicePrefix.RandomAddr()
294295
conn, err := tailnet.NewConn(&tailnet.Options{
295296
Addresses: []netip.Prefix{netip.PrefixFrom(ip, 128)},
296297
Logger: logger.Named("w1"),
@@ -320,7 +321,7 @@ func TestConn_UpdateDERP(t *testing.T) {
320321

321322
// Connect from a different client.
322323
client1, err := tailnet.NewConn(&tailnet.Options{
323-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
324+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
324325
Logger: logger.Named("client1"),
325326
DERPMap: derpMap1,
326327
BlockEndpoints: true,
@@ -394,7 +395,7 @@ parentLoop:
394395
// Connect from a different different client with up-to-date derp map and
395396
// nodes.
396397
client2, err := tailnet.NewConn(&tailnet.Options{
397-
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
398+
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()},
398399
Logger: logger.Named("client2"),
399400
DERPMap: derpMap2,
400401
BlockEndpoints: true,
@@ -425,7 +426,7 @@ func TestConn_BlockEndpoints(t *testing.T) {
425426
derpMap, _ := tailnettest.RunDERPAndSTUN(t)
426427

427428
// Setup conn 1.
428-
ip1 := tailnet.IP()
429+
ip1 := tailnet.TailscaleServicePrefix.RandomAddr()
429430
conn1, err := tailnet.NewConn(&tailnet.Options{
430431
Addresses: []netip.Prefix{netip.PrefixFrom(ip1, 128)},
431432
Logger: logger.Named("w1"),
@@ -439,7 +440,7 @@ func TestConn_BlockEndpoints(t *testing.T) {
439440
}()
440441

441442
// Setup conn 2.
442-
ip2 := tailnet.IP()
443+
ip2 := tailnet.TailscaleServicePrefix.RandomAddr()
443444
conn2, err := tailnet.NewConn(&tailnet.Options{
444445
Addresses: []netip.Prefix{netip.PrefixFrom(ip2, 128)},
445446
Logger: logger.Named("w2"),
@@ -492,3 +493,31 @@ func stitch(t *testing.T, dst, src *tailnet.Conn) {
492493
assert.NoError(t, err)
493494
})
494495
}
496+
497+
func TestTailscaleServicePrefix(t *testing.T) {
498+
t.Parallel()
499+
a := tailnet.TailscaleServicePrefix.RandomAddr()
500+
require.True(t, strings.HasPrefix(a.String(), "fd7a:115c:a1e0"))
501+
p := tailnet.TailscaleServicePrefix.RandomPrefix()
502+
require.True(t, strings.HasPrefix(p.String(), "fd7a:115c:a1e0"))
503+
require.True(t, strings.HasSuffix(p.String(), "/128"))
504+
u := uuid.MustParse("aaaaaaaa-aaaa-aaaa-aaaa-123456789abc")
505+
a = tailnet.TailscaleServicePrefix.AddrFromUUID(u)
506+
require.Equal(t, "fd7a:115c:a1e0:aaaa:aaaa:1234:5678:9abc", a.String())
507+
p = tailnet.TailscaleServicePrefix.PrefixFromUUID(u)
508+
require.Equal(t, "fd7a:115c:a1e0:aaaa:aaaa:1234:5678:9abc/128", p.String())
509+
}
510+
511+
func TestCoderServicePrefix(t *testing.T) {
512+
t.Parallel()
513+
a := tailnet.CoderServicePrefix.RandomAddr()
514+
require.True(t, strings.HasPrefix(a.String(), "fd60:627a:a42b"))
515+
p := tailnet.CoderServicePrefix.RandomPrefix()
516+
require.True(t, strings.HasPrefix(p.String(), "fd60:627a:a42b"))
517+
require.True(t, strings.HasSuffix(p.String(), "/128"))
518+
u := uuid.MustParse("aaaaaaaa-aaaa-aaaa-aaaa-123456789abc")
519+
a = tailnet.CoderServicePrefix.AddrFromUUID(u)
520+
require.Equal(t, "fd60:627a:a42b:aaaa:aaaa:1234:5678:9abc", a.String())
521+
p = tailnet.CoderServicePrefix.PrefixFromUUID(u)
522+
require.Equal(t, "fd60:627a:a42b:aaaa:aaaa:1234:5678:9abc/128", p.String())
523+
}

0 commit comments

Comments
 (0)