OpenSSH/Cookbook/Multiplexing

From Wikibooks, open books for an open world
< OpenSSH
Jump to: navigation, search

Multiplexing is the ability to send more than one signal over a single line or connection. With multiplexing, OpenSSH can re-use an existing TCP connection for multiple concurrent SSH sessions rather than creating a new one each time.

Advantages of Multiplexing[edit]

An advantage with SSH multiplexing is that the overhead of creating new TCP connections is eliminated. The overall number of connections that a machine may accept is a finite resource and the limit is more noticeable on some machines than on others, and varies greatly depending on both load and usage. There is also significant delay when opening a new connection. Activities that repeatedly open new connections can be significantly sped up using multiplexing.

The difference multiplexing and standalone sessions can be seen by comparing the tables below. We see in the table "SSH Connections, Separate" that without multiplexing each new login creates a new TCP connection, one per login session. Following that we see in the other table, "SSH Connections, Multiplexed", that when multiplexing is used, new logins are channeled over the already established TCP connection.

#              Local Address       Foreign Address         State

# one connection
tcp    0   0   192.168.x.y:45050   192.168.x.z:22       ESTABLISHED

# two separate connections
tcp    0   0   192.168.x.y:45050   192.168.x.z:22       ESTABLISHED
tcp    0   0   192.168.x.y:45051   192.168.x.z:22       ESTABLISHED

# three separate connections
tcp    0   0   192.168.x.y:45050   192.168.x.z:22       ESTABLISHED
tcp    0   0   192.168.x.y:45051   192.168.x.z:22       ESTABLISHED
tcp    0   0   192.168.x.y:45052   192.168.x.z:22       ESTABLISHED

Table 1: SSH Connections, Separate

Both tables show TCP/IP connections associated with SSH sessions. The table above shows a new TCP/IP connection for each new SSH connection. The table below shows a single TCP/IP connection despite multiple active SSH sessions.

#              Local Address       Foreign Address         State

# one connection
tcp    0   0   192.168.x.y:58913   192.168.x.z:22       ESTABLISHED

# two multiplexed connections
tcp    0   0   192.168.x.y:58913   192.168.x.z:22       ESTABLISHED

# three multiplexed connections
tcp    0   0   192.168.x.y:58913   192.168.x.z:22       ESTABLISHED

Table 2: SSH Connections, Multiplexed

As we can see, with multiplexing only a single TCP connection is set up and used regardless of whether or not there are multiple SSH sessions carried over it.

Or we can compare the time it takes to run ls(1) on a slow remote server, using time(1), first without multiplexing:

real    0m0.658s
user    0m0.016s
sys     0m0.008s

Then we do the same, but use a multiplexed connection:

real    0m0.029s
user    0m0.004s
sys     0m0.004s

The difference is quite large and will definitely add up for any activity where connections are made repeatedly.

Setting Up Multiplexing[edit]

OpenSSH does multiplexing using the ControlMaster, ControlPath and ControlPersist configuration directives. All three are described in the manual page for ssh_config(5). They can be specified in ssh_config(5) (~/.ssh/config) or they can be set on-demand at run time using the -o parameter. The run time parameters -M and -S also correspond to ControlMaster and ControlPath, respectively.

ControlMaster determines whether ssh(1) will listen for control connections and what to do about them. ControlPath is the location for the control socket used by the multiplexed sessions. These can be set globally or locally in ssh_config(5) or specified at run time. Control sockets are removed automatically when the master connection is ended. ControlPersist can be used in conjunction with ControlMaster. If set to 'yes', it will leave the master connection open in the background indefinitely to accept new connections until either killed explicitly or closed with -O.

Establishing Multiplexed Connections[edit]

Multiplexed sessions need a control master to connect to. So first an initial master connection is established using ControlMaster or -M.

$ ssh -M -S /home/fred/.ssh/controlmasters/fred@server.example.org:22 server.example.org

Then subsequent multiplexed connections are made in other terminals. They use ControlPath or -S to point to the control socket.

$ ssh -S /home/fred/.ssh/controlmasters/fred@server.example.org:22 server.example.org

Note that the control socket is named "fred@server.example.org:22", or %r@%h:%p, to try to make the name unique. The sockets should be given unique names. The combination %r,  %h and %p stand for the remote user name, the remote host and the remote host's port.

