From cb0f9642462ba044a720c8df51a5530a36aaddae Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 14 Jul 2023 13:46:13 +0200 Subject: [PATCH 01/12] make gen --- cli/ssh.go | 8 ++++++++ cli/testdata/coder_ssh_--help.golden | 3 +++ docs/cli/ssh.md | 9 +++++++++ 3 files changed, 20 insertions(+) diff --git a/cli/ssh.go b/cli/ssh.go index def41c091d6e0..0e180332c2416 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -53,6 +53,7 @@ func (r *RootCmd) ssh() *clibase.Cmd { waitEnum string noWait bool logDirPath string + remoteForward string ) client := new(codersdk.Client) cmd := &clibase.Cmd{ @@ -424,6 +425,13 @@ func (r *RootCmd) ssh() *clibase.Cmd { FlagShorthand: "l", Value: clibase.StringOf(&logDirPath), }, + { + Flag: "remote-forward", + Description: "Enable remote port forwarding.", + Env: "CODER_SSH_REMOTE_FORWARD", + FlagShorthand: "R", + Value: clibase.StringOf(&remoteForward), + }, } return cmd } diff --git a/cli/testdata/coder_ssh_--help.golden b/cli/testdata/coder_ssh_--help.golden index 4824f1c0dff95..9c5e7722906b9 100644 --- a/cli/testdata/coder_ssh_--help.golden +++ b/cli/testdata/coder_ssh_--help.golden @@ -27,6 +27,9 @@ Start a shell into a workspace behavior as non-blocking. DEPRECATED: Use --wait instead. + -R, --remote-forward string, $CODER_SSH_REMOTE_FORWARD + Enable remote port forwarding. + --stdio bool, $CODER_SSH_STDIO Specifies whether to emit SSH output over stdin/stdout. diff --git a/docs/cli/ssh.md b/docs/cli/ssh.md index bcdaae5a82c71..ac86ee53cbd71 100644 --- a/docs/cli/ssh.md +++ b/docs/cli/ssh.md @@ -57,6 +57,15 @@ Specify the directory containing SSH diagnostic log files. Enter workspace immediately after the agent has connected. This is the default if the template has configured the agent startup script behavior as non-blocking. +### -R, --remote-forward + +| | | +| ----------- | -------------------------------------- | +| Type | string | +| Environment | $CODER_SSH_REMOTE_FORWARD | + +Enable remote port forwarding. + ### --stdio | | | From a532f7bd9a4f218727d6f5108e15469b5f99595d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 14 Jul 2023 14:09:25 +0200 Subject: [PATCH 02/12] TODOs --- cli/ssh.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli/ssh.go b/cli/ssh.go index 0e180332c2416..7c098c48e043e 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -123,6 +123,8 @@ func (r *RootCmd) ssh() *clibase.Cmd { client.SetLogger(logger) } + // TODO Validate -R and --stdio + workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, codersdk.Me, inv.Args[0]) if err != nil { return err @@ -301,6 +303,8 @@ func (r *RootCmd) ssh() *clibase.Cmd { defer closer.Close() } + // TODO if sshForwardRemote + stdoutFile, validOut := inv.Stdout.(*os.File) stdinFile, validIn := inv.Stdin.(*os.File) if validOut && validIn && isatty.IsTerminal(stdoutFile.Fd()) { From 6df2a9bc2f13daa804967bd83a94840c975b0039 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 14 Jul 2023 17:38:08 +0200 Subject: [PATCH 03/12] basic implementation --- cli/ssh.go | 55 +++++++++++++++++++++++++++++++++++++++++----- cli/ssh_other.go | 2 +- cli/ssh_windows.go | 2 +- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/cli/ssh.go b/cli/ssh.go index 7c098c48e043e..8df72e89ce4d7 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -11,6 +11,8 @@ import ( "os" "os/exec" "path/filepath" + "regexp" + "strconv" "strings" "sync" "time" @@ -40,6 +42,8 @@ import ( var ( workspacePollInterval = time.Minute autostopNotifyCountdown = []time.Duration{30 * time.Minute} + + remoteForwardRegex = regexp.MustCompile(`^\d+:\w+:\d+$`) ) //nolint:gocyclo @@ -123,7 +127,15 @@ func (r *RootCmd) ssh() *clibase.Cmd { client.SetLogger(logger) } - // TODO Validate -R and --stdio + if remoteForward != "" { + isValid := remoteForwardRegex.MatchString(remoteForward) + if !isValid { + return xerrors.Errorf(`invalid format of remote-forward, expected: remote_port:local_address:local_port`) + } + if isValid && stdio { + return xerrors.Errorf(`remote-forward can't be enabled in the stdio mode`) + } + } workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, codersdk.Me, inv.Args[0]) if err != nil { @@ -303,7 +315,40 @@ func (r *RootCmd) ssh() *clibase.Cmd { defer closer.Close() } - // TODO if sshForwardRemote + if remoteForward != "" { + matches := remoteForwardRegex.FindStringSubmatch(remoteForward) + + // Format: + // remote_port:local_address:local_port + remotePort, err := strconv.Atoi(matches[1]) + if err != nil { + return xerrors.Errorf("remote port is invalid: %w", err) + } + localAddress, err := net.ResolveIPAddr("ip", matches[2]) + if err != nil { + return xerrors.Errorf("local address is invalid: %w", err) + } + localPort, err := strconv.Atoi(matches[3]) + if err != nil { + return xerrors.Errorf("local port is invalid: %w", err) + } + + localAddr := &net.TCPAddr{ + IP: localAddress.IP, + Port: localPort, + } + + remoteAddr := &net.TCPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: remotePort, + } + + closer, err := sshRemoteForward(ctx, inv.Stderr, sshClient, localAddr, remoteAddr) + if err != nil { + return xerrors.Errorf("ssh remote forward: %w", err) + } + defer closer.Close() + } stdoutFile, validOut := inv.Stdout.(*os.File) stdinFile, validIn := inv.Stdin.(*os.File) @@ -765,18 +810,18 @@ func remoteGPGAgentSocket(sshClient *gossh.Client) (string, error) { return string(bytes.TrimSpace(remoteSocket)), nil } -// cookieAddr is a special net.Addr accepted by sshForward() which includes a +// cookieAddr is a special net.Addr accepted by sshRemoteForward() which includes a // cookie which is written to the connection before forwarding. type cookieAddr struct { net.Addr cookie []byte } -// sshForwardRemote starts forwarding connections from a remote listener to a +// sshRemoteForward starts forwarding connections from a remote listener to a // local address via SSH in a goroutine. // // Accepts a `cookieAddr` as the local address. -func sshForwardRemote(ctx context.Context, stderr io.Writer, sshClient *gossh.Client, localAddr, remoteAddr net.Addr) (io.Closer, error) { +func sshRemoteForward(ctx context.Context, stderr io.Writer, sshClient *gossh.Client, localAddr, remoteAddr net.Addr) (io.Closer, error) { listener, err := sshClient.Listen(remoteAddr.Network(), remoteAddr.String()) if err != nil { return nil, xerrors.Errorf("listen on remote SSH address %s: %w", remoteAddr.String(), err) diff --git a/cli/ssh_other.go b/cli/ssh_other.go index 064436da31406..50c69dcf9d6d3 100644 --- a/cli/ssh_other.go +++ b/cli/ssh_other.go @@ -44,5 +44,5 @@ func forwardGPGAgent(ctx context.Context, stderr io.Writer, sshClient *gossh.Cli Net: "unix", } - return sshForwardRemote(ctx, stderr, sshClient, localAddr, remoteAddr) + return sshRemoteForward(ctx, stderr, sshClient, localAddr, remoteAddr) } diff --git a/cli/ssh_windows.go b/cli/ssh_windows.go index bf579c9df56b4..208687b03d034 100644 --- a/cli/ssh_windows.go +++ b/cli/ssh_windows.go @@ -101,5 +101,5 @@ func forwardGPGAgent(ctx context.Context, stderr io.Writer, sshClient *gossh.Cli Net: "unix", } - return sshForwardRemote(ctx, stderr, sshClient, localAddr, remoteAddr) + return sshRemoteForward(ctx, stderr, sshClient, localAddr, remoteAddr) } From 7506df9c5cee05634edc92ac5e41b1df52b04b8e Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 14 Jul 2023 18:09:29 +0200 Subject: [PATCH 04/12] Forwarding works --- cli/ssh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/ssh.go b/cli/ssh.go index 8df72e89ce4d7..fd7896fb8c0c3 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -43,7 +43,7 @@ var ( workspacePollInterval = time.Minute autostopNotifyCountdown = []time.Duration{30 * time.Minute} - remoteForwardRegex = regexp.MustCompile(`^\d+:\w+:\d+$`) + remoteForwardRegex = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) ) //nolint:gocyclo From ece557ebcca68491951a391e0c4c5942ab56aa2b Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 18 Jul 2023 14:14:13 +0200 Subject: [PATCH 05/12] Cancel condition --- cli/ssh.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cli/ssh.go b/cli/ssh.go index fd7896fb8c0c3..3199398eb5c0b 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -213,6 +213,7 @@ func (r *RootCmd) ssh() *clibase.Cmd { } defer conn.Close() conn.AwaitReachable(ctx) + stopPolling := tryPollWorkspaceAutostop(ctx, client, workspace) defer stopPolling() @@ -625,8 +626,15 @@ func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client * // of the CLI running simultaneously. func tryPollWorkspaceAutostop(ctx context.Context, client *codersdk.Client, workspace codersdk.Workspace) (stop func()) { lock := flock.New(filepath.Join(os.TempDir(), "coder-autostop-notify-"+workspace.ID.String())) - condition := notifyCondition(ctx, client, workspace.ID, lock) - return notify.Notify(condition, workspacePollInterval, autostopNotifyCountdown...) + conditionCtx, cancelCondition := context.WithCancel(ctx) + condition := notifyCondition(conditionCtx, client, workspace.ID, lock) + stopFunc := notify.Notify(condition, workspacePollInterval, autostopNotifyCountdown...) + return func() { + // With many "ssh" processes running, `lock.TryLockContext` can be hanging until the context canceled. + // Without this cancellation, a CLI process with failed remote-forward could be hanging indefinitely. + cancelCondition() + stopFunc() + } } // Notify the user if the workspace is due to shutdown. From 9f5e5003be7994a756286a792cbf381b5af96094 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 18 Jul 2023 14:33:30 +0200 Subject: [PATCH 06/12] Remote forward --- cli/remoteforward.go | 104 +++++++++++++++++++++++++++++++++++++++++++ cli/ssh.go | 87 ++---------------------------------- 2 files changed, 107 insertions(+), 84 deletions(-) create mode 100644 cli/remoteforward.go diff --git a/cli/remoteforward.go b/cli/remoteforward.go new file mode 100644 index 0000000000000..6583b249d29e2 --- /dev/null +++ b/cli/remoteforward.go @@ -0,0 +1,104 @@ +package cli + +import ( + "context" + "fmt" + "io" + "net" + "regexp" + "strconv" + + gossh "golang.org/x/crypto/ssh" + "golang.org/x/xerrors" + + "github.com/coder/coder/agent/agentssh" +) + +// cookieAddr is a special net.Addr accepted by sshRemoteForward() which includes a +// cookie which is written to the connection before forwarding. +type cookieAddr struct { + net.Addr + cookie []byte +} + +var remoteForwardRegex = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) + +func validateRemoteForward(flag string) bool { + return remoteForwardRegex.MatchString(flag) +} + +func parseRemoteForward(flag string) (net.Addr, net.Addr, error) { + matches := remoteForwardRegex.FindStringSubmatch(flag) + + // Format: + // remote_port:local_address:local_port + remotePort, err := strconv.Atoi(matches[1]) + if err != nil { + return nil, nil, xerrors.Errorf("remote port is invalid: %w", err) + } + localAddress, err := net.ResolveIPAddr("ip", matches[2]) + if err != nil { + return nil, nil, xerrors.Errorf("local address is invalid: %w", err) + } + localPort, err := strconv.Atoi(matches[3]) + if err != nil { + return nil, nil, xerrors.Errorf("local port is invalid: %w", err) + } + + localAddr := &net.TCPAddr{ + IP: localAddress.IP, + Port: localPort, + } + + remoteAddr := &net.TCPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: remotePort, + } + return localAddr, remoteAddr, nil +} + +// sshRemoteForward starts forwarding connections from a remote listener to a +// local address via SSH in a goroutine. +// +// Accepts a `cookieAddr` as the local address. +func sshRemoteForward(ctx context.Context, stderr io.Writer, sshClient *gossh.Client, localAddr, remoteAddr net.Addr) (io.Closer, error) { + listener, err := sshClient.Listen(remoteAddr.Network(), remoteAddr.String()) + if err != nil { + return nil, xerrors.Errorf("listen on remote SSH address %s: %w", remoteAddr.String(), err) + } + + go func() { + for { + remoteConn, err := listener.Accept() + if err != nil { + if ctx.Err() == nil { + _, _ = fmt.Fprintf(stderr, "Accept SSH listener connection: %+v\n", err) + } + return + } + + go func() { + defer remoteConn.Close() + + localConn, err := net.Dial(localAddr.Network(), localAddr.String()) + if err != nil { + _, _ = fmt.Fprintf(stderr, "Dial local address %s: %+v\n", localAddr.String(), err) + return + } + defer localConn.Close() + + if c, ok := localAddr.(cookieAddr); ok { + _, err = localConn.Write(c.cookie) + if err != nil { + _, _ = fmt.Fprintf(stderr, "Write cookie to local connection: %+v\n", err) + return + } + } + + agentssh.Bicopy(ctx, localConn, remoteConn) + }() + } + }() + + return listener, nil +} diff --git a/cli/ssh.go b/cli/ssh.go index 3199398eb5c0b..b78e4664f4091 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -6,13 +6,10 @@ import ( "errors" "fmt" "io" - "net" "net/url" "os" "os/exec" "path/filepath" - "regexp" - "strconv" "strings" "sync" "time" @@ -29,7 +26,6 @@ import ( "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" "github.com/coder/coder/coderd/autobuild/notify" @@ -42,8 +38,6 @@ import ( var ( workspacePollInterval = time.Minute autostopNotifyCountdown = []time.Duration{30 * time.Minute} - - remoteForwardRegex = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) ) //nolint:gocyclo @@ -128,7 +122,7 @@ func (r *RootCmd) ssh() *clibase.Cmd { } if remoteForward != "" { - isValid := remoteForwardRegex.MatchString(remoteForward) + isValid := validateRemoteForward(remoteForward) if !isValid { return xerrors.Errorf(`invalid format of remote-forward, expected: remote_port:local_address:local_port`) } @@ -317,31 +311,9 @@ func (r *RootCmd) ssh() *clibase.Cmd { } if remoteForward != "" { - matches := remoteForwardRegex.FindStringSubmatch(remoteForward) - - // Format: - // remote_port:local_address:local_port - remotePort, err := strconv.Atoi(matches[1]) - if err != nil { - return xerrors.Errorf("remote port is invalid: %w", err) - } - localAddress, err := net.ResolveIPAddr("ip", matches[2]) - if err != nil { - return xerrors.Errorf("local address is invalid: %w", err) - } - localPort, err := strconv.Atoi(matches[3]) + localAddr, remoteAddr, err := parseRemoteForward(remoteForward) if err != nil { - return xerrors.Errorf("local port is invalid: %w", err) - } - - localAddr := &net.TCPAddr{ - IP: localAddress.IP, - Port: localPort, - } - - remoteAddr := &net.TCPAddr{ - IP: net.ParseIP("127.0.0.1"), - Port: remotePort, + return err } closer, err := sshRemoteForward(ctx, inv.Stderr, sshClient, localAddr, remoteAddr) @@ -817,56 +789,3 @@ func remoteGPGAgentSocket(sshClient *gossh.Client) (string, error) { return string(bytes.TrimSpace(remoteSocket)), nil } - -// cookieAddr is a special net.Addr accepted by sshRemoteForward() which includes a -// cookie which is written to the connection before forwarding. -type cookieAddr struct { - net.Addr - cookie []byte -} - -// sshRemoteForward starts forwarding connections from a remote listener to a -// local address via SSH in a goroutine. -// -// Accepts a `cookieAddr` as the local address. -func sshRemoteForward(ctx context.Context, stderr io.Writer, sshClient *gossh.Client, localAddr, remoteAddr net.Addr) (io.Closer, error) { - listener, err := sshClient.Listen(remoteAddr.Network(), remoteAddr.String()) - if err != nil { - return nil, xerrors.Errorf("listen on remote SSH address %s: %w", remoteAddr.String(), err) - } - - go func() { - for { - remoteConn, err := listener.Accept() - if err != nil { - if ctx.Err() == nil { - _, _ = fmt.Fprintf(stderr, "Accept SSH listener connection: %+v\n", err) - } - return - } - - go func() { - defer remoteConn.Close() - - localConn, err := net.Dial(localAddr.Network(), localAddr.String()) - if err != nil { - _, _ = fmt.Fprintf(stderr, "Dial local address %s: %+v\n", localAddr.String(), err) - return - } - defer localConn.Close() - - if c, ok := localAddr.(cookieAddr); ok { - _, err = localConn.Write(c.cookie) - if err != nil { - _, _ = fmt.Fprintf(stderr, "Write cookie to local connection: %+v\n", err) - return - } - } - - agentssh.Bicopy(ctx, localConn, remoteConn) - }() - } - }() - - return listener, nil -} From 0b53a5c0832afb5c3199e1c75703435350220451 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 18 Jul 2023 16:22:36 +0200 Subject: [PATCH 07/12] httptest --- cli/ssh_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 3749a55b1a863..e933839e9ba48 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -10,6 +10,8 @@ import ( "fmt" "io" "net" + "net/http" + "net/http/httptest" "os" "os/exec" "path/filepath" @@ -408,6 +410,58 @@ func TestSSH(t *testing.T) { <-cmdDone }) + t.Run("RemoteForward", func(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Test not supported on windows") + } + + t.Parallel() + + httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("hello world")) + })) + defer httpServer.Close() + + client, workspace, agentToken := setupWorkspaceForAgent(t, nil) + + agentClient := agentsdk.New(client.URL) + agentClient.SetSessionToken(agentToken) + agentCloser := agent.New(agent.Options{ + Client: agentClient, + Logger: slogtest.Make(t, nil).Named("agent"), + }) + defer agentCloser.Close() + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + inv, root := clitest.New(t, + "ssh", + workspace.Name, + "--remote-forward", + "8222:"+httpServer.Listener.Addr().String(), + ) + clitest.SetupConfig(t, client, root) + pty := ptytest.New(t).Attach(inv) + inv.Stderr = pty.Output() + cmdDone := tGo(t, func() { + err := inv.WithContext(ctx).Run() + assert.NoError(t, err, "ssh command failed") + }) + + // Wait for the prompt or any output really to indicate the command has + // started and accepting input on stdin. + _ = pty.Peek(ctx, 1) + + // Download the test page + pty.WriteLine("curl localhost:8222") + pty.ExpectMatch("hello world") + + // And we're done. + pty.WriteLine("exit") + <-cmdDone + }) + t.Run("FileLogging", func(t *testing.T) { t.Parallel() From 5e87bc6fb604b339a30a0c82b0eabfd773f55359 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 19 Jul 2023 12:28:09 +0200 Subject: [PATCH 08/12] format --- cli/remoteforward.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/remoteforward.go b/cli/remoteforward.go index 6583b249d29e2..9e53669a7ee47 100644 --- a/cli/remoteforward.go +++ b/cli/remoteforward.go @@ -21,6 +21,8 @@ type cookieAddr struct { cookie []byte } +// Format: +// remote_port:local_address:local_port var remoteForwardRegex = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) func validateRemoteForward(flag string) bool { @@ -30,8 +32,6 @@ func validateRemoteForward(flag string) bool { func parseRemoteForward(flag string) (net.Addr, net.Addr, error) { matches := remoteForwardRegex.FindStringSubmatch(flag) - // Format: - // remote_port:local_address:local_port remotePort, err := strconv.Atoi(matches[1]) if err != nil { return nil, nil, xerrors.Errorf("remote port is invalid: %w", err) From f4d5c682415a6762e6d8ef3f5313b2c4c42358cd Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 19 Jul 2023 12:31:16 +0200 Subject: [PATCH 09/12] format --- cli/ssh.go | 2 +- cli/testdata/coder_ssh_--help.golden | 2 +- docs/cli/ssh.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/ssh.go b/cli/ssh.go index b78e4664f4091..4bf6f021b4881 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -449,7 +449,7 @@ func (r *RootCmd) ssh() *clibase.Cmd { }, { Flag: "remote-forward", - Description: "Enable remote port forwarding.", + Description: "Enable remote port forwarding (remote_port:local_address:local_port).", Env: "CODER_SSH_REMOTE_FORWARD", FlagShorthand: "R", Value: clibase.StringOf(&remoteForward), diff --git a/cli/testdata/coder_ssh_--help.golden b/cli/testdata/coder_ssh_--help.golden index 9c5e7722906b9..74ce84d2b677a 100644 --- a/cli/testdata/coder_ssh_--help.golden +++ b/cli/testdata/coder_ssh_--help.golden @@ -28,7 +28,7 @@ Start a shell into a workspace DEPRECATED: Use --wait instead. -R, --remote-forward string, $CODER_SSH_REMOTE_FORWARD - Enable remote port forwarding. + Enable remote port forwarding (remote_port:local_address:local_port). --stdio bool, $CODER_SSH_STDIO Specifies whether to emit SSH output over stdin/stdout. diff --git a/docs/cli/ssh.md b/docs/cli/ssh.md index ac86ee53cbd71..784ba3674f74c 100644 --- a/docs/cli/ssh.md +++ b/docs/cli/ssh.md @@ -64,7 +64,7 @@ Enter workspace immediately after the agent has connected. This is the default i | Type | string | | Environment | $CODER_SSH_REMOTE_FORWARD | -Enable remote port forwarding. +Enable remote port forwarding (remote_port:local_address:local_port). ### --stdio From 9ae8f1d4c4a04d50b9e08348c4210d2a6e6d80e8 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 19 Jul 2023 16:14:06 +0200 Subject: [PATCH 10/12] WIP --- cli/portforward.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/portforward.go b/cli/portforward.go index 01dc4a637ebb9..8b9fb74cff6e8 100644 --- a/cli/portforward.go +++ b/cli/portforward.go @@ -32,7 +32,7 @@ func (r *RootCmd) portForward() *clibase.Cmd { client := new(codersdk.Client) cmd := &clibase.Cmd{ Use: "port-forward ", - Short: "Forward ports from machine to a workspace", + Short: "Forward ports from a workspace to the local machine", Aliases: []string{"tunnel"}, Long: formatExamples( example{ From 3e77af3cb2891ebe9a680e46df24b656c2cbf489 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 20 Jul 2023 11:15:26 +0200 Subject: [PATCH 11/12] Add notice about ssh -R --- cli/portforward.go | 2 +- cli/testdata/coder_--help.golden | 4 +- cli/testdata/coder_port-forward_--help.golden | 3 +- docs/cli.md | 68 +++++++++---------- docs/cli/port-forward.md | 2 +- docs/manifest.json | 2 +- 6 files changed, 42 insertions(+), 39 deletions(-) diff --git a/cli/portforward.go b/cli/portforward.go index 8b9fb74cff6e8..c3aa21bc08cc4 100644 --- a/cli/portforward.go +++ b/cli/portforward.go @@ -32,7 +32,7 @@ func (r *RootCmd) portForward() *clibase.Cmd { client := new(codersdk.Client) cmd := &clibase.Cmd{ Use: "port-forward ", - Short: "Forward ports from a workspace to the local machine", + Short: `Forward ports from a workspace to the local machine. Please note that to achieve reverse port forwarding, utilize the "coder ssh -R" command.`, Aliases: []string{"tunnel"}, Long: formatExamples( example{ diff --git a/cli/testdata/coder_--help.golden b/cli/testdata/coder_--help.golden index 7ef7d52f0b746..05b57ac10c835 100644 --- a/cli/testdata/coder_--help.golden +++ b/cli/testdata/coder_--help.golden @@ -21,7 +21,9 @@ Coder v0.0.0-devel — A tool for provisioning self-hosted development environme logout Unauthenticate your local session netcheck Print network debug information for DERP and STUN ping Ping a workspace - port-forward Forward ports from machine to a workspace + port-forward Forward ports from a workspace to the local machine. + Please note that to achieve reverse port forwarding, + utilize the "coder ssh -R" command. publickey Output your Coder public key used for Git operations rename Rename a workspace reset-password Directly connect to the database to reset a user's diff --git a/cli/testdata/coder_port-forward_--help.golden b/cli/testdata/coder_port-forward_--help.golden index 6efd95c0e89cf..c668e9b767ee1 100644 --- a/cli/testdata/coder_port-forward_--help.golden +++ b/cli/testdata/coder_port-forward_--help.golden @@ -1,6 +1,7 @@ Usage: coder port-forward [flags] -Forward ports from machine to a workspace +Forward ports from a workspace to the local machine. Please note that to achieve +reverse port forwarding, utilize the "coder ssh -R" command. Aliases: tunnel diff --git a/docs/cli.md b/docs/cli.md index 26e57b23455ad..548b7284d5253 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -23,40 +23,40 @@ Coder — A tool for provisioning self-hosted development environments with Terr ## Subcommands -| Name | Purpose | -| ------------------------------------------------------ | ---------------------------------------------------------------------- | -| [config-ssh](./cli/config-ssh.md) | Add an SSH Host entry for your workspaces "ssh coder.workspace" | -| [create](./cli/create.md) | Create a workspace | -| [delete](./cli/delete.md) | Delete a workspace | -| [dotfiles](./cli/dotfiles.md) | Personalize your workspace by applying a canonical dotfiles repository | -| [features](./cli/features.md) | List Enterprise features | -| [groups](./cli/groups.md) | Manage groups | -| [licenses](./cli/licenses.md) | Add, delete, and list licenses | -| [list](./cli/list.md) | List workspaces | -| [login](./cli/login.md) | Authenticate with Coder deployment | -| [logout](./cli/logout.md) | Unauthenticate your local session | -| [netcheck](./cli/netcheck.md) | Print network debug information for DERP and STUN | -| [ping](./cli/ping.md) | Ping a workspace | -| [port-forward](./cli/port-forward.md) | Forward ports from machine to a workspace | -| [provisionerd](./cli/provisionerd.md) | Manage provisioner daemons | -| [publickey](./cli/publickey.md) | Output your Coder public key used for Git operations | -| [rename](./cli/rename.md) | Rename a workspace | -| [reset-password](./cli/reset-password.md) | Directly connect to the database to reset a user's password | -| [restart](./cli/restart.md) | Restart a workspace | -| [schedule](./cli/schedule.md) | Schedule automated start and stop times for workspaces | -| [server](./cli/server.md) | Start a Coder server | -| [show](./cli/show.md) | Display details of a workspace's resources and agents | -| [speedtest](./cli/speedtest.md) | Run upload and download tests from your machine to a workspace | -| [ssh](./cli/ssh.md) | Start a shell into a workspace | -| [start](./cli/start.md) | Start a workspace | -| [stat](./cli/stat.md) | Show resource usage for the current workspace. | -| [state](./cli/state.md) | Manually manage Terraform state to fix broken workspaces | -| [stop](./cli/stop.md) | Stop a workspace | -| [templates](./cli/templates.md) | Manage templates | -| [tokens](./cli/tokens.md) | Manage personal access tokens | -| [update](./cli/update.md) | Will update and start a given workspace if it is out of date | -| [users](./cli/users.md) | Manage users | -| [version](./cli/version.md) | Show coder version | +| Name | Purpose | +| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | +| [config-ssh](./cli/config-ssh.md) | Add an SSH Host entry for your workspaces "ssh coder.workspace" | +| [create](./cli/create.md) | Create a workspace | +| [delete](./cli/delete.md) | Delete a workspace | +| [dotfiles](./cli/dotfiles.md) | Personalize your workspace by applying a canonical dotfiles repository | +| [features](./cli/features.md) | List Enterprise features | +| [groups](./cli/groups.md) | Manage groups | +| [licenses](./cli/licenses.md) | Add, delete, and list licenses | +| [list](./cli/list.md) | List workspaces | +| [login](./cli/login.md) | Authenticate with Coder deployment | +| [logout](./cli/logout.md) | Unauthenticate your local session | +| [netcheck](./cli/netcheck.md) | Print network debug information for DERP and STUN | +| [ping](./cli/ping.md) | Ping a workspace | +| [port-forward](./cli/port-forward.md) | Forward ports from a workspace to the local machine. Please note that to achieve reverse port forwarding, utilize the "coder ssh -R" command. | +| [provisionerd](./cli/provisionerd.md) | Manage provisioner daemons | +| [publickey](./cli/publickey.md) | Output your Coder public key used for Git operations | +| [rename](./cli/rename.md) | Rename a workspace | +| [reset-password](./cli/reset-password.md) | Directly connect to the database to reset a user's password | +| [restart](./cli/restart.md) | Restart a workspace | +| [schedule](./cli/schedule.md) | Schedule automated start and stop times for workspaces | +| [server](./cli/server.md) | Start a Coder server | +| [show](./cli/show.md) | Display details of a workspace's resources and agents | +| [speedtest](./cli/speedtest.md) | Run upload and download tests from your machine to a workspace | +| [ssh](./cli/ssh.md) | Start a shell into a workspace | +| [start](./cli/start.md) | Start a workspace | +| [stat](./cli/stat.md) | Show resource usage for the current workspace. | +| [state](./cli/state.md) | Manually manage Terraform state to fix broken workspaces | +| [stop](./cli/stop.md) | Stop a workspace | +| [templates](./cli/templates.md) | Manage templates | +| [tokens](./cli/tokens.md) | Manage personal access tokens | +| [update](./cli/update.md) | Will update and start a given workspace if it is out of date | +| [users](./cli/users.md) | Manage users | +| [version](./cli/version.md) | Show coder version | ## Options diff --git a/docs/cli/port-forward.md b/docs/cli/port-forward.md index 5c3ac14c1b126..4d5f5475f6859 100644 --- a/docs/cli/port-forward.md +++ b/docs/cli/port-forward.md @@ -2,7 +2,7 @@ # port-forward -Forward ports from machine to a workspace +Forward ports from a workspace to the local machine. Please note that to achieve reverse port forwarding, utilize the "coder ssh -R" command. Aliases: diff --git a/docs/manifest.json b/docs/manifest.json index 517f30490ae70..5bb251ce6e306 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -610,7 +610,7 @@ }, { "title": "port-forward", - "description": "Forward ports from machine to a workspace", + "description": "Forward ports from a workspace to the local machine. Please note that to achieve reverse port forwarding, utilize the \"coder ssh -R\" command.", "path": "cli/port-forward.md" }, { From d8859c34a0d1172ec41afc63633b768e61303324 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 20 Jul 2023 11:34:13 +0200 Subject: [PATCH 12/12] Rephrase --- cli/portforward.go | 2 +- cli/testdata/coder_--help.golden | 4 +- cli/testdata/coder_port-forward_--help.golden | 4 +- docs/cli.md | 68 +++++++++---------- docs/cli/port-forward.md | 2 +- docs/manifest.json | 2 +- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/cli/portforward.go b/cli/portforward.go index c3aa21bc08cc4..a8ae4c9f75d6d 100644 --- a/cli/portforward.go +++ b/cli/portforward.go @@ -32,7 +32,7 @@ func (r *RootCmd) portForward() *clibase.Cmd { client := new(codersdk.Client) cmd := &clibase.Cmd{ Use: "port-forward ", - Short: `Forward ports from a workspace to the local machine. Please note that to achieve reverse port forwarding, utilize the "coder ssh -R" command.`, + Short: `Forward ports from a workspace to the local machine. Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R".`, Aliases: []string{"tunnel"}, Long: formatExamples( example{ diff --git a/cli/testdata/coder_--help.golden b/cli/testdata/coder_--help.golden index 05b57ac10c835..3395b6ecd30b5 100644 --- a/cli/testdata/coder_--help.golden +++ b/cli/testdata/coder_--help.golden @@ -22,8 +22,8 @@ Coder v0.0.0-devel — A tool for provisioning self-hosted development environme netcheck Print network debug information for DERP and STUN ping Ping a workspace port-forward Forward ports from a workspace to the local machine. - Please note that to achieve reverse port forwarding, - utilize the "coder ssh -R" command. + Forward ports from a workspace to the local machine. For + reverse port forwarding, use "coder ssh -R". publickey Output your Coder public key used for Git operations rename Rename a workspace reset-password Directly connect to the database to reset a user's diff --git a/cli/testdata/coder_port-forward_--help.golden b/cli/testdata/coder_port-forward_--help.golden index c668e9b767ee1..a6eb7d936b8e6 100644 --- a/cli/testdata/coder_port-forward_--help.golden +++ b/cli/testdata/coder_port-forward_--help.golden @@ -1,7 +1,7 @@ Usage: coder port-forward [flags] -Forward ports from a workspace to the local machine. Please note that to achieve -reverse port forwarding, utilize the "coder ssh -R" command. +Forward ports from a workspace to the local machine. Forward ports from a +workspace to the local machine. For reverse port forwarding, use "coder ssh -R". Aliases: tunnel diff --git a/docs/cli.md b/docs/cli.md index 548b7284d5253..95fbbabc95677 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -23,40 +23,40 @@ Coder — A tool for provisioning self-hosted development environments with Terr ## Subcommands -| Name | Purpose | -| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | -| [config-ssh](./cli/config-ssh.md) | Add an SSH Host entry for your workspaces "ssh coder.workspace" | -| [create](./cli/create.md) | Create a workspace | -| [delete](./cli/delete.md) | Delete a workspace | -| [dotfiles](./cli/dotfiles.md) | Personalize your workspace by applying a canonical dotfiles repository | -| [features](./cli/features.md) | List Enterprise features | -| [groups](./cli/groups.md) | Manage groups | -| [licenses](./cli/licenses.md) | Add, delete, and list licenses | -| [list](./cli/list.md) | List workspaces | -| [login](./cli/login.md) | Authenticate with Coder deployment | -| [logout](./cli/logout.md) | Unauthenticate your local session | -| [netcheck](./cli/netcheck.md) | Print network debug information for DERP and STUN | -| [ping](./cli/ping.md) | Ping a workspace | -| [port-forward](./cli/port-forward.md) | Forward ports from a workspace to the local machine. Please note that to achieve reverse port forwarding, utilize the "coder ssh -R" command. | -| [provisionerd](./cli/provisionerd.md) | Manage provisioner daemons | -| [publickey](./cli/publickey.md) | Output your Coder public key used for Git operations | -| [rename](./cli/rename.md) | Rename a workspace | -| [reset-password](./cli/reset-password.md) | Directly connect to the database to reset a user's password | -| [restart](./cli/restart.md) | Restart a workspace | -| [schedule](./cli/schedule.md) | Schedule automated start and stop times for workspaces | -| [server](./cli/server.md) | Start a Coder server | -| [show](./cli/show.md) | Display details of a workspace's resources and agents | -| [speedtest](./cli/speedtest.md) | Run upload and download tests from your machine to a workspace | -| [ssh](./cli/ssh.md) | Start a shell into a workspace | -| [start](./cli/start.md) | Start a workspace | -| [stat](./cli/stat.md) | Show resource usage for the current workspace. | -| [state](./cli/state.md) | Manually manage Terraform state to fix broken workspaces | -| [stop](./cli/stop.md) | Stop a workspace | -| [templates](./cli/templates.md) | Manage templates | -| [tokens](./cli/tokens.md) | Manage personal access tokens | -| [update](./cli/update.md) | Will update and start a given workspace if it is out of date | -| [users](./cli/users.md) | Manage users | -| [version](./cli/version.md) | Show coder version | +| Name | Purpose | +| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [config-ssh](./cli/config-ssh.md) | Add an SSH Host entry for your workspaces "ssh coder.workspace" | +| [create](./cli/create.md) | Create a workspace | +| [delete](./cli/delete.md) | Delete a workspace | +| [dotfiles](./cli/dotfiles.md) | Personalize your workspace by applying a canonical dotfiles repository | +| [features](./cli/features.md) | List Enterprise features | +| [groups](./cli/groups.md) | Manage groups | +| [licenses](./cli/licenses.md) | Add, delete, and list licenses | +| [list](./cli/list.md) | List workspaces | +| [login](./cli/login.md) | Authenticate with Coder deployment | +| [logout](./cli/logout.md) | Unauthenticate your local session | +| [netcheck](./cli/netcheck.md) | Print network debug information for DERP and STUN | +| [ping](./cli/ping.md) | Ping a workspace | +| [port-forward](./cli/port-forward.md) | Forward ports from a workspace to the local machine. Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R". | +| [provisionerd](./cli/provisionerd.md) | Manage provisioner daemons | +| [publickey](./cli/publickey.md) | Output your Coder public key used for Git operations | +| [rename](./cli/rename.md) | Rename a workspace | +| [reset-password](./cli/reset-password.md) | Directly connect to the database to reset a user's password | +| [restart](./cli/restart.md) | Restart a workspace | +| [schedule](./cli/schedule.md) | Schedule automated start and stop times for workspaces | +| [server](./cli/server.md) | Start a Coder server | +| [show](./cli/show.md) | Display details of a workspace's resources and agents | +| [speedtest](./cli/speedtest.md) | Run upload and download tests from your machine to a workspace | +| [ssh](./cli/ssh.md) | Start a shell into a workspace | +| [start](./cli/start.md) | Start a workspace | +| [stat](./cli/stat.md) | Show resource usage for the current workspace. | +| [state](./cli/state.md) | Manually manage Terraform state to fix broken workspaces | +| [stop](./cli/stop.md) | Stop a workspace | +| [templates](./cli/templates.md) | Manage templates | +| [tokens](./cli/tokens.md) | Manage personal access tokens | +| [update](./cli/update.md) | Will update and start a given workspace if it is out of date | +| [users](./cli/users.md) | Manage users | +| [version](./cli/version.md) | Show coder version | ## Options diff --git a/docs/cli/port-forward.md b/docs/cli/port-forward.md index 4d5f5475f6859..b4b14d605392b 100644 --- a/docs/cli/port-forward.md +++ b/docs/cli/port-forward.md @@ -2,7 +2,7 @@ # port-forward -Forward ports from a workspace to the local machine. Please note that to achieve reverse port forwarding, utilize the "coder ssh -R" command. +Forward ports from a workspace to the local machine. Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R". Aliases: diff --git a/docs/manifest.json b/docs/manifest.json index 5bb251ce6e306..423bb33353a70 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -610,7 +610,7 @@ }, { "title": "port-forward", - "description": "Forward ports from a workspace to the local machine. Please note that to achieve reverse port forwarding, utilize the \"coder ssh -R\" command.", + "description": "Forward ports from a workspace to the local machine. Forward ports from a workspace to the local machine. For reverse port forwarding, use \"coder ssh -R\".", "path": "cli/port-forward.md" }, {