@@ -15,8 +15,10 @@ import (
15
15
"github.com/pion/webrtc/v3"
16
16
"golang.org/x/net/proxy"
17
17
"golang.org/x/xerrors"
18
+ "inet.af/netaddr"
18
19
"nhooyr.io/websocket"
19
20
"nhooyr.io/websocket/wsjson"
21
+ "tailscale.com/tailcfg"
20
22
21
23
"cdr.dev/slog"
22
24
@@ -302,6 +304,67 @@ func (c *Client) WorkspaceAgentNodeBroker(ctx context.Context) (agent.NodeBroker
302
304
return & workspaceAgentNodeBroker {conn }, nil
303
305
}
304
306
307
+ func (c * Client ) DialWorkspaceAgentTailnet (ctx context.Context , agentID uuid.UUID , logger slog.Logger ) (agent.Conn , error ) {
308
+ res , err := c .Request (ctx , http .MethodGet , fmt .Sprintf ("/api/v2/workspaceagents/%s/derpmap" , agentID ), nil )
309
+ if err != nil {
310
+ return nil , err
311
+ }
312
+ defer res .Body .Close ()
313
+ if res .StatusCode != http .StatusOK {
314
+ return nil , readBodyAsError (res )
315
+ }
316
+ var derpMap tailcfg.DERPMap
317
+ err = json .NewDecoder (res .Body ).Decode (& derpMap )
318
+ if err != nil {
319
+ return nil , xerrors .Errorf ("decode derpmap: %w" , err )
320
+ }
321
+ ip := tailnet .IP ()
322
+
323
+ server , err := tailnet .New (& tailnet.Options {
324
+ Addresses : []netaddr.IPPrefix {netaddr .IPPrefixFrom (ip , 128 )},
325
+ DERPMap : & derpMap ,
326
+ Logger : logger ,
327
+ })
328
+ if err != nil {
329
+ return nil , xerrors .Errorf ("create tailnet: %w" , err )
330
+ }
331
+ server .SetNodeCallback (func (node * tailnet.Node ) {
332
+ res , err := c .Request (ctx , http .MethodPost , fmt .Sprintf ("/api/v2/workspaceagents/%s/node" , agentID ), node )
333
+ if err != nil {
334
+ logger .Error (ctx , "update node" , slog .Error (err ), slog .F ("node" , node ))
335
+ return
336
+ }
337
+ defer res .Body .Close ()
338
+ if res .StatusCode != http .StatusOK {
339
+ logger .Error (ctx , "update node" , slog .F ("status_code" , res .StatusCode ), slog .F ("node" , node ))
340
+ }
341
+ })
342
+ workspaceAgent , err := c .WorkspaceAgent (ctx , agentID )
343
+ if err != nil {
344
+ return nil , xerrors .Errorf ("get workspace agent: %w" , err )
345
+ }
346
+ ipRanges := make ([]netaddr.IPPrefix , 0 , len (workspaceAgent .IPAddresses ))
347
+ for _ , address := range workspaceAgent .IPAddresses {
348
+ ipRanges = append (ipRanges , netaddr .IPPrefixFrom (address , 128 ))
349
+ }
350
+ agentNode := & tailnet.Node {
351
+ Key : workspaceAgent .NodePublicKey ,
352
+ DiscoKey : workspaceAgent .DiscoPublicKey ,
353
+ PreferredDERP : workspaceAgent .PreferredDERP ,
354
+ Addresses : ipRanges ,
355
+ AllowedIPs : ipRanges ,
356
+ }
357
+ logger .Debug (ctx , "adding agent node" , slog .F ("node" , agentNode ))
358
+ err = server .UpdateNodes ([]* tailnet.Node {agentNode })
359
+ if err != nil {
360
+ return nil , xerrors .Errorf ("update nodes: %w" , err )
361
+ }
362
+ return & agent.TailnetConn {
363
+ Target : workspaceAgent .IPAddresses [0 ],
364
+ Server : server ,
365
+ }, nil
366
+ }
367
+
305
368
// DialWorkspaceAgent creates a connection to the specified resource.
306
369
func (c * Client ) DialWorkspaceAgent (ctx context.Context , agentID uuid.UUID , options * peer.ConnOptions ) (agent.Conn , error ) {
307
370
serverURL , err := c .URL .Parse (fmt .Sprintf ("/api/v2/workspaceagents/%s/dial" , agentID .String ()))
@@ -447,9 +510,9 @@ type workspaceAgentNodeBroker struct {
447
510
}
448
511
449
512
func (w * workspaceAgentNodeBroker ) Read (ctx context.Context ) (* tailnet.Node , error ) {
450
- var node * tailnet.Node
451
- err := wsjson .Read (ctx , w .conn , node )
452
- return node , err
513
+ var node tailnet.Node
514
+ err := wsjson .Read (ctx , w .conn , & node )
515
+ return & node , err
453
516
}
454
517
455
518
func (w * workspaceAgentNodeBroker ) Write (ctx context.Context , node * tailnet.Node ) error {
0 commit comments