The way below sets configuration options as run time parameters. First, a control master is started.

$ ssh -o "ControlMaster=yes" -o "ControlPath=/home/fred/.ssh/controlmasters/%r@%h:%p" server.example.org

Then subsequent sessions are connected to the control master via socket at the end of the control path.

$ ssh -o "ControlPath=/home/fred/.ssh/controlmasters/%r@%h:%p"  server.example.org

And of course all that can be put into ssh_config.

Host machine1
  HostName machine1.example.org
  ControlPath ~/.ssh/controlmasters/%r@%h:%p
  ControlMaster autoask
  ControlPersist yes

In the examples above, the control socket is placed in the directory ~/.ssh/controlmasters/.

Ending Multiplexed Connections[edit]

One way to end multiplexed sessions is to exit all related ssh sessions, including the control master. If the control master has been placed in the background using ControlPersist, then it will be necessary to stop it with -O and either stop or exit. That also requires knowing the full path and filename of the control socket.

$ ssh -O stop -S ~/.ssh/controlmasters/fred@server.example.org:22 server.example.org

The multiplex command -O stop will gracefully shutdown multiplexing. After issuing the command, the control socket is removed and no new multiplexed sessions are accepted for that master. Existing connections are allowed to continue. The multiplex command -O exit removes the control socket and immediately terminates all existing connections.

The directive ControlPersist can also be set to a timeout in seconds or a time in a format listed in sshd_config(5). In that case the master connection will automatically close if it has no client connections for the specified time.

Host machine1
  HostName machine1.example.org
  ControlPath ~/.ssh/controlmasters/%r@%h:%p
  ControlMaster yes
  ControlPersist 2h

The above example lets the control master timeout after 2 hours of inactivity.

Multiplexing Options[edit]

The values for either ControlMaster or ControlPath or both can be set either in the user-specific ssh_config(5) or the global /etc/ssh/ssh_config or using parameters when running from the shell or a script. ControlMaster can be overridden in the run time arguments to re-use an existing master when ControlMaster is set to 'yes', by setting it explicitly to 'no':

$ ssh -o "ControlMaster=no" server.example.org

ControlMaster accepts five different values: no, yes, ask, auto, and autoask. ControlMaster=no is the default. New sessions will not try to connect to an established master session, but additional sessions can still multiplex by connecting explicitly to an existing socket. ControlMaster=yes creates a new master session each time, unless explicitly overridden. The new master session will listen for connections. ControlMaster=ask creates a master each time, unless over ridden, and listen for connections. If overridden, ssh-askpass(1) will popup an message in X to ask the master session owner to approve or deny the request. If the request is denied, then the session being created falls back to being a regular, standalone session. ControlMaster=auto is one is a bit dangerous. If there is a master session available, subsequent sessions are automatically multiplexed. ControlMaster=autoask automatically assume that if a master session exists, that subsequent sessions should be multiplexed, but ask first as with 'ask'.

'ask' queries user via X ssh-askpass(1). Refused connections are logged to the master session.

Host * 
        ControlMaster ask 
        ControlPath ~/.ssh/controlmasters/mux-%r@%h:%p 

Alternately, the initial control connection and subsequent multiplexed sessions can be invoked as run time options. -M places the SSH client into master mode for connection sharing. Multiple -M options places ssh(1) into master mode with confirmation required before slave connections are accepted. This is the same as ControlMaster=ask. Both require X to ask for the confirmation.

Here is the master connection for the host server.example.org as set to ask for confirmation of new multiplexed sessions:

$ ssh -MM -S ~/.ssh/controlmasters/fred@server.example.org:22 server.example.org

And here is a subsequent multiplexed connection:

$ ssh -S ~/.ssh/controlmasters/fred@server.example.org:22  server.example.org

The status of the control master connection can be queried using -O check, which will tell if the it is running or not.

$ ssh -O check -S ~/.ssh/controlmasters/fred@server.example.org:22  server.example.org

If the control session has been stopped, the query will return an error about "No such file or directory" even if there are still multiplexed sessions running because the socket is gone.

It is possible to request port forwarding without having to establish new connections. Here we forward port 8080 on the local host to port 80 on the remote host using -L:

$ ssh -O forward -L 8080:localhost:80 -S ~/.ssh/controlmasters/fred@server.example.org:22  server.example.org

The same can be done for remote forwarding, using -R. The escape sequence ~C is not available for multiplexed sessions, so -O forward is the only way to add port forwarding on the fly.

Port forwarding can be canceled without having to close any sessions using -O cancel.

$ ssh -O cancel -L 8080:localhost:80 -S ~/.ssh/controlmasters/fred@server.example.org:22  server.example.org

The exact same syntax used for forwarding is used for cancelling. However, there is no way currently to look up which ports are being forwarded and how they are forwarded.

Additional Notes About Multiplexing[edit]

Never use any publicly accessible directory for the control path sockets. Place those sockets in a directory somewhere else, one to which only your account has access. For example ~/.ssh/socket/ would be a much safer choice and /tmp/ would be a bad choice.

In cases where it is useful to have a standing connection, it is always possible to combine multiplexing with other options, such as -f or -N to have the control master drop to the background upon connection and not load a shell.

In sshd_config(5), the directive MaxSessions specifies the maximum number of open sessions permitted per network connection. This is used when multiplexing ssh sessions over a single TCP connection. Setting MaxSessions to 1 disables multiplexing and setting it to 0 disables login/shell/subsystem sessions completely. The default is 10. The MaxSessions directive can also be set under a Match conditional block.

Errors Preventing Multiplexing[edit]

The directory used must be on a file system that allows the creation of sockets. AFS is one example, as are some implementations of HFS+. If an attempt is made at creating a socket on a file system that does not allow it, an error something like the following will occur:

$ ssh -M -S /home/fred/.ssh/mux 192.0.2.57
muxserver_listen: link mux listener /home/fred/.ssh/mux.vjfeIFFzHnhgHoOV => /home/fred/.ssh/mux: Operation not permitted

If the file system cannot be reconfigured to allow sockets, then the only other option is to place the control path socket somewhere else on a file system that does support creation of sockets.

Multiplexing HTTPS and SSH Using sslh[edit]

A different kind of multiplexing is when more than one protocol is carried over the same port. sslh does just that with SSL and SSH. It figures out what kind of connection is coming in and forwards it to the appropriate service. Thus it allows a server to receive both HTTPS and SSH over the same port. Note that this does not actually hide SSH. A scan for SSH on the listening port, say with scanssh(1), will show that it is there. What it does is make it possible to have one less port open in the firewall. Here's how sslh(8) can be installed in four steps:

  • First install your web server and configure it to accept HTTPS. Be sure that it listens for HTTPS on the localhost only. It can also help if it is on a non-standard port, say 2443, instead of 443.
  • Next, set your SSH server to accept connections on the localhost for port 22. It really could be any port, but port 22 is the standard for SSH.
  • Next, create an unprivileged user to run sslh(8). The example below has the user sslh for the unprivileged account.
  • Lastly, install and launch sslh(8) so that it listens on port 443 and forwards HTTPS and SSH to the appropriate ports on the localhost. Substitute the external IP number for your machine. The actual name of the executable and path may vary from system to system:
$ /usr/local/sbin/sslh-fork -u sslh -p xx.yy.zz.aa:443 --tls 127.0.0.1:2443 --ssh 127.0.0.1:22

Another option is to use a configuration file with sslh(8) and not pass any parameters at runtime. There should be at least one sample configuration file, basic.cfg or example.cfg, included in the package when it is installed. The finished configuration file should look something like this:

user: "sslh";
listen: ( { host: "xx.yy.zz.aa"; port: "443" } );
on-timeout: "ssl";
protocols:
(
   { name: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },
   { name: "ssl"; host: "localhost"; port: "2443"; probe: "builtin"; }
);

Mind the quotes, commas, and semicolons.

If SSH is used in conjunction with TCP Wrappers, as described in hosts_access(5), then the option service: works to provide the name of the service that they need.

   { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },

If TCP Wrappers are not used, which is most likely the case, then service: is not needed.

Runtime parameters override any configuration file settings that may be in place. sslh(8) supports the protocols HTTP, SSL, SSH, OpenVPN, tinc, and XMPP out of the box. But actually any protocol that can be identified by regular expression pattern matching can be used. There are two variants of sslh, a forked version (sslh or sslh-fork) and a single-threaded version (sslh-select).