Instead of using a forced command, here's another way to connect by SSH through a gateway: forward a port on client C to the SSH server on S, using an SSH session from C to G, and then run a second SSH session through the first (see Figure 11-16).
# Execute on client C $ ssh -L2001:S:22 G # Execute on client C in a different shell $ ssh -p 2001 localhost
This connects to server S by carrying the second SSH connection (from C to S) inside a port-forwarding channel of the first (from C to G ). You can make this more transparent by creating a nickname S in your client configuration file:
# ~/.ssh/config on client C host S hostname localhost port 2001
Now the earlier commands become:
# Execute on client C $ ssh -L2001:S:22 G # Execute on client C in a different shell $ ssh S
Because this technique requires a separate, manual step to establish the port forwarding, it is less transparent than the one in Making Transparent SSH Connections. However, it has some advantages. If you plan to use port or X forwarding between C and S with the first method, it's a little complicated. scp not only gives the -a switch to ssh to turn off agent forwarding, but also it gives -x and -o "ClearAllForwardings yes", turning off X and port forwarding. So you need to modify the earlier wrapper script to remove these unwanted options as well. See Authentication. Then, for port forwarding you need to set up a chain of forwarded ports that connect to one another. For example, to forward port 2017 on client C to port 143 (the IMAP port) on server S:
# ~/.ssh/config on client C host S hostname G user gilligan # ~/.ssh/authorized_keys on gateway G command="ssh -L1234:localhost:143 skipper@S" ...key... # Execute on client C $ ssh -L2017:localhost:1234 S
This works, but it's difficult to understand, error-prone, and fragile: if you trigger the TIME_WAIT problem , you have to edit files and redo the tunnel just to pick a new ephemeral port to replace 1234.
Using the SSH-in-SSH technique instead, your port and X-forwarding options operate directly between client C and server S in the usual, straightforward manner. The preceding example becomes:
# ~/.ssh/config on client C host S hostname localhost port 2001 # Execute on client C $ ssh -L2001:S:22 G # Execute on client C in a different shell $ ssh -L2017:localhost:143 S
This final command connects to server S, forwarding local port 2017 to the IMAP port on S.
The two methods just discussed differ in their security properties. Again, we assume the situation with machines C, G, and S as used earlier.
Using SCP Through a Gateway
SSH Case Studies: Connecting Through a Gateway Host
The first method was a chain of two SSH connections in series. The weakness with this is that if the SSH server in the middle (on G ) has been compromised, the session data is exposed. Data from C is decrypted by that server and passed to the second SSH client (also on G ), which then reencrypts it for transmission to S. So the session plaintext is recovered on G: a compromised server there has access to it and can read and alter it at will.
The second method, with port forwarding, doesn't suffer from this weakness. The server on G is in no special position with regard to observing the forwarded SSH connection from C to S. Any attempt to read or alter that session will fail, in the same way that network snooping or an active network attack will fail.
On the other hand, the port forwarding method is weaker than the chain-of-connections when implemented with SSH1 or OpenSSH, because it lacks server authentication. The reason for this is that the SSH1 and OpenSSH clients both behave specially when the server address is 127.0.0.1 ("localhost"): they force acceptance of the host key, regardless of what key is actually provided. More precisely: they omit checking the host key against the known-hosts list, behaving always as if the server-provided host key were associated with "localhost" in the list.
The reason for this feature is convenience. If a user's home directory is shared between machines, the SSH client on each machine sees the same per-user known-hosts file. But the name "localhost" is special, in that on each machine it means something different: that same host. So if the user employs ssh localhost on multiple machines, she will constantly get spurious warnings about the host key having changed. The known-hosts file maps "localhost" to the host key of the last host on which she did this, not the current one.
So the problem here is that, since the remote IP address of the SSH session from C to S is actually localhost, it effectively omits server authentication, and is thus vulnerable to a man-in-the-middle or spoofed server attack.
SSH2 doesn't have this special treatment of localhost and so doesn't exhibit the weakness. Its known-hosts list is also more fine-grained: it maps server sockets ([host,port] pairs) to keys, rather than server hosts. This means you can have separate keys for each locally forwarded port. So, to be as secure as possible, you don't just accept the server host key the first time you use ssh2 to connect from C to S over the forwarded port 2001 on C. Doing so circumvents server authentication for that first connection. Instead, before making the first connection, you should copy S 's host key into this file on C: ~/.ssh2/hostkeys/key_2001_localhost.pub. This associates S 's host key with the socket (localhost,2001), and you will have proper server authentication for the initial forwarded connection.
Daniel J. Barrett has been immersed in Internet technology since 1985. Currently, he is working as a software engineer. He is the author of O'Reilly's Linux Pocket Guide, and the coauthor of two more O'Reilly books: Linux Security Cookbook, and SSH, The Secure Shell: The Definitive Guide.
Richard E. Silverman first touched a computer as a college junior in 1986, when he logged into a DEC-20, typed "MM" to send some mail, and was promptly lost to the world.
View catalog information for SSH, The Secure Shell: The Definitive Guide
Return to ONLamp.com.
Copyright © 2009 O'Reilly Media, Inc.