Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 3af8385

Browse files
authored
feat: Enable arbitrary SSH options passed via config-ssh (#410)
* feat: Add option to disable SSH connection cache * Refactor to enable arbitrary options * Generate docs * Add comment for duplicated values
1 parent edc273f commit 3af8385

File tree

2 files changed

+34
-22
lines changed

2 files changed

+34
-22
lines changed

docs/coder_config-ssh.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ coder config-ssh [flags]
1515
```
1616
--filepath string override the default path of your ssh config file (default "~/.ssh/config")
1717
-h, --help help for config-ssh
18+
-o, --option strings additional options injected in the ssh config (ex. disable caching with "-o ControlPath=none")
1819
--remove remove the auto-generated Coder ssh config
1920
```
2021

internal/cmd/configssh.go

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,25 @@ const sshEndToken = "# ------------END-CODER-ENTERPRISE------------"
3333

3434
func configSSHCmd() *cobra.Command {
3535
var (
36-
configpath string
37-
remove = false
36+
configpath string
37+
remove = false
38+
additionalOptions []string
3839
)
3940

4041
cmd := &cobra.Command{
4142
Use: "config-ssh",
4243
Short: "Configure SSH to access Coder workspaces",
4344
Long: "Inject the proper OpenSSH configuration into your local SSH config file.",
44-
RunE: configSSH(&configpath, &remove),
45+
RunE: configSSH(&configpath, &remove, &additionalOptions),
4546
}
4647
cmd.Flags().StringVar(&configpath, "filepath", filepath.Join("~", ".ssh", "config"), "override the default path of your ssh config file")
48+
cmd.Flags().StringSliceVarP(&additionalOptions, "option", "o", []string{}, "additional options injected in the ssh config (ex. disable caching with \"-o ControlPath=none\")")
4749
cmd.Flags().BoolVar(&remove, "remove", false, "remove the auto-generated Coder ssh config")
4850

4951
return cmd
5052
}
5153

52-
func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []string) error {
54+
func configSSH(configpath *string, remove *bool, additionalOptions *[]string) func(cmd *cobra.Command, _ []string) error {
5355
return func(cmd *cobra.Command, _ []string) error {
5456
ctx := cmd.Context()
5557
usr, err := user.Current()
@@ -118,7 +120,7 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st
118120
return xerrors.Errorf("Failed to get executable path: %w", err)
119121
}
120122

121-
newConfig := makeNewConfigs(binPath, workspacesWithProviders, privateKeyFilepath)
123+
newConfig := makeNewConfigs(binPath, workspacesWithProviders, privateKeyFilepath, *additionalOptions)
122124

123125
err = os.MkdirAll(filepath.Dir(*configpath), os.ModePerm)
124126
if err != nil {
@@ -226,7 +228,7 @@ func writeSSHKey(ctx context.Context, client coder.Client, privateKeyPath string
226228
return ioutil.WriteFile(privateKeyPath, []byte(key.PrivateKey), 0600)
227229
}
228230

229-
func makeNewConfigs(binPath string, workspaces []coderutil.WorkspaceWithWorkspaceProvider, privateKeyFilepath string) string {
231+
func makeNewConfigs(binPath string, workspaces []coderutil.WorkspaceWithWorkspaceProvider, privateKeyFilepath string, additionalOptions []string) string {
230232
newConfig := fmt.Sprintf("\n%s\n%s\n\n", sshStartToken, sshStartMessage)
231233

232234
sort.Slice(workspaces, func(i, j int) bool { return workspaces[i].Workspace.Name < workspaces[j].Workspace.Name })
@@ -240,32 +242,41 @@ func makeNewConfigs(binPath string, workspaces []coderutil.WorkspaceWithWorkspac
240242
continue
241243
}
242244

243-
newConfig += makeSSHConfig(binPath, workspace.Workspace.Name, privateKeyFilepath)
245+
newConfig += makeSSHConfig(binPath, workspace.Workspace.Name, privateKeyFilepath, additionalOptions)
244246
}
245247
newConfig += fmt.Sprintf("\n%s\n", sshEndToken)
246248

247249
return newConfig
248250
}
249251

250-
func makeSSHConfig(binPath, workspaceName, privateKeyFilepath string) string {
251-
entry := fmt.Sprintf(
252-
`Host coder.%s
253-
HostName coder.%s
254-
ProxyCommand "%s" tunnel %s 12213 stdio
255-
StrictHostKeyChecking no
256-
ConnectTimeout=0
257-
IdentitiesOnly yes
258-
IdentityFile="%s"
259-
`, workspaceName, workspaceName, binPath, workspaceName, privateKeyFilepath)
252+
func makeSSHConfig(binPath, workspaceName, privateKeyFilepath string, additionalOptions []string) string {
253+
// Custom user options come first to maximizessh customization.
254+
options := []string{}
255+
if len(additionalOptions) > 0 {
256+
options = []string{
257+
"# Custom options. Duplicated values will always prefer the first!",
258+
}
259+
options = append(options, additionalOptions...)
260+
options = append(options, "# End custom options.")
261+
}
262+
options = append(options,
263+
fmt.Sprintf("HostName coder.%s", workspaceName),
264+
fmt.Sprintf("ProxyCommand %q tunnel %s 12213 stdio", binPath, workspaceName),
265+
"StrictHostKeyChecking no",
266+
"ConnectTimeout=0",
267+
"IdentitiesOnly yes",
268+
fmt.Sprintf("IdentityFile=%q", privateKeyFilepath),
269+
)
260270

261271
if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
262-
entry += ` ControlMaster auto
263-
ControlPath ~/.ssh/.connection-%r@%h:%p
264-
ControlPersist 600
265-
`
272+
options = append(options,
273+
"ControlMaster auto",
274+
"ControlPath ~/.ssh/.connection-%r@%h:%p",
275+
"ControlPersist 600",
276+
)
266277
}
267278

268-
return entry
279+
return fmt.Sprintf("Host coder.%s\n\t%s\n\n", workspaceName, strings.Join(options, "\n\t"))
269280
}
270281

271282
func writeStr(filename, data string) error {

0 commit comments

Comments
 (0)