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

Skip to content

Commit 5b5bc1d

Browse files
authored
feat: Add local configuration option for DERP mapping (#3996)
This allows entirely airgapped geodistributed deployments of Coder!
1 parent 6e20f9c commit 5b5bc1d

File tree

5 files changed

+81
-30
lines changed

5 files changed

+81
-30
lines changed

cli/server.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ func Server(newAPI func(*coderd.Options) *coderd.API) *cobra.Command {
8181
derpServerRegionName string
8282
derpServerSTUNAddrs []string
8383
derpConfigURL string
84+
derpConfigPath string
8485
promEnabled bool
8586
promAddress string
8687
pprofEnabled bool
@@ -345,7 +346,7 @@ func Server(newAPI func(*coderd.Options) *coderd.API) *cobra.Command {
345346
if !derpServerEnabled {
346347
defaultRegion = nil
347348
}
348-
derpMap, err := tailnet.NewDERPMap(ctx, defaultRegion, derpServerSTUNAddrs, derpConfigURL)
349+
derpMap, err := tailnet.NewDERPMap(ctx, defaultRegion, derpServerSTUNAddrs, derpConfigURL, derpConfigPath)
349350
if err != nil {
350351
return xerrors.Errorf("create derp map: %w", err)
351352
}
@@ -753,6 +754,8 @@ func Server(newAPI func(*coderd.Options) *coderd.API) *cobra.Command {
753754
cliflag.StringVarP(root.Flags(), &address, "address", "a", "CODER_ADDRESS", "127.0.0.1:3000", "The address to serve the API and dashboard.")
754755
cliflag.StringVarP(root.Flags(), &derpConfigURL, "derp-config-url", "", "CODER_DERP_CONFIG_URL", "",
755756
"Specifies a URL to periodically fetch a DERP map. See: https://tailscale.com/kb/1118/custom-derp-servers/")
757+
cliflag.StringVarP(root.Flags(), &derpConfigPath, "derp-config-path", "", "CODER_DERP_CONFIG_PATH", "",
758+
"Specifies a path to read a DERP map from. See: https://tailscale.com/kb/1118/custom-derp-servers/")
756759
cliflag.BoolVarP(root.Flags(), &derpServerEnabled, "derp-server-enable", "", "CODER_DERP_SERVER_ENABLE", true, "Specifies whether to enable or disable the embedded DERP server.")
757760
cliflag.IntVarP(root.Flags(), &derpServerRegionID, "derp-server-region-id", "", "CODER_DERP_SERVER_REGION_ID", 999, "Specifies the region ID to use for the embedded DERP server.")
758761
cliflag.StringVarP(root.Flags(), &derpServerRegionCode, "derp-server-region-code", "", "CODER_DERP_SERVER_REGION_CODE", "coder", "Specifies the region code that is displayed in the Coder UI for the embedded DERP server.")
@@ -763,6 +766,7 @@ func Server(newAPI func(*coderd.Options) *coderd.API) *cobra.Command {
763766

764767
// Mark hidden while this feature is in testing!
765768
_ = root.Flags().MarkHidden("derp-config-url")
769+
_ = root.Flags().MarkHidden("derp-config-path")
766770
_ = root.Flags().MarkHidden("derp-server-enable")
767771
_ = root.Flags().MarkHidden("derp-server-region-id")
768772
_ = root.Flags().MarkHidden("derp-server-region-code")

cli/speedtest.go

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
tsspeedtest "tailscale.com/net/speedtest"
1212

1313
"cdr.dev/slog"
14+
"cdr.dev/slog/sloggers/sloghuman"
1415
"github.com/coder/coder/agent"
1516
"github.com/coder/coder/cli/cliflag"
1617
"github.com/coder/coder/cli/cliui"
@@ -51,36 +52,43 @@ func speedtest() *cobra.Command {
5152
if err != nil {
5253
return xerrors.Errorf("await agent: %w", err)
5354
}
54-
conn, err := client.DialWorkspaceAgentTailnet(ctx, slog.Logger{}, workspaceAgent.ID)
55+
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()))
56+
if cliflag.IsSetBool(cmd, varVerbose) {
57+
logger = logger.Leveled(slog.LevelDebug)
58+
}
59+
conn, err := client.DialWorkspaceAgentTailnet(ctx, logger, workspaceAgent.ID)
5560
if err != nil {
5661
return err
5762
}
5863
defer conn.Close()
59-
if direct {
60-
ticker := time.NewTicker(time.Second)
61-
defer ticker.Stop()
62-
for {
63-
select {
64-
case <-ctx.Done():
65-
return ctx.Err()
66-
case <-ticker.C:
67-
}
68-
dur, err := conn.Ping()
69-
if err != nil {
70-
continue
71-
}
72-
tc, _ := conn.(*agent.TailnetConn)
73-
status := tc.Status()
74-
if len(status.Peers()) != 1 {
75-
continue
76-
}
77-
peer := status.Peer[status.Peers()[0]]
78-
if peer.CurAddr == "" {
79-
cmd.Printf("Waiting for a direct connection... (%dms via %s)\n", dur.Milliseconds(), peer.Relay)
80-
continue
81-
}
82-
break
64+
ticker := time.NewTicker(time.Second)
65+
defer ticker.Stop()
66+
for {
67+
select {
68+
case <-ctx.Done():
69+
return ctx.Err()
70+
case <-ticker.C:
71+
}
72+
dur, err := conn.Ping()
73+
if err != nil {
74+
continue
75+
}
76+
tc, _ := conn.(*agent.TailnetConn)
77+
status := tc.Status()
78+
if len(status.Peers()) != 1 {
79+
continue
80+
}
81+
peer := status.Peer[status.Peers()[0]]
82+
if peer.CurAddr == "" && direct {
83+
cmd.Printf("Waiting for a direct connection... (%dms via %s)\n", dur.Milliseconds(), peer.Relay)
84+
continue
85+
}
86+
via := peer.Relay
87+
if via == "" {
88+
via = "direct"
8389
}
90+
cmd.Printf("%dms via %s\n", dur.Milliseconds(), via)
91+
break
8492
}
8593
dir := tsspeedtest.Download
8694
if reverse {

tailnet/conn.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ func (c *Conn) SetNodeCallback(callback func(node *Node)) {
268268
case <-c.closed:
269269
return
270270
case node := <-queue:
271+
c.logger.Debug(context.Background(), "send node callback", slog.F("node", node))
271272
callback(node)
272273
}
273274
}
@@ -299,6 +300,8 @@ func (c *Conn) SetNodeCallback(callback func(node *Node)) {
299300
func (c *Conn) SetDERPMap(derpMap *tailcfg.DERPMap) {
300301
c.mutex.Lock()
301302
defer c.mutex.Unlock()
303+
c.netMap.DERPMap = derpMap
304+
c.logger.Debug(context.Background(), "updating derp map", slog.F("derp_map", derpMap))
302305
c.wireguardEngine.SetDERPMap(derpMap)
303306
}
304307

@@ -340,6 +343,9 @@ func (c *Conn) UpdateNodes(nodes []*Node) error {
340343
existingNode, ok := peerMap[node.ID]
341344
if ok {
342345
peerNode.Created = existingNode.Created
346+
c.logger.Debug(context.Background(), "updating peer", slog.F("peer", peerNode))
347+
} else {
348+
c.logger.Debug(context.Background(), "adding peer", slog.F("peer", peerNode))
343349
}
344350
peerMap[node.ID] = peerNode
345351
}

tailnet/derpmap.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"net"
88
"net/http"
9+
"os"
910
"strconv"
1011

1112
"golang.org/x/xerrors"
@@ -14,7 +15,10 @@ import (
1415

1516
// NewDERPMap constructs a DERPMap from a set of STUN addresses and optionally a remote
1617
// URL to fetch a mapping from e.g. https://controlplane.tailscale.com/derpmap/default.
17-
func NewDERPMap(ctx context.Context, region *tailcfg.DERPRegion, stunAddrs []string, remoteURL string) (*tailcfg.DERPMap, error) {
18+
func NewDERPMap(ctx context.Context, region *tailcfg.DERPRegion, stunAddrs []string, remoteURL, localPath string) (*tailcfg.DERPMap, error) {
19+
if remoteURL != "" && localPath != "" {
20+
return nil, xerrors.New("a remote URL or local path must be specified, not both")
21+
}
1822
if region != nil {
1923
for index, stunAddr := range stunAddrs {
2024
host, rawPort, err := net.SplitHostPort(stunAddr)
@@ -53,6 +57,16 @@ func NewDERPMap(ctx context.Context, region *tailcfg.DERPRegion, stunAddrs []str
5357
return nil, xerrors.Errorf("fetch derpmap: %w", err)
5458
}
5559
}
60+
if localPath != "" {
61+
content, err := os.ReadFile(localPath)
62+
if err != nil {
63+
return nil, xerrors.Errorf("read derpmap from %q: %w", localPath, err)
64+
}
65+
err = json.Unmarshal(content, &derpMap)
66+
if err != nil {
67+
return nil, xerrors.Errorf("unmarshal derpmap: %w", err)
68+
}
69+
}
5670
if region != nil {
5771
_, conflicts := derpMap.Regions[region.RegionID]
5872
if conflicts {

tailnet/derpmap_test.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"encoding/json"
66
"net/http"
77
"net/http/httptest"
8+
"os"
9+
"path/filepath"
810
"testing"
911

1012
"github.com/stretchr/testify/require"
@@ -20,7 +22,7 @@ func TestNewDERPMap(t *testing.T) {
2022
derpMap, err := tailnet.NewDERPMap(context.Background(), &tailcfg.DERPRegion{
2123
RegionID: 1,
2224
Nodes: []*tailcfg.DERPNode{{}},
23-
}, []string{"stun.google.com:2345"}, "")
25+
}, []string{"stun.google.com:2345"}, "", "")
2426
require.NoError(t, err)
2527
require.Len(t, derpMap.Regions[1].Nodes, 2)
2628
})
@@ -37,7 +39,7 @@ func TestNewDERPMap(t *testing.T) {
3739
t.Cleanup(server.Close)
3840
derpMap, err := tailnet.NewDERPMap(context.Background(), &tailcfg.DERPRegion{
3941
RegionID: 2,
40-
}, []string{}, server.URL)
42+
}, []string{}, server.URL, "")
4143
require.NoError(t, err)
4244
require.Len(t, derpMap.Regions, 2)
4345
})
@@ -54,7 +56,24 @@ func TestNewDERPMap(t *testing.T) {
5456
t.Cleanup(server.Close)
5557
_, err := tailnet.NewDERPMap(context.Background(), &tailcfg.DERPRegion{
5658
RegionID: 1,
57-
}, []string{}, server.URL)
59+
}, []string{}, server.URL, "")
5860
require.Error(t, err)
5961
})
62+
t.Run("LocalPath", func(t *testing.T) {
63+
t.Parallel()
64+
localPath := filepath.Join(t.TempDir(), "derp.json")
65+
content, err := json.Marshal(&tailcfg.DERPMap{
66+
Regions: map[int]*tailcfg.DERPRegion{
67+
1: {},
68+
},
69+
})
70+
require.NoError(t, err)
71+
err = os.WriteFile(localPath, content, 0600)
72+
require.NoError(t, err)
73+
derpMap, err := tailnet.NewDERPMap(context.Background(), &tailcfg.DERPRegion{
74+
RegionID: 2,
75+
}, []string{}, "", localPath)
76+
require.NoError(t, err)
77+
require.Len(t, derpMap.Regions, 2)
78+
})
6079
}

0 commit comments

Comments
 (0)