Skip to content

Commit 0499bda

Browse files
committed
feat: --ssh-host-prefix flag for "coder ssh"
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>`.
1 parent ec6645b commit 0499bda

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed

cli/ssh.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ var (
6161
func (r *RootCmd) ssh() *serpent.Command {
6262
var (
6363
stdio bool
64+
hostPrefix string
6465
forwardAgent bool
6566
forwardGPG bool
6667
identityAgent string
@@ -195,7 +196,11 @@ func (r *RootCmd) ssh() *serpent.Command {
195196
parsedEnv = append(parsedEnv, [2]string{k, v})
196197
}
197198

198-
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, inv.Args[0])
199+
namedWorkspace := strings.TrimPrefix(inv.Args[0], hostPrefix)
200+
// Support "--" as a delimiter between owner and workspace name
201+
namedWorkspace = strings.Replace(namedWorkspace, "--", "/", 1)
202+
203+
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, namedWorkspace)
199204
if err != nil {
200205
return err
201206
}
@@ -509,6 +514,12 @@ func (r *RootCmd) ssh() *serpent.Command {
509514
Description: "Specifies whether to emit SSH output over stdin/stdout.",
510515
Value: serpent.BoolOf(&stdio),
511516
},
517+
{
518+
Flag: "ssh-host-prefix",
519+
Env: "CODER_SSH_SSH_HOST_PREFIX",
520+
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.",
521+
Value: serpent.StringOf(&hostPrefix),
522+
},
512523
{
513524
Flag: "forward-agent",
514525
FlagShorthand: "A",

cli/ssh_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,69 @@ func TestSSH(t *testing.T) {
15681568
})
15691569
}
15701570
})
1571+
1572+
t.Run("SSHHostPrefix", func(t *testing.T) {
1573+
t.Parallel()
1574+
client, workspace, agentToken := setupWorkspaceForAgent(t)
1575+
_, _ = tGoContext(t, func(ctx context.Context) {
1576+
// Run this async so the SSH command has to wait for
1577+
// the build and agent to connect!
1578+
_ = agenttest.New(t, client.URL, agentToken)
1579+
<-ctx.Done()
1580+
})
1581+
1582+
clientOutput, clientInput := io.Pipe()
1583+
serverOutput, serverInput := io.Pipe()
1584+
defer func() {
1585+
for _, c := range []io.Closer{clientOutput, clientInput, serverOutput, serverInput} {
1586+
_ = c.Close()
1587+
}
1588+
}()
1589+
1590+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
1591+
defer cancel()
1592+
1593+
user, err := client.User(ctx, codersdk.Me)
1594+
require.NoError(t, err)
1595+
1596+
inv, root := clitest.New(t, "ssh", "--stdio", "--ssh-host-prefix", "coder.dummy.com--", fmt.Sprintf("coder.dummy.com--%s--%s", user.Username, workspace.Name))
1597+
clitest.SetupConfig(t, client, root)
1598+
inv.Stdin = clientOutput
1599+
inv.Stdout = serverInput
1600+
inv.Stderr = io.Discard
1601+
1602+
cmdDone := tGo(t, func() {
1603+
err := inv.WithContext(ctx).Run()
1604+
assert.NoError(t, err)
1605+
})
1606+
1607+
conn, channels, requests, err := ssh.NewClientConn(&stdioConn{
1608+
Reader: serverOutput,
1609+
Writer: clientInput,
1610+
}, "", &ssh.ClientConfig{
1611+
// #nosec
1612+
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
1613+
})
1614+
require.NoError(t, err)
1615+
defer conn.Close()
1616+
1617+
sshClient := ssh.NewClient(conn, channels, requests)
1618+
session, err := sshClient.NewSession()
1619+
require.NoError(t, err)
1620+
defer session.Close()
1621+
1622+
command := "sh -c exit"
1623+
if runtime.GOOS == "windows" {
1624+
command = "cmd.exe /c exit"
1625+
}
1626+
err = session.Run(command)
1627+
require.NoError(t, err)
1628+
err = sshClient.Close()
1629+
require.NoError(t, err)
1630+
_ = clientOutput.Close()
1631+
1632+
<-cmdDone
1633+
})
15711634
}
15721635

15731636
//nolint:paralleltest // This test uses t.Setenv, parent test MUST NOT be parallel.

cli/testdata/coder_ssh_--help.golden

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ OPTIONS:
4545
-R, --remote-forward string-array, $CODER_SSH_REMOTE_FORWARD
4646
Enable remote port forwarding (remote_port:local_address:local_port).
4747

48+
--ssh-host-prefix string, $CODER_SSH_SSH_HOST_PREFIX
49+
Strip this prefix from the provided hostname to determine the
50+
workspace name. This is useful when used as part of an OpenSSH proxy
51+
command.
52+
4853
--stdio bool, $CODER_SSH_STDIO
4954
Specifies whether to emit SSH output over stdin/stdout.
5055

docs/reference/cli/ssh.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy