System administrators and other IT professionals who need to maintain or use Unix-based computers in the course of their duties generally use the Secure Shell protocol, usually known as SSH, to connect a command line prompt from a remote server to their own computer. SSH can also be used to transfer files, and to act as a transport for other protocols such as HTTP, HTTPS, FTP and so on.
However, it’s often not possible to connect directly. Many companies use a gateway machine as an additional line of defence against would-be intruders – so that their production hosts can only be accessed via a single machine; a point of entry from the Internet which is carefully maintained and secure. To complicate matters, a gateway machine can often only be accessed via a VPN.
This certainly allows for a secure environment, but it can substantially complicate remote access. Let’s say (for example) I need to login to a server at a company with a gateway machine accessible via a site to site VPN. If I’m doing this from home, I might login first to a server at e-DBA (to gain access to the site to site VPN), then from there to the gateway machine, and from there to the server in need of my attention.
One way to take some of the pain out of this is to organise SSH tunnels which will forward SSH traffic from a port on my local machine through both of the intermediate hosts and to the destination server automatically. This is quite a common approach, and it certainly works. But there’s a better way to forward traffic through multiple hosts, that’s often overlooked – by using the SSH ProxyCommand directive in an SSH config.
Using an SSH config file is itself an underused practice, despite the typing and errors it can save, so first a quick overview of how that works.
Let’s say I want to login directly to an Internet-facing host somewhere. The host allows SSH connections not on the default port, 22, but on port 2233. The login to which I have access is ‘oracle’. Access is via a private key, db2key.rsa.
To login there, I can do:
$ ssh -p 2233 -i ~/keys/db2key.rsa firstname.lastname@example.org
.. but I could instead have the SSH client remember the port, hostname, key and user for me by recording the details in its configuration file, ~/.ssh/config. The entry would look like this:
user oracle port 2233
With this in place, I can now login to the dbserver2 host like this:
$ ssh db2
Now, let’s suppose that I regularly need to login to a database server called jupiter at somecompany.net which is only accessible via their gateway machine, spock. Let’s further suppose that I can only connect to spock from our site to site VPN, so I need either to start the VPN, or connect first to a general purpose machine on our own company network. The SSH config entry for the e-DBA host might be:
I can now login to suzanne just by typing
$ ssh suzanne
.. but more importantly for the purposes of this piece, I can use the above configuration in an entry for the somecompany.net gateway machine which will allow me to login using a single command – using the SSH ProxyCommand directive to forward SSH traffic through automatically. This would take the following form:
ProxyCommand ssh suzanne -W %h:%p
With the above two entries in place in ~/.ssh/config on my own computer I can type
$ ssh spock
.. and I’ll appear to login there directly, though in actual fact all SSH traffic will be forwarded via suzanne, using the details from the first entry.
Note that the name provided in the hostname directive must in this case resolve from suzanne (I could instead have used the IP address). The location of the key given in the IdentityFile directive though is still a local pathname (in effect the authentication is passed through with the SSH traffic).
The Proxycommand directive specifies the command to connect to the server, so that the stdin / stdout of the command will be used as a transport for SSH.
So with the entry for the gateway machine spock in place, an additional entry for the database server, jupiter, can be added that will make use of it – in effect chaining the three configurations together recursively. It would look like this:
ProxyCommand ssh spock -W %h:%p
So this configuration tells SSH that connections to jupiter are routed through the gateway spock, which itself accepts connections through a machine on our own network, suzanne. And with the three entries in place on my local PC, even if working at home or otherwise away from the office, I can simply do:
jglaptop $ ssh jupiter
Last login: Thu Apr 9 06:11:00 2015 from polaski.somecompany,net
.. and I’m straight in, no need to start tunnels or fire up the company VPN. All communication with jupiter from this point will be directed through two intermediate hosts, but that’s handled by the SSH configuration and nicely transparent.
This technique is most valuable when used in conjunction with SSH keys for passwordless login, as illustrated in the example above. Traditional password-based authentication will still work, but you may be prompted for more than one password on the way to your destination, depending on the number of intermediate hosts set up to require passwords.