Skip to content

Commit 0f8c2f5

Browse files
feat: Use Tailscale networking by default (#4003)
* feat: Use Tailscale networking by default Removal of WebRTC code will happen in another PR, but it felt dangerious to default and remove in a single commit. Ideally, we can release this version and collect final thoughts and feedback before a full commitment. * Remove UNIX forwarding Tailscale doesn't support this, and adding support for it shouldn't block our rollout. Customers can always forward over SSH. * Update cli/portforward_test.go Co-authored-by: Dean Sheather <dean@deansheather.com> Co-authored-by: Dean Sheather <dean@deansheather.com>
1 parent 478d49c commit 0f8c2f5

File tree

8 files changed

+28
-183
lines changed

8 files changed

+28
-183
lines changed

agent/agent_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ func TestAgent(t *testing.T) {
559559
DERPMap: derpMap,
560560
}, 0)
561561
defer conn.Close()
562-
res, err := conn.Speedtest(speedtest.Upload, speedtest.MinDuration)
562+
res, err := conn.Speedtest(speedtest.Upload, 250*time.Millisecond)
563563
require.NoError(t, err)
564564
t.Logf("%.2f MBits/s", res[len(res)-1].MBitsPerSecond())
565565
})

cli/agent_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestWorkspaceAgent(t *testing.T) {
4747
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
4848
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
4949

50-
cmd, _ := clitest.New(t, "agent", "--auth", "azure-instance-identity", "--agent-url", client.URL.String(), "--wireguard=false")
50+
cmd, _ := clitest.New(t, "agent", "--auth", "azure-instance-identity", "--agent-url", client.URL.String())
5151
ctx, cancelFunc := context.WithCancel(context.Background())
5252
defer cancelFunc()
5353
errC := make(chan error)
@@ -105,7 +105,7 @@ func TestWorkspaceAgent(t *testing.T) {
105105
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
106106
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
107107

108-
cmd, _ := clitest.New(t, "agent", "--auth", "aws-instance-identity", "--agent-url", client.URL.String(), "--wireguard=false")
108+
cmd, _ := clitest.New(t, "agent", "--auth", "aws-instance-identity", "--agent-url", client.URL.String())
109109
ctx, cancelFunc := context.WithCancel(context.Background())
110110
defer cancelFunc()
111111
errC := make(chan error)
@@ -163,7 +163,7 @@ func TestWorkspaceAgent(t *testing.T) {
163163
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
164164
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
165165

166-
cmd, _ := clitest.New(t, "agent", "--auth", "google-instance-identity", "--agent-url", client.URL.String(), "--wireguard=false")
166+
cmd, _ := clitest.New(t, "agent", "--auth", "google-instance-identity", "--agent-url", client.URL.String())
167167
ctx, cancelFunc := context.WithCancel(context.Background())
168168
defer cancelFunc()
169169
errC := make(chan error)

cli/configssh.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ func configSSH() *cobra.Command {
374374
cmd.Flags().BoolVarP(&skipProxyCommand, "skip-proxy-command", "", false, "Specifies whether the ProxyCommand option should be skipped. Useful for testing.")
375375
_ = cmd.Flags().MarkHidden("skip-proxy-command")
376376
cliflag.BoolVarP(cmd.Flags(), &usePreviousOpts, "use-previous-options", "", "CODER_SSH_USE_PREVIOUS_OPTIONS", false, "Specifies whether or not to keep options from previous run of config-ssh.")
377-
cliflag.BoolVarP(cmd.Flags(), &wireguard, "wireguard", "", "CODER_CONFIG_SSH_WIREGUARD", false, "Whether to use Wireguard for SSH tunneling.")
377+
cliflag.BoolVarP(cmd.Flags(), &wireguard, "wireguard", "", "CODER_CONFIG_SSH_WIREGUARD", true, "Whether to use Wireguard for SSH tunneling.")
378378
_ = cmd.Flags().MarkHidden("wireguard")
379379

380380
cliui.AllowSkipPrompt(cmd)

cli/portforward.go

+8-87
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"net"
77
"os"
88
"os/signal"
9-
"runtime"
109
"strconv"
1110
"strings"
1211
"sync"
@@ -24,10 +23,9 @@ import (
2423

2524
func portForward() *cobra.Command {
2625
var (
27-
tcpForwards []string // <port>:<port>
28-
udpForwards []string // <port>:<port>
29-
unixForwards []string // <path>:<path> OR <port>:<path>
30-
wireguard bool
26+
tcpForwards []string // <port>:<port>
27+
udpForwards []string // <port>:<port>
28+
wireguard bool
3129
)
3230
cmd := &cobra.Command{
3331
Use: "port-forward <workspace>",
@@ -43,14 +41,6 @@ func portForward() *cobra.Command {
4341
Description: "Port forward a single UDP port from port 9000 to port 9000 on your local machine",
4442
Command: "coder port-forward <workspace> --udp 9000",
4543
},
46-
example{
47-
Description: "Forward a Unix socket in the workspace to a local Unix socket",
48-
Command: "coder port-forward <workspace> --unix ./local.sock:~/remote.sock",
49-
},
50-
example{
51-
Description: "Forward a Unix socket in the workspace to a local TCP port",
52-
Command: "coder port-forward <workspace> --unix 8080:~/remote.sock",
53-
},
5444
example{
5545
Description: "Port forward multiple TCP ports and a UDP port",
5646
Command: "coder port-forward <workspace> --tcp 8080:8080 --tcp 9000:3000 --udp 5353:53",
@@ -60,7 +50,7 @@ func portForward() *cobra.Command {
6050
ctx, cancel := context.WithCancel(cmd.Context())
6151
defer cancel()
6252

63-
specs, err := parsePortForwards(tcpForwards, udpForwards, unixForwards)
53+
specs, err := parsePortForwards(tcpForwards, udpForwards)
6454
if err != nil {
6555
return xerrors.Errorf("parse port-forward specs: %w", err)
6656
}
@@ -165,8 +155,7 @@ func portForward() *cobra.Command {
165155

166156
cmd.Flags().StringArrayVarP(&tcpForwards, "tcp", "p", []string{}, "Forward a TCP port from the workspace to the local machine")
167157
cmd.Flags().StringArrayVar(&udpForwards, "udp", []string{}, "Forward a UDP port from the workspace to the local machine. The UDP connection has TCP-like semantics to support stateful UDP protocols")
168-
cmd.Flags().StringArrayVar(&unixForwards, "unix", []string{}, "Forward a Unix socket in the workspace to a local Unix socket or TCP port")
169-
cmd.Flags().BoolVarP(&wireguard, "wireguard", "", false, "Specifies whether to use wireguard networking or not.")
158+
cmd.Flags().BoolVarP(&wireguard, "wireguard", "", true, "Specifies whether to use wireguard networking or not.")
170159
_ = cmd.Flags().MarkHidden("wireguard")
171160
return cmd
172161
}
@@ -198,8 +187,6 @@ func listenAndPortForward(ctx context.Context, cmd *cobra.Command, conn agent.Co
198187
IP: net.ParseIP(host),
199188
Port: portInt,
200189
})
201-
case "unix":
202-
l, err = net.Listen(spec.listenNetwork, spec.listenAddress)
203190
default:
204191
return nil, xerrors.Errorf("unknown listen network %q", spec.listenNetwork)
205192
}
@@ -236,14 +223,14 @@ func listenAndPortForward(ctx context.Context, cmd *cobra.Command, conn agent.Co
236223
}
237224

238225
type portForwardSpec struct {
239-
listenNetwork string // tcp, udp, unix
226+
listenNetwork string // tcp, udp
240227
listenAddress string // <ip>:<port> or path
241228

242-
dialNetwork string // tcp, udp, unix
229+
dialNetwork string // tcp, udp
243230
dialAddress string // <ip>:<port> or path
244231
}
245232

246-
func parsePortForwards(tcpSpecs, udpSpecs, unixSpecs []string) ([]portForwardSpec, error) {
233+
func parsePortForwards(tcpSpecs, udpSpecs []string) ([]portForwardSpec, error) {
247234
specs := []portForwardSpec{}
248235

249236
for _, spec := range tcpSpecs {
@@ -274,29 +261,6 @@ func parsePortForwards(tcpSpecs, udpSpecs, unixSpecs []string) ([]portForwardSpe
274261
})
275262
}
276263

277-
for _, specStr := range unixSpecs {
278-
localPath, localTCP, remotePath, err := parseUnixUnix(specStr)
279-
if err != nil {
280-
return nil, xerrors.Errorf("failed to parse Unix port-forward specification %q: %w", specStr, err)
281-
}
282-
283-
spec := portForwardSpec{
284-
dialNetwork: "unix",
285-
dialAddress: remotePath,
286-
}
287-
if localPath == "" {
288-
spec.listenNetwork = "tcp"
289-
spec.listenAddress = fmt.Sprintf("127.0.0.1:%v", localTCP)
290-
} else {
291-
if runtime.GOOS == "windows" {
292-
return nil, xerrors.Errorf("Unix port-forwarding is not supported on Windows")
293-
}
294-
spec.listenNetwork = "unix"
295-
spec.listenAddress = localPath
296-
}
297-
specs = append(specs, spec)
298-
}
299-
300264
// Check for duplicate entries.
301265
locals := map[string]struct{}{}
302266
for _, spec := range specs {
@@ -322,15 +286,6 @@ func parsePort(in string) (uint16, error) {
322286
return uint16(port), nil
323287
}
324288

325-
func parseUnixPath(in string) (string, error) {
326-
path, err := agent.ExpandRelativeHomePath(strings.TrimSpace(in))
327-
if err != nil {
328-
return "", xerrors.Errorf("tidy path %q: %w", in, err)
329-
}
330-
331-
return path, nil
332-
}
333-
334289
func parsePortPort(in string) (local uint16, remote uint16, err error) {
335290
parts := strings.Split(in, ":")
336291
if len(parts) > 2 {
@@ -352,37 +307,3 @@ func parsePortPort(in string) (local uint16, remote uint16, err error) {
352307

353308
return local, remote, nil
354309
}
355-
356-
func parsePortOrUnixPath(in string) (string, uint16, error) {
357-
port, err := parsePort(in)
358-
if err == nil {
359-
return "", port, nil
360-
}
361-
362-
path, err := parseUnixPath(in)
363-
if err != nil {
364-
return "", 0, xerrors.Errorf("could not parse port or unix path %q: %w", in, err)
365-
}
366-
367-
return path, 0, nil
368-
}
369-
370-
func parseUnixUnix(in string) (string, uint16, string, error) {
371-
parts := strings.Split(in, ":")
372-
if len(parts) > 2 {
373-
return "", 0, "", xerrors.Errorf("invalid port-forward specification %q", in)
374-
}
375-
if len(parts) == 1 {
376-
// Duplicate the single part
377-
parts = append(parts, parts[0])
378-
}
379-
380-
localPath, localPort, err := parsePortOrUnixPath(parts[0])
381-
if err != nil {
382-
return "", 0, "", xerrors.Errorf("parse local part of spec %q: %w", in, err)
383-
}
384-
385-
// We don't really touch the remote path at all since it gets cleaned
386-
// up/expanded on the remote.
387-
return localPath, localPort, parts[1], nil
388-
}

cli/portforward_test.go

+7-89
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import (
66
"fmt"
77
"io"
88
"net"
9-
"path/filepath"
10-
"runtime"
119
"strings"
1210
"sync"
1311
"testing"
@@ -58,7 +56,7 @@ func TestPortForward(t *testing.T) {
5856
// setupRemote creates a "remote" listener to emulate a service in the
5957
// workspace.
6058
setupRemote func(t *testing.T) net.Listener
61-
// setupLocal returns an available port or Unix socket path that the
59+
// setupLocal returns an available port that the
6260
// port-forward command will listen on "locally". Returns the address
6361
// you pass to net.Dial, and the port/path you pass to `coder
6462
// port-forward`.
@@ -110,26 +108,6 @@ func TestPortForward(t *testing.T) {
110108
return l.Addr().String(), port
111109
},
112110
},
113-
{
114-
name: "Unix",
115-
network: "unix",
116-
flag: "--unix=%v:%v",
117-
setupRemote: func(t *testing.T) net.Listener {
118-
if runtime.GOOS == "windows" {
119-
t.Skip("Unix socket forwarding isn't supported on Windows")
120-
}
121-
122-
tmpDir := t.TempDir()
123-
l, err := net.Listen("unix", filepath.Join(tmpDir, "test.sock"))
124-
require.NoError(t, err, "create UDP listener")
125-
return l
126-
},
127-
setupLocal: func(t *testing.T) (string, string) {
128-
tmpDir := t.TempDir()
129-
path := filepath.Join(tmpDir, "test.sock")
130-
return path, path
131-
},
132-
},
133111
}
134112

135113
// Setup agent once to be shared between test-cases (avoid expensive
@@ -234,74 +212,16 @@ func TestPortForward(t *testing.T) {
234212
})
235213
}
236214

237-
// Test doing a TCP -> Unix forward.
238-
//nolint:paralleltest
239-
t.Run("TCP2Unix", func(t *testing.T) {
240-
var (
241-
// Find the TCP and Unix cases so we can use their setupLocal and
242-
// setupRemote methods respectively.
243-
tcpCase = cases[0]
244-
unixCase = cases[2]
245-
246-
// Setup remote Unix listener.
247-
p1 = setupTestListener(t, unixCase.setupRemote(t))
248-
)
249-
250-
// Create a flag that forwards from local TCP to Unix listener 1.
251-
// Notably this is a --unix flag.
252-
localAddress, localFlag := tcpCase.setupLocal(t)
253-
flag := fmt.Sprintf(unixCase.flag, localFlag, p1)
254-
255-
// Launch port-forward in a goroutine so we can start dialing
256-
// the "local" listener.
257-
cmd, root := clitest.New(t, "port-forward", workspace.Name, flag)
258-
clitest.SetupConfig(t, client, root)
259-
buf := newThreadSafeBuffer()
260-
cmd.SetOut(buf)
261-
ctx, cancel := context.WithCancel(context.Background())
262-
defer cancel()
263-
errC := make(chan error)
264-
go func() {
265-
errC <- cmd.ExecuteContext(ctx)
266-
}()
267-
waitForPortForwardReady(t, buf)
268-
269-
t.Parallel() // Port is reserved, enable parallel execution.
270-
271-
// Open two connections simultaneously and test them out of
272-
// sync.
273-
d := net.Dialer{Timeout: testutil.WaitShort}
274-
c1, err := d.DialContext(ctx, tcpCase.network, localAddress)
275-
require.NoError(t, err, "open connection 1 to 'local' listener")
276-
defer c1.Close()
277-
c2, err := d.DialContext(ctx, tcpCase.network, localAddress)
278-
require.NoError(t, err, "open connection 2 to 'local' listener")
279-
defer c2.Close()
280-
testDial(t, c2)
281-
testDial(t, c1)
282-
283-
cancel()
284-
err = <-errC
285-
require.ErrorIs(t, err, context.Canceled)
286-
})
287-
288-
// Test doing TCP, UDP and Unix at the same time.
215+
// Test doing TCP and UDP at the same time.
289216
//nolint:paralleltest
290217
t.Run("All", func(t *testing.T) {
291218
var (
292-
// These aren't fixed size because we exclude Unix on Windows.
293219
dials = []addr{}
294220
flags = []string{}
295221
)
296222

297223
// Start listeners and populate arrays with the cases.
298224
for _, c := range cases {
299-
if strings.HasPrefix(c.network, "unix") && runtime.GOOS == "windows" {
300-
// Unix isn't supported on Windows, but we can still
301-
// test other protocols together.
302-
continue
303-
}
304-
305225
p := setupTestListener(t, c.setupRemote(t))
306226

307227
localAddress, localFlag := c.setupLocal(t)
@@ -391,7 +311,7 @@ func runAgent(t *testing.T, client *codersdk.Client, userID uuid.UUID) ([]coders
391311
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
392312

393313
// Start workspace agent in a goroutine
394-
cmd, root := clitest.New(t, "agent", "--agent-token", agentToken, "--agent-url", client.URL.String(), "--wireguard=false")
314+
cmd, root := clitest.New(t, "agent", "--agent-token", agentToken, "--agent-url", client.URL.String())
395315
clitest.SetupConfig(t, client, root)
396316
errC := make(chan error)
397317
agentCtx, agentCancel := context.WithCancel(ctx)
@@ -412,7 +332,7 @@ func runAgent(t *testing.T, client *codersdk.Client, userID uuid.UUID) ([]coders
412332
}
413333

414334
// setupTestListener starts accepting connections and echoing a single packet.
415-
// Returns the listener and the listen port or Unix path.
335+
// Returns the listener and the listen port.
416336
func setupTestListener(t *testing.T, l net.Listener) string {
417337
t.Helper()
418338

@@ -444,11 +364,9 @@ func setupTestListener(t *testing.T, l net.Listener) string {
444364
}()
445365

446366
addr := l.Addr().String()
447-
if !strings.HasPrefix(l.Addr().Network(), "unix") {
448-
_, port, err := net.SplitHostPort(addr)
449-
require.NoErrorf(t, err, "split non-Unix listen path %q", addr)
450-
addr = port
451-
}
367+
_, port, err := net.SplitHostPort(addr)
368+
require.NoErrorf(t, err, "split listen path %q", addr)
369+
addr = port
452370

453371
return addr
454372
}

cli/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ func Server(newAPI func(*coderd.Options) *coderd.API) *cobra.Command {
812812
"Specifies an issuer URL to use for OIDC.")
813813
cliflag.StringArrayVarP(root.Flags(), &oidcScopes, "oidc-scopes", "", "CODER_OIDC_SCOPES", []string{oidc.ScopeOpenID, "profile", "email"},
814814
"Specifies scopes to grant when authenticating with OIDC.")
815-
cliflag.BoolVarP(root.Flags(), &tailscaleEnable, "tailscale", "", "CODER_TAILSCALE", false,
815+
cliflag.BoolVarP(root.Flags(), &tailscaleEnable, "tailscale", "", "CODER_TAILSCALE", true,
816816
"Specifies whether Tailscale networking is used for web applications and terminals.")
817817
_ = root.Flags().MarkHidden("tailscale")
818818
enableTelemetryByDefault := !isTest()

cli/ssh.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ func ssh() *cobra.Command {
221221
cliflag.BoolVarP(cmd.Flags(), &forwardAgent, "forward-agent", "A", "CODER_SSH_FORWARD_AGENT", false, "Specifies whether to forward the SSH agent specified in $SSH_AUTH_SOCK")
222222
cliflag.StringVarP(cmd.Flags(), &identityAgent, "identity-agent", "", "CODER_SSH_IDENTITY_AGENT", "", "Specifies which identity agent to use (overrides $SSH_AUTH_SOCK), forward agent must also be enabled")
223223
cliflag.DurationVarP(cmd.Flags(), &wsPollInterval, "workspace-poll-interval", "", "CODER_WORKSPACE_POLL_INTERVAL", workspacePollInterval, "Specifies how often to poll for workspace automated shutdown.")
224-
cliflag.BoolVarP(cmd.Flags(), &wireguard, "wireguard", "", "CODER_SSH_WIREGUARD", false, "Whether to use Wireguard for SSH tunneling.")
224+
cliflag.BoolVarP(cmd.Flags(), &wireguard, "wireguard", "", "CODER_SSH_WIREGUARD", true, "Whether to use Wireguard for SSH tunneling.")
225225
_ = cmd.Flags().MarkHidden("wireguard")
226226

227227
return cmd

coderd/coderd.go

+6
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ func New(options *Options) *API {
9595
if options.APIRateLimit == 0 {
9696
options.APIRateLimit = 512
9797
}
98+
if options.AgentStatsRefreshInterval == 0 {
99+
options.AgentStatsRefreshInterval = 10 * time.Minute
100+
}
101+
if options.MetricsCacheRefreshInterval == 0 {
102+
options.MetricsCacheRefreshInterval = time.Hour
103+
}
98104
if options.Authorizer == nil {
99105
var err error
100106
options.Authorizer, err = rbac.NewAuthorizer()

0 commit comments

Comments
 (0)