Seventh post in a series where I read through SSH man pages and write about things worth knowing. Previous posts: ObscureKeystrokeTiming, ChannelTimeout, Match version, Match sessiontype, Escape Sequences, AddKeysToAgent. Today I want to do back to the classics, I will talk about specific directives: ControlMaster, ControlPath, and ControlPersist, They are basically what we call SSH connection multiplexing directives.

Every time we run ssh host, the client does a lot of work: TCP handshake, key exchange, authentication, possibly agent forwarding negotiation. If we connect to the same host ten times in a row, maybe because we're running scp, then we open a shell, then doing rsync, then another scp, that entire dance happens ten times. On a high-latency link, this adds up fast. On hosts where authentication involves MFA or Kerberos ticket negotiation (hello, lxplus), it's actively painful.

SSH multiplexing lets us reuse a single connection for multiple sessions. The first connection does the full handshake and authentication. Every subsequent connection to the same host piggybacks on it, new sessions open nearly instantly because they skip the entire TCP and crypto setup. Three directives control this. ControlMaster enables it, ControlPath tells ssh where to put the Unix domain socket used for multiplexing, and ControlPersist keeps the master connection alive after the first session closes.

Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 10m

ControlMaster auto means: if no master connection exists, become one; if one already exists, reuse it. The auto value is what we want for everyday use. There's also yes (always be master, never reuse) and no (never be master), but auto handles both cases.

ControlPath is the location of the Unix socket file. The %r, %h, and %p tokens expand to the remote user, hostname, and port, so we get one socket per unique connection target. We'll want to create the directory first:

mkdir -p ~/.ssh/sockets

If we skip this, ssh will fail with a somewhat cryptic error about not being able to create the socket. The path needs to be short enough to fit within the OS limit for Unix socket paths (typically 104 or 108 bytes), so don't put it in a deeply nested directory.

ControlPersist 10m means the master connection stays alive for 10 minutes after the last session using it disconnects. Without ControlPersist, the master connection dies when the first session that created it exits, which defeats the purpose if we're opening and closing sessions frequently. Setting it to yes makes the master persist indefinitely (until we manually kill it or the server disconnects). A time value like 10m or 1h is usually more sensible.

The speed difference is noticeable. On a connection to a host behind a VPN with ~100ms latency, the first ssh host takes a few seconds for key exchange and authentication. Subsequent connections while the master is alive take under 200ms. For scripting workflows that SSH into the same host repeatedly, deploy scripts, batch file copies, Ansible runs, this can dramatically reduce total runtime.

There are a few gotchas. If the master connection dies uncleanly (network drops, laptop sleeps), the socket file can be left behind, and new connections will hang trying to connect to a dead socket before eventually falling back to a fresh connection. We can clean up stale sockets manually (rm ~/.ssh/sockets/*) or use ssh -O check host to test if a master is alive and ssh -O exit host to shut one down cleanly.

The -O flag gives us manual control over the master. ssh -O forward -L 8080:localhost:80 host adds a port forward to an existing master without opening a new session (similar to the ~C escape sequence from a few posts ago, but from the command line). ssh -O cancel -L 8080:localhost:80 host removes it.

One important security note: anyone who can access the socket file can piggyback on our multiplexed connection without authenticating. The socket is protected by filesystem permissions (it's in our ~/.ssh/ directory, mode 0600), but on shared machines we should be aware this is how it works. If that's a concern, don't use multiplexing on hosts where others have root access.

OpenSSH 10.0 made a relevant change here: scp and sftp now pass ControlMaster no to ssh by default. Previously, if we had ControlMaster auto in our config, running scp or sftp could accidentally create a master connection that then stuck around. Now they'll reuse an existing master but won't create one, which is generally the behavior we'd expect from file transfer tools.

I've used multiplexing for years, and it's one of those things where once we set it up, we forget about it, everything just feels faster. The three lines in ssh_config are all it takes. But I need to be careful about some security implications and edge cases, especially on shared machines or when I'm using lxplus as a hub for connecting to other machines.