feat: add --ssh-host-prefix flag for "coder ssh" (#16088)

This adds a flag matching `--ssh-host-prefix` from `coder config-ssh` to
`coder ssh`. By trimming a custom prefix from the argument, we can set
up wildcard-based `Host` entries in SSH config for the IDE plugins (and
eventually `coder config-ssh`).

We also replace `--` in the argument with `/`, so ownership can be
specified in wildcard-based SSH hosts like `<owner>--<workspace>`.

Replaces #16087.

Part of https://github.com/coder/coder/issues/14986.

Related to https://github.com/coder/coder/pull/16078 and
https://github.com/coder/coder/pull/16080.
This commit is contained in:
Aaron Lehmann
2025-01-13 17:07:21 -08:00
committed by GitHub
parent ec6645b832
commit 1aa9e32a2b
4 changed files with 89 additions and 1 deletions

View File

@ -61,6 +61,7 @@ var (
func (r *RootCmd) ssh() *serpent.Command {
var (
stdio bool
hostPrefix string
forwardAgent bool
forwardGPG bool
identityAgent string
@ -195,7 +196,11 @@ func (r *RootCmd) ssh() *serpent.Command {
parsedEnv = append(parsedEnv, [2]string{k, v})
}
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, inv.Args[0])
namedWorkspace := strings.TrimPrefix(inv.Args[0], hostPrefix)
// Support "--" as a delimiter between owner and workspace name
namedWorkspace = strings.Replace(namedWorkspace, "--", "/", 1)
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, namedWorkspace)
if err != nil {
return err
}
@ -509,6 +514,12 @@ func (r *RootCmd) ssh() *serpent.Command {
Description: "Specifies whether to emit SSH output over stdin/stdout.",
Value: serpent.BoolOf(&stdio),
},
{
Flag: "ssh-host-prefix",
Env: "CODER_SSH_SSH_HOST_PREFIX",
Description: "Strip this prefix from the provided hostname to determine the workspace name. This is useful when used as part of an OpenSSH proxy command.",
Value: serpent.StringOf(&hostPrefix),
},
{
Flag: "forward-agent",
FlagShorthand: "A",

View File

@ -1568,6 +1568,69 @@ func TestSSH(t *testing.T) {
})
}
})
t.Run("SSHHostPrefix", func(t *testing.T) {
t.Parallel()
client, workspace, agentToken := setupWorkspaceForAgent(t)
_, _ = tGoContext(t, func(ctx context.Context) {
// Run this async so the SSH command has to wait for
// the build and agent to connect!
_ = agenttest.New(t, client.URL, agentToken)
<-ctx.Done()
})
clientOutput, clientInput := io.Pipe()
serverOutput, serverInput := io.Pipe()
defer func() {
for _, c := range []io.Closer{clientOutput, clientInput, serverOutput, serverInput} {
_ = c.Close()
}
}()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
user, err := client.User(ctx, codersdk.Me)
require.NoError(t, err)
inv, root := clitest.New(t, "ssh", "--stdio", "--ssh-host-prefix", "coder.dummy.com--", fmt.Sprintf("coder.dummy.com--%s--%s", user.Username, workspace.Name))
clitest.SetupConfig(t, client, root)
inv.Stdin = clientOutput
inv.Stdout = serverInput
inv.Stderr = io.Discard
cmdDone := tGo(t, func() {
err := inv.WithContext(ctx).Run()
assert.NoError(t, err)
})
conn, channels, requests, err := ssh.NewClientConn(&stdioConn{
Reader: serverOutput,
Writer: clientInput,
}, "", &ssh.ClientConfig{
// #nosec
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
})
require.NoError(t, err)
defer conn.Close()
sshClient := ssh.NewClient(conn, channels, requests)
session, err := sshClient.NewSession()
require.NoError(t, err)
defer session.Close()
command := "sh -c exit"
if runtime.GOOS == "windows" {
command = "cmd.exe /c exit"
}
err = session.Run(command)
require.NoError(t, err)
err = sshClient.Close()
require.NoError(t, err)
_ = clientOutput.Close()
<-cmdDone
})
}
//nolint:paralleltest // This test uses t.Setenv, parent test MUST NOT be parallel.

View File

@ -45,6 +45,11 @@ OPTIONS:
-R, --remote-forward string-array, $CODER_SSH_REMOTE_FORWARD
Enable remote port forwarding (remote_port:local_address:local_port).
--ssh-host-prefix string, $CODER_SSH_SSH_HOST_PREFIX
Strip this prefix from the provided hostname to determine the
workspace name. This is useful when used as part of an OpenSSH proxy
command.
--stdio bool, $CODER_SSH_STDIO
Specifies whether to emit SSH output over stdin/stdout.

View File

@ -20,6 +20,15 @@ coder ssh [flags] <workspace>
Specifies whether to emit SSH output over stdin/stdout.
### --ssh-host-prefix
| | |
|-------------|-----------------------------------------|
| Type | <code>string</code> |
| Environment | <code>$CODER_SSH_SSH_HOST_PREFIX</code> |
Strip this prefix from the provided hostname to determine the workspace name. This is useful when used as part of an OpenSSH proxy command.
### -A, --forward-agent
| | |