diff --git a/cli/ping.go b/cli/ping.go index 1d51b6ac03..f69958666a 100644 --- a/cli/ping.go +++ b/cli/ping.go @@ -49,7 +49,13 @@ func (r *RootCmd) ping() *clibase.Cmd { logger = slog.Make(sloghuman.Sink(inv.Stdout)).Leveled(slog.LevelDebug) } - conn, err := client.DialWorkspaceAgent(ctx, workspaceAgent.ID, &codersdk.DialWorkspaceAgentOptions{Logger: logger}) + if r.disableDirect { + _, _ = fmt.Fprintln(inv.Stderr, "Direct connections disabled.") + } + conn, err := client.DialWorkspaceAgent(ctx, workspaceAgent.ID, &codersdk.DialWorkspaceAgentOptions{ + Logger: logger, + BlockEndpoints: r.disableDirect, + }) if err != nil { return err } diff --git a/cli/portforward.go b/cli/portforward.go index 9d3d63030d..0aa9576970 100644 --- a/cli/portforward.go +++ b/cli/portforward.go @@ -15,6 +15,9 @@ import ( "github.com/pion/udp" "golang.org/x/xerrors" + "cdr.dev/slog" + "cdr.dev/slog/sloggers/sloghuman" + "github.com/coder/coder/agent/agentssh" "github.com/coder/coder/cli/clibase" "github.com/coder/coder/cli/cliui" @@ -98,7 +101,18 @@ func (r *RootCmd) portForward() *clibase.Cmd { return xerrors.Errorf("await agent: %w", err) } - conn, err := client.DialWorkspaceAgent(ctx, workspaceAgent.ID, nil) + var logger slog.Logger + if r.verbose { + logger = slog.Make(sloghuman.Sink(inv.Stdout)).Leveled(slog.LevelDebug) + } + + if r.disableDirect { + _, _ = fmt.Fprintln(inv.Stderr, "Direct connections disabled.") + } + conn, err := client.DialWorkspaceAgent(ctx, workspaceAgent.ID, &codersdk.DialWorkspaceAgentOptions{ + Logger: logger, + BlockEndpoints: r.disableDirect, + }) if err != nil { return err } diff --git a/cli/root.go b/cli/root.go index 618f8827e1..9b63bebe4d 100644 --- a/cli/root.go +++ b/cli/root.go @@ -60,6 +60,7 @@ const ( varNoFeatureWarning = "no-feature-warning" varForceTty = "force-tty" varVerbose = "verbose" + varDisableDirect = "disable-direct-connections" notLoggedInMessage = "You are not logged in. Try logging in using 'coder login '." envNoVersionCheck = "CODER_NO_VERSION_WARNING" @@ -367,6 +368,13 @@ func (r *RootCmd) Command(subcommands []*clibase.Cmd) (*clibase.Cmd, error) { Value: clibase.BoolOf(&r.verbose), Group: globalGroup, }, + { + Flag: varDisableDirect, + Env: "CODER_DISABLE_DIRECT_CONNECTIONS", + Description: "Disable direct (P2P) connections to workspaces.", + Value: clibase.BoolOf(&r.disableDirect), + Group: globalGroup, + }, { Flag: "debug-http", Description: "Debug codersdk HTTP requests.", @@ -413,16 +421,17 @@ func isTest() bool { // RootCmd contains parameters and helpers useful to all commands. type RootCmd struct { - clientURL *url.URL - token string - globalConfig string - header []string - agentToken string - agentURL *url.URL - forceTTY bool - noOpen bool - verbose bool - debugHTTP bool + clientURL *url.URL + token string + globalConfig string + header []string + agentToken string + agentURL *url.URL + forceTTY bool + noOpen bool + verbose bool + disableDirect bool + debugHTTP bool noVersionCheck bool noFeatureWarning bool @@ -524,6 +533,7 @@ func (r *RootCmd) InitClient(client *codersdk.Client) clibase.MiddlewareFunc { client.PlainLogger = os.Stderr client.LogBodies = true } + client.DisableDirectConnections = r.disableDirect // We send these requests in parallel to minimize latency. var ( diff --git a/cli/speedtest.go b/cli/speedtest.go index c9d63bbf36..a60686710b 100644 --- a/cli/speedtest.go +++ b/cli/speedtest.go @@ -50,6 +50,7 @@ func (r *RootCmd) speedtest() *clibase.Cmd { if err != nil && !xerrors.Is(err, cliui.AgentStartError) { return xerrors.Errorf("await agent: %w", err) } + logger, ok := LoggerFromContext(ctx) if !ok { logger = slog.Make(sloghuman.Sink(inv.Stderr)) @@ -57,6 +58,10 @@ func (r *RootCmd) speedtest() *clibase.Cmd { if r.verbose { logger = logger.Leveled(slog.LevelDebug) } + + if r.disableDirect { + _, _ = fmt.Fprintln(inv.Stderr, "Direct connections disabled.") + } conn, err := client.DialWorkspaceAgent(ctx, workspaceAgent.ID, &codersdk.DialWorkspaceAgentOptions{ Logger: logger, }) diff --git a/cli/ssh.go b/cli/ssh.go index 761c521881..ba104fe15b 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -195,8 +195,12 @@ func (r *RootCmd) ssh() *clibase.Cmd { // We don't print the error because cliui.Agent does that for us. } + if r.disableDirect { + _, _ = fmt.Fprintln(inv.Stderr, "Direct connections disabled.") + } conn, err := client.DialWorkspaceAgent(ctx, workspaceAgent.ID, &codersdk.DialWorkspaceAgentOptions{ - Logger: logger, + Logger: logger, + BlockEndpoints: r.disableDirect, }) if err != nil { return xerrors.Errorf("dial agent: %w", err) diff --git a/cli/testdata/coder_--help.golden b/cli/testdata/coder_--help.golden index 381bc30ece..a1eb63de4a 100644 --- a/cli/testdata/coder_--help.golden +++ b/cli/testdata/coder_--help.golden @@ -51,6 +51,9 @@ variables or flags. --debug-options bool Print all options, how they're set, then exit. + --disable-direct-connections bool, $CODER_DISABLE_DIRECT_CONNECTIONS + Disable direct (P2P) connections to workspaces. + --global-config string, $CODER_CONFIG_DIR (default: ~/.config/coderv2) Path to the global `coder` config directory. diff --git a/cli/vscodessh.go b/cli/vscodessh.go index 363f0215a7..f302188829 100644 --- a/cli/vscodessh.go +++ b/cli/vscodessh.go @@ -17,6 +17,9 @@ import ( "tailscale.com/tailcfg" "tailscale.com/types/netlogtype" + "cdr.dev/slog" + "cdr.dev/slog/sloggers/sloghuman" + "github.com/coder/coder/cli/clibase" "github.com/coder/coder/codersdk" ) @@ -126,7 +129,18 @@ func (r *RootCmd) vscodeSSH() *clibase.Cmd { } } - agentConn, err := client.DialWorkspaceAgent(ctx, agent.ID, &codersdk.DialWorkspaceAgentOptions{}) + var logger slog.Logger + if r.verbose { + logger = slog.Make(sloghuman.Sink(inv.Stdout)).Leveled(slog.LevelDebug) + } + + if r.disableDirect { + _, _ = fmt.Fprintln(inv.Stderr, "Direct connections disabled.") + } + agentConn, err := client.DialWorkspaceAgent(ctx, agent.ID, &codersdk.DialWorkspaceAgentOptions{ + Logger: logger, + BlockEndpoints: r.disableDirect, + }) if err != nil { return xerrors.Errorf("dial workspace agent: %w", err) } diff --git a/codersdk/client.go b/codersdk/client.go index 19f765c097..dcd3a981a4 100644 --- a/codersdk/client.go +++ b/codersdk/client.go @@ -117,6 +117,11 @@ type Client struct { // Trace can be enabled to propagate tracing spans to the Coder API. // This is useful for tracking a request end-to-end. Trace bool + + // DisableDirectConnections forces any connections to workspaces to go + // through DERP, regardless of the BlockEndpoints setting on each + // connection. + DisableDirectConnections bool } // SessionToken returns the currently set token for the client. diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 24fac161da..4b5b636f11 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -193,7 +193,8 @@ func (c *Client) WorkspaceAgentConnectionInfo(ctx context.Context) (*WorkspaceAg // @typescript-ignore DialWorkspaceAgentOptions type DialWorkspaceAgentOptions struct { Logger slog.Logger - // BlockEndpoints forced a direct connection through DERP. + // BlockEndpoints forced a direct connection through DERP. The Client may + // have DisableDirect set which will override this value. BlockEndpoints bool } @@ -228,7 +229,7 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti DERPMap: connInfo.DERPMap, DERPHeader: &header, Logger: options.Logger, - BlockEndpoints: options.BlockEndpoints, + BlockEndpoints: c.DisableDirectConnections || options.BlockEndpoints, }) if err != nil { return nil, xerrors.Errorf("create tailnet: %w", err) diff --git a/docs/cli.md b/docs/cli.md index 9ff6f4596a..14c182f616 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -68,6 +68,15 @@ Coder — A tool for provisioning self-hosted development environments with Terr Print all options, how they're set, then exit. +### --disable-direct-connections + +| | | +| ----------- | ---------------------------------------------- | +| Type | bool | +| Environment | $CODER_DISABLE_DIRECT_CONNECTIONS | + +Disable direct (P2P) connections to workspaces. + ### --global-config | | | diff --git a/enterprise/cli/testdata/coder_--help.golden b/enterprise/cli/testdata/coder_--help.golden index 1a78134627..7fc6962ded 100644 --- a/enterprise/cli/testdata/coder_--help.golden +++ b/enterprise/cli/testdata/coder_--help.golden @@ -23,6 +23,9 @@ variables or flags. --debug-options bool Print all options, how they're set, then exit. + --disable-direct-connections bool, $CODER_DISABLE_DIRECT_CONNECTIONS + Disable direct (P2P) connections to workspaces. + --global-config string, $CODER_CONFIG_DIR (default: ~/.config/coderv2) Path to the global `coder` config directory.