resurrect ssh multiplexing#4699
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Some minor linting issues; one of them is probably to use |
|
Oh! And looks like you missed adding DCO sign-offs 😅 |
17260f6 to
937954d
Compare
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🔴 CRITICAL
The SSH multiplexing implementation has two high-severity bugs where addMultiplexingArgs returns nil instead of the original sshFlags, silently dropping all previously-accumulated flags (including the connect timeout set by addSSHTimeout) in two paths: when DOCKER_SSH_NO_MUX is truthy, and when os.MkdirAll fails. Both should return sshFlags unchanged. Additionally, the DOCKER_SSH_NO_MUX env var silently ignores common Unix values like yes, and DOCKER_SSH_MUX_PERSIST is passed unsanitised to SSH.
| func addMultiplexingArgs(sshFlags []string) []string { | ||
| if v := os.Getenv("DOCKER_SSH_NO_MUX"); v != "" { | ||
| if b, err := strconv.ParseBool(v); err == nil && b { | ||
| return nil |
There was a problem hiding this comment.
[HIGH] addMultiplexingArgs returns nil (drops all existing sshFlags) when DOCKER_SSH_NO_MUX is truthy
When DOCKER_SSH_NO_MUX is set to a truthy value (e.g. "true", "1"), the function returns nil instead of the original sshFlags. The caller at line 56 does sshFlags = addMultiplexingArgs(sshFlags), so all flags previously appended by addSSHTimeout (e.g. -o ConnectTimeout=30) and any caller-supplied flags from GetConnectionHelperWithSSHOpts are silently discarded. The connection then proceeds without a connect timeout and without user-supplied flags.
Fix: Return sshFlags (unchanged) instead of nil:
if b, err := strconv.ParseBool(v); err == nil && b {
return sshFlags // not nil
}There was a problem hiding this comment.
yeah, that doesn't look right; fixed
There was a problem hiding this comment.
Thanks for the quick fix! Confirmed — line 93 now correctly returns sshFlags instead of nil, preserving the timeout and any user-supplied flags when multiplexing is disabled.
| } | ||
| } | ||
| if err := os.MkdirAll(config.Dir(), 0o700); err != nil { | ||
| return nil |
There was a problem hiding this comment.
[HIGH] addMultiplexingArgs returns nil (drops all existing sshFlags) on MkdirAll error
When os.MkdirAll(config.Dir(), 0o700) fails (e.g. permissions error, read-only filesystem), the function returns nil, discarding all previously-accumulated sshFlags. This silently removes -o ConnectTimeout=30 (added by addSSHTimeout) and any caller-supplied flags on every connection attempt where the config directory is unavailable. The intent is clearly to fall back gracefully — but nil is not a graceful fallback.
Fix: Return the original sshFlags unchanged so the connection still benefits from the timeout and other flags:
if err := os.MkdirAll(config.Dir(), 0o700); err != nil {
return sshFlags // not nil — multiplexing skipped, but existing flags preserved
}|
|
||
| func addMultiplexingArgs(sshFlags []string) []string { | ||
| if v := os.Getenv("DOCKER_SSH_NO_MUX"); v != "" { | ||
| if b, err := strconv.ParseBool(v); err == nil && b { |
There was a problem hiding this comment.
[MEDIUM] DOCKER_SSH_NO_MUX=yes silently fails to disable multiplexing — only Go ParseBool strings accepted
strconv.ParseBool only recognises "1", "t", "T", "TRUE", "true", "True", "0", "f", "F", "FALSE", "false", "False". A user setting DOCKER_SSH_NO_MUX=yes (the common Unix/shell convention used by many tools) will have ParseBool return an error, making the condition err == nil && b false, and multiplexing will not be disabled — the env var is silently ignored.
The documentation in docker.md only says "If set will turn off SSH multiplexing" without clarifying which values are accepted, so users will be surprised.
Fix: Use the simpler v != "" check (consistent with how Docker treats DOCKER_TLS, NO_COLOR, etc.) or at minimum document the accepted values:
if v := os.Getenv("DOCKER_SSH_NO_MUX"); v != "" {
return sshFlags
}| } | ||
| sshFlags = append(sshFlags, "-o", "ControlMaster=auto", "-o", "ControlPath="+config.Dir()+"/%r@%h:%p") | ||
| if v := os.Getenv("DOCKER_SSH_MUX_PERSIST"); v != "" { | ||
| sshFlags = append(sshFlags, "-o", "ControlPersist="+v) |
There was a problem hiding this comment.
[MEDIUM] DOCKER_SSH_MUX_PERSIST value passed unsanitised into SSH -o ControlPersist option
The value of DOCKER_SSH_MUX_PERSIST is appended directly to the SSH flag string without any validation. While the SSH client receives it as a separate argv element (not a shell string, so no shell injection), there is no check that the value is a valid SSH duration (e.g. 60, 60s, yes, no). A malformed value (e.g. an empty suffix, a value with spaces) will be passed to the SSH client as-is and may result in confusing SSH errors rather than a clear user-facing message.
Suggestion: Validate the value against the expected format (integer seconds or SSH duration string) before appending, or at minimum document what values are accepted in the env var table.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
- What I did
resurrect #2132 which has been reverted due to CI flakyness
- How I did it
git revert + sme tweaks to adjust to current codebase
- How to verify it
- Description for the changelog
Add multiplexing to ssh connection helper so commands that do multiple requests share the connection. Environment variables for opting out and controlling the persist time for even better connection sharing.
- A picture of a cute animal (not mandatory but encouraged)