mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
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:
13
cli/ssh.go
13
cli/ssh.go
@ -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",
|
||||
|
@ -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.
|
||||
|
5
cli/testdata/coder_ssh_--help.golden
vendored
5
cli/testdata/coder_ssh_--help.golden
vendored
@ -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.
|
||||
|
||||
|
9
docs/reference/cli/ssh.md
generated
9
docs/reference/cli/ssh.md
generated
@ -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
|
||||
|
||||
| | |
|
||||
|
Reference in New Issue
Block a user