Skip to content

Commit 62ada21

Browse files
committed
fix(cli): do not overwrite repeated SSH options in config-ssh
Fixes #11593
1 parent bdd2caf commit 62ada21

File tree

2 files changed

+39
-33
lines changed

2 files changed

+39
-33
lines changed

cli/configssh.go

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type sshConfigOptions struct {
5454
disableAutostart bool
5555
header []string
5656
headerCommand string
57+
removedKeys map[string]bool
5758
}
5859

5960
// addOptions expects options in the form of "option=value" or "option value".
@@ -74,30 +75,19 @@ func (o *sshConfigOptions) addOption(option string) error {
7475
if err != nil {
7576
return err
7677
}
77-
for i, existing := range o.sshOptions {
78-
// Override existing option if they share the same key.
79-
// This is case-insensitive. Parsing each time might be a little slow,
80-
// but it is ok.
81-
existingKey, _, err := codersdk.ParseSSHConfigOption(existing)
82-
if err != nil {
83-
// Don't mess with original values if there is an error.
84-
// This could have come from the user's manual edits.
85-
continue
86-
}
87-
if strings.EqualFold(existingKey, key) {
88-
if value == "" {
89-
// Delete existing option.
90-
o.sshOptions = append(o.sshOptions[:i], o.sshOptions[i+1:]...)
91-
} else {
92-
// Override existing option.
93-
o.sshOptions[i] = option
94-
}
95-
return nil
96-
}
78+
if o.removedKeys != nil && o.removedKeys[key] {
79+
// Key marked as removed, skip.
80+
return nil
9781
}
98-
// Only append the option if it is not empty.
82+
// Only append the option if it is not empty
83+
// (we interpret empty as removal).
9984
if value != "" {
10085
o.sshOptions = append(o.sshOptions, option)
86+
} else {
87+
if o.removedKeys == nil {
88+
o.removedKeys = make(map[string]bool)
89+
}
90+
o.removedKeys[key] = true
10191
}
10292
return nil
10393
}
@@ -440,26 +430,29 @@ func (r *RootCmd) configSSH() *serpent.Command {
440430
configOptions := sshConfigOpts
441431
configOptions.sshOptions = nil
442432

443-
// Add standard options.
444-
err := configOptions.addOptions(defaultOptions...)
445-
if err != nil {
446-
return err
433+
// User options first (SSH only uses the first
434+
// option unless it can be given multiple times)
435+
for _, opt := range sshConfigOpts.sshOptions {
436+
err := configOptions.addOptions(opt)
437+
if err != nil {
438+
return xerrors.Errorf("add flag config option %q: %w", opt, err)
439+
}
447440
}
448441

449-
// Override with deployment options
442+
// Deployment options second, allow them to
443+
// override standard options.
450444
for k, v := range coderdConfig.SSHConfigOptions {
451445
opt := fmt.Sprintf("%s %s", k, v)
452446
err := configOptions.addOptions(opt)
453447
if err != nil {
454448
return xerrors.Errorf("add coderd config option %q: %w", opt, err)
455449
}
456450
}
457-
// Override with flag options
458-
for _, opt := range sshConfigOpts.sshOptions {
459-
err := configOptions.addOptions(opt)
460-
if err != nil {
461-
return xerrors.Errorf("add flag config option %q: %w", opt, err)
462-
}
451+
452+
// Finally, add the standard options.
453+
err := configOptions.addOptions(defaultOptions...)
454+
if err != nil {
455+
return err
463456
}
464457

465458
hostBlock := []string{

cli/configssh_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func TestConfigSSH(t *testing.T) {
6565

6666
const hostname = "test-coder."
6767
const expectedKey = "ConnectionAttempts"
68-
const removeKey = "ConnectionTimeout"
68+
const removeKey = "ConnectTimeout"
6969
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
7070
ConfigSSH: codersdk.SSHConfigResponse{
7171
HostnamePrefix: hostname,
@@ -620,6 +620,19 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
620620
regexMatch: `ProxyCommand .* --header-command "printf h1=v1 h2='v2'" ssh`,
621621
},
622622
},
623+
{
624+
name: "Multiple remote forwards",
625+
args: []string{
626+
"--yes",
627+
"--ssh-option", "RemoteForward 2222 192.168.11.1:2222",
628+
"--ssh-option", "RemoteForward 2223 192.168.11.1:2223",
629+
},
630+
wantErr: false,
631+
hasAgent: true,
632+
wantConfig: wantConfig{
633+
regexMatch: "RemoteForward 2222 192.168.11.1:2222.*\n.*RemoteForward 2223 192.168.11.1:2223",
634+
},
635+
},
623636
}
624637
for _, tt := range tests {
625638
tt := tt

0 commit comments

Comments
 (0)