Skip to content

Commit 685e067

Browse files
committed
limactl shell, ssh.config: Remove ControlMaster, ControlPath, and ControlPersist options on Windows
Remove ControlMaster, ControlPath, and ControlPersist options, because Cygwin-based SSH clients do not support multiplexing when executing commands. References: https://inbox.sourceware.org/cygwin/c98988a5-7e65-4282-b2a1-bb8e350d5fab@acm.org/T/ https://stackoverflow.com/questions/20959792/is-ssh-controlmaster-with-cygwin-on-windows-actually-possible By removing these options: - Avoids execution failures when the control master is not yet available. - Prevents error messages such as: > mux_client_request_session: read from master failed: Connection reset by peer > ControlSocket ....sock already exists, disabling multiplexing Only remove these options when writing the SSH config file and executing `limactl shell`, since multiplexing seems to work with port forwarding. Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
1 parent 178704d commit 685e067

File tree

3 files changed

+37
-0
lines changed

3 files changed

+37
-0
lines changed

cmd/limactl/shell.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,20 @@ func shellAction(cmd *cobra.Command, args []string) error {
236236
if err != nil {
237237
return err
238238
}
239+
if runtime.GOOS == "windows" {
240+
// Remove ControlMaster, ControlPath, and ControlPersist options,
241+
// because Cygwin-based SSH clients do not support multiplexing when executing commands.
242+
// References:
243+
// https://inbox.sourceware.org/cygwin/c98988a5-7e65-4282-b2a1-bb8e350d5fab@acm.org/T/
244+
// https://stackoverflow.com/questions/20959792/is-ssh-controlmaster-with-cygwin-on-windows-actually-possible
245+
// By removing these options:
246+
// - Avoids execution failures when the control master is not yet available.
247+
// - Prevents error messages such as:
248+
// > mux_client_request_session: read from master failed: Connection reset by peer
249+
// > ControlSocket ....sock already exists, disabling multiplexing
250+
// Only remove these options when writing the SSH config file and executing `limactl shell`, since multiplexing seems to work with port forwarding.
251+
sshOpts = sshutil.SSHOptsRemovingControlPath(sshOpts)
252+
}
239253
sshArgs := append([]string{}, sshExe.Args...)
240254
sshArgs = append(sshArgs, sshutil.SSHArgsFromOpts(sshOpts)...)
241255
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {

pkg/hostagent/hostagent.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"os"
1616
"os/exec"
1717
"path/filepath"
18+
"runtime"
1819
"strconv"
1920
"strings"
2021
"sync"
@@ -265,6 +266,20 @@ func writeSSHConfigFile(sshPath, instName, instDir, instSSHAddress string, sshLo
265266
# This file is created by Lima, but not used by Lima itself currently.
266267
# Modifications to this file will be lost on restarting the Lima instance.
267268
`)
269+
if runtime.GOOS == "windows" {
270+
// Remove ControlMaster, ControlPath, and ControlPersist options,
271+
// because Cygwin-based SSH clients do not support multiplexing when executing commands.
272+
// References:
273+
// https://inbox.sourceware.org/cygwin/c98988a5-7e65-4282-b2a1-bb8e350d5fab@acm.org/T/
274+
// https://stackoverflow.com/questions/20959792/is-ssh-controlmaster-with-cygwin-on-windows-actually-possible
275+
// By removing these options:
276+
// - Avoids execution failures when the control master is not yet available.
277+
// - Prevents error messages such as:
278+
// > mux_client_request_session: read from master failed: Connection reset by peer
279+
// > ControlSocket ....sock already exists, disabling multiplexing
280+
// Only remove these options when writing the SSH config file and executing `limactl shell`, since multiplexing seems to work with port forwarding.
281+
sshOpts = sshutil.SSHOptsRemovingControlPath(sshOpts)
282+
}
268283
if err := sshutil.Format(b, sshPath, instName, sshutil.FormatConfig,
269284
append(sshOpts,
270285
fmt.Sprintf("Hostname=%s", instSSHAddress),

pkg/sshutil/sshutil.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"path/filepath"
1717
"regexp"
1818
"runtime"
19+
"slices"
1920
"strings"
2021
"sync"
2122
"time"
@@ -340,6 +341,13 @@ func SSHArgsFromOpts(opts []string) []string {
340341
return args
341342
}
342343

344+
// SSHOptsRemovingControlPath removes ControlMaster, ControlPath, and ControlPersist options from SSH options.
345+
func SSHOptsRemovingControlPath(opts []string) []string {
346+
return slices.DeleteFunc(opts, func(s string) bool {
347+
return strings.HasPrefix(s, "ControlMaster") || strings.HasPrefix(s, "ControlPath") || strings.HasPrefix(s, "ControlPersist")
348+
})
349+
}
350+
343351
func ParseOpenSSHVersion(version []byte) *semver.Version {
344352
regex := regexp.MustCompile(`(?m)^OpenSSH_(\d+\.\d+)(?:p(\d+))?\b`)
345353
matches := regex.FindSubmatch(version)

0 commit comments

Comments
 (0)