diff --git a/cli/connhelper/connhelper.go b/cli/connhelper/connhelper.go index 8d97a2a40b7f..0d128bd0e383 100644 --- a/cli/connhelper/connhelper.go +++ b/cli/connhelper/connhelper.go @@ -9,9 +9,12 @@ import ( "fmt" "net" "net/url" + "os" "slices" + "strconv" "strings" + "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/connhelper/commandconn" "github.com/docker/cli/cli/connhelper/ssh" ) @@ -50,6 +53,7 @@ func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper return nil, fmt.Errorf("ssh host connection is not valid: %w", err) } sshFlags = addSSHTimeout(sshFlags) + sshFlags = addMultiplexingArgs(sshFlags) sshFlags = disablePseudoTerminalAllocation(sshFlags) remoteCommand := []string{"docker", "system", "dial-stdio"} @@ -83,6 +87,24 @@ func GetCommandConnectionHelper(cmd string, flags ...string) (*ConnectionHelper, }, nil } +func addMultiplexingArgs(sshFlags []string) []string { + if v := os.Getenv("DOCKER_SSH_NO_MUX"); v != "" { + if b, err := strconv.ParseBool(v); err == nil && b { + return sshFlags + } + } + if err := os.MkdirAll(config.Dir(), 0o700); err != nil { + 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) + } else { + sshFlags = append(sshFlags, "-o", "ControlPersist=60") + } + return sshFlags +} + func addSSHTimeout(sshFlags []string) []string { if !strings.Contains(strings.Join(sshFlags, ""), "ConnectTimeout") { sshFlags = append(sshFlags, "-o ConnectTimeout=30") diff --git a/docs/reference/commandline/docker.md b/docs/reference/commandline/docker.md index b926315defde..2037783c5143 100644 --- a/docs/reference/commandline/docker.md +++ b/docs/reference/commandline/docker.md @@ -126,11 +126,12 @@ The following environment variables control the behavior of the `docker` command | `DOCKER_DEFAULT_PLATFORM` | Default platform for commands that take the `--platform` flag. | | `DOCKER_HIDE_LEGACY_COMMANDS` | When set, Docker hides "legacy" top-level commands (such as `docker rm`, and `docker pull`) in `docker help` output, and only `Management commands` per object-type (e.g., `docker container`) are printed. This may become the default in a future release. | | `DOCKER_HOST` | Daemon socket to connect to. | +| `DOCKER_SSH_NO_MUX` | If set will turn off SSH multiplexing when connecting to daemon through SSH. | +| `DOCKER_SSH_MUX_PERSIST` | Set a duration for keeping SSH multiplexing socket alive between commands (e.g `60s`). | | `DOCKER_TLS` | Enable TLS for connections made by the `docker` CLI (equivalent of the `--tls` command-line option). Set to a non-empty value to enable TLS. Note that TLS is enabled automatically if any of the other TLS options are set. | | `DOCKER_TLS_VERIFY` | When set Docker uses TLS and verifies the remote. This variable is used both by the `docker` CLI and the [`dockerd` daemon](https://docs.docker.com/reference/cli/dockerd/) | | `BUILDKIT_PROGRESS` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`) when [building](https://docs.docker.com/reference/cli/docker/image/build/) with [BuildKit backend](https://docs.docker.com/build/buildkit/). Use plain to show container output (default `auto`). | -| `NO_COLOR` | Disable any ANSI escape codes in the output in accordance with https://no-color.org/ - | +| `NO_COLOR` | Disable any ANSI escape codes in the output in accordance with https://no-color.org/ | Because Docker is developed using Go, you can also use any environment variables used by the Go runtime. In particular, you may find these useful: