Elegant OpenSSH configuration
This is one part in a series on OpenSSH client configuration. Also read Secure OpenSSH defaults and The SSH agent.
The OpenSSH client is very robust, verify flexible, and very configurable. Many times I see people struggling to remember server-specific ssh flags or arcane, manual multi-hop procedures. I even see entire scripts written to automate the process.
But the vast majority of what you might want ssh to do can be abstracted
away with some configuration in your ~/.ssh/config
file.
All (or, at least, most) of these configuration directives are fully documented in the ssh_config manpage.
If you prefer, follow along with an example of a complete ~/.ssh/config file.
HostName
One of the first annoyances people have–and one of the first things
people try to fix–when using a command-line ssh client is having to type
in long hostnames. For example, the Research Computing login service is
available at login.rc.colorado.edu
.
$ ssh login.rc.colorado.edu
This particular name isn’t too bad; but coupled with usernames and
especially when used as part of an scp
, these fully-qualified domain
names can become cumbersome.
$ scp -r /path/to/src/ user1234@login.rc.colorado.edu:dest/
OpenSSH supports host aliases through pattern-matching in Host
directives.
Host login*.rc HostName %h.colorado.edu Host *.rc HostName %h.int.colorado.edu
In this example, %h
is substituted with the name specified on the
command-line. With a configuration like this in place, connections to
login.rc
are directed to the full name login.rc.colorado.edu
.
$ scp -r /path/to/src/ user1234@login.rc:dest/
Failing that, other references to hosts with a .rc
suffix are
directed to the internal Research Computing domain. (We’ll use these
later.)
(The .rc
domain segment could be moved from the Host
pattern to
the HostName
value; but leaving it in the alias helps to distinguish
the Research Computing login nodes from other login nodes that you may
have access to. You can use arbitrary aliases in the Host
directive,
too; but then the %h
substitution isn’t useful: you have to
enumerate each targeted host.)
User
Unless you happen to use the same username on your local workstation as
you have on the remove server, you likely specify a username using
either the @
syntax or -l
argument to the ssh
command.
$ ssh user1234@login.rc
As with specifying a fully-qualified domain name, tracking and
specifying a different username for each remote host can become
burdensome, especially during an scp
operation. Record the correct
username in your ~/.ssh/config
file in stead.
Match host=*.rc.colorado.edu,*.rc.int.colorado.edu User user1234
Now all connections to Research Computing hosts use the specified username by default, without it having to be specified on the command-line.
$ scp -r /path/to/src/ login.rc:dest/
Note that we’re using a Match
directive here, rather than a Host
directive. The host=
argument to Match
matches against the
derived hostname, so it reflects the real hostname as determined using
the previous Host
directives. (Make sure the correct HostName
is
established earlier in the configuration, though.)
ControlMaster
Even if the actual command is simple to type, authenticating to the host
may be require manual intervention. The Research Computing login nodes,
for example, require two-factor authentication using a password or pin
coupled with a one-time VASCO password or Duo credential. If you want to
open multiple connections–or, again, copy files using scp
–having to
authenticate with multiple factors quickly becomes tedious. (Even having
to type in a password at all may be unnecessary; but we’ll assume, as is
the case with the Research Computing login example, that you can’t use
public-key authentication.)
OpenSSH supports sharing a single network connection for multiple ssh sessions.
Match host=login.rc.colorado.edu ControlMaster auto ControlPath ~/.ssh/.socket_%h_%p_%r ControlPersist 4h
With ControlMaster
and ControlPath
defined, the first ssh
connection authenticates and establishes a session normally; but future
connections join the active connection, bypassing the need to
re-authenticate. The optional ControlPersist
option causes this
connection to remain active for a period of time even after the last
session has been closed.
$ ssh login.rc user1234@login.rc.colorado.edu's password: [user1234@login01 ~]$ logout $ ssh login.rc [user1234@login01 ~]$
(Note that many arguments to the ssh
command are effectively ignored
after the initial connection is established. Notably, if X11 was not
forwarded with -X
or -Y
during the first session, you cannot use
the shared connection to forward X11 in a later session. In this case,
use the -S none
argument to ssh
to ignore the existing
connection and explicitly establish a new connection.)
ProxyCommand
But what if you want to get to a host that isn’t directly available
from your local workstation? The hosts in the rc.int.colorado.edu
domain referenced above may be accessible from a local network
connection; but if you are connecting from elsewhere on the Internet,
you won’t be able to access them directly.
Except that OpenSSH provides the ProxyCommand
option which, when
coupled with the OpenSSH client presumed to be available on the
intermediate server, supports arbitrary proxy connections through to
remotely-accessible servers.
Match host=*.rc.int.colorado.edu ProxyCommand ssh -W %h:%p login.rc.colorado.edu
Even though you can’t connect directly to Janus compute nodes from the
Internet, for example, you can connect to them from a Research
Computing login node; so this ProxyCommand
configuration allows
transparent access to hosts in the internal Research Computing domain.
$ ssh janus-compile1.rc [user1234@janus-compile1 ~]$
And it even works with scp
.
$ echo 'Hello, world!' >/tmp/hello.txt $ scp /tmp/hello.txt janus-compile1.rc:/tmp hello.txt 100% 14 0.0KB/s 00:00 $ ssh janus-compile1.rc cat /tmp/hello.txt Hello, world!
Public-key authentication
If you tried the example above, chances are that you were met with an unexpected password prompt that didn’t accept any password that you used. That’s because most internal Research Computing hosts don’t actually support interactive authentication, two-factor or otherwise. Connections from a CURC login node are authorized by the login node; but a proxied connection must authenticate from your local client.
The best way to authenticate your local workstation to an internal CURC host is using public-key authentication.
If you don’t already have an SSH key, generate one now.
$ ssh-keygen -t rsa -b 4096 # if you don't already have a key
Now we have to copy the (new?) public key to the remote CURC
~/.ssh/authorized_keys
file. RC provides a global home directory, so
copying to any login node will do. Targeting a specific login node is
useful, though: the ControlMaster
configuration for
login.rc.colorado.edu
tends to confuse ssh-copy-id
.
$ ssh-copy-id login01.rc
(The ssh-copy-id
command doesn’t come with OS X, but theres a
third-party port available on
GitHub. It’s
usually available on a Linux system, too. Alternatively, you can just
edit ~/.ssh/authorized_keys
manually.)