by Brian Hatch
SSH is typically used for logging into remote servers so you have shell access to do maintenance, read your email, restart services, or whatever administration you require. SSH also offers some other native services, such as file copy (using
Whenever we SSH from one machine to another, we establish a secure encrypted session. This first article in this SSH series looked at properly verifying a server's host key, so that we can be sure that no attacker is able to perform a man-in-the-middle attack and gain access to read or manipulate what we do in that session. Other articles in this series looked at removing the need for static passwords using SSH user identities, and then using ssh-agent to automate the task of typing passphrases.
SSH also has a wonderful feature called SSH Port Forwarding, sometimes called SSH Tunneling, which allows you to establish a secure SSH session and then tunnel arbitrary TCP connections through it. Tunnels can be created at any time, with almost no effort and no programming, which makes them very appealing. In this article we look at SSH Port Forwarding in detail, as it is a very useful but often misunderstood technology. SSH Port Forwarding can be used for secure communications in a myriad of different ways. Let's start with an example.
Say you have a mail client on your desktop, and currently use it to get your email from your mail server via POP, the Post Office Protocol, on port 110. You may want to protect your POP connection for several reasons, such as keeping your password from going across the line in the clear, or just to make sure no one's sniffing the email you're downloading.
Normally, your mail client will establish a TCP connection to the mail server on port 110, supply your username and password, and download your email. You can try this yourself using
xahria@desktop$ nc mailserver 110 +OK SuperDuper POP3 mail server (mailserver.my_isp.net) ready. USER xahria +OK PASS twinnies +OK User successfully logged on. LIST +OK 48 1420253 1 1689 2 1359 3 59905 ... 47 3476 48 3925 . QUIT +OK SuperDuper POP3 mail server signing off. xahria@desktop$
We can wrap this TCP connection inside an SSH session using SSH Port Forwarding. If you have SSH access to the machine that offers your service (POP, port 110 in this case) then SSH to it. If you don't, you can SSH to a server on the same network if the network is trusted. (See the security implications of port forwarding later in this article.)
In this case, let's assume we don't have SSH access to the mail server, but we can log into a shell server on the same network, and create a tunnel for our cleartext POP connection:
# first, show that nothing's listening on our local machine on port 9999: xahria@desktop$ nc localhost 9999 Connection refused. xahria@desktop$ ssh -L 9999:mailserver:110 shellserver xahria@shellserver's password: ******** xahria@shellserver$ hostname shellserver
From a different window on your desktop machine, connect to your local machine (
xahria@desktop$ nc localhost 9999 +OK SuperDuper POP3 mail server (mailserver.my_isp.net) ready. USER xahria +OK PASS twinnies ...
Before we connected to the shellserver with SSH, nothing was listening on port 9999 on our desktop - once we'd logged in to the mail server with our tunnel, this port was bound by our SSH process, and the TCP connection to local port 9999 was magically tunneled through SSH to the other side.
Let's describe how this works in detail, using the example above.
SSH Port Forward Debugging
Let's see it in action by using the verbose option to
xahria@desktop$ ssh -v -L 9999:mailserver:110 shellserver debug1: Reading configuration data /etc/ssh/ssh_config debug1: Rhosts Authentication disabled, originating port will not be trusted. debug1: Connecting to shellserver [296.62.257.251] port 22. debug1: Connection established. debug1: identity file /home/bri/.ssh/identity type 0 debug1: identity file /home/bri/.ssh/id_rsa type 1 debug1: identity file /home/bri/.ssh/id_dsa type 2 ... debug1: Next authentication method: password xahria@shellserver's password: ******** debug1: Authentication succeeded (password). debug1: Connections to local port 9999 forwarded to remote address localhost:110 debug1: Local forwarding listening on 127.0.0.1 port 9999. debug1: channel 0: new [client-session] debug1: Entering interactive session. debug1: channel 0: request pty-req debug1: channel 0: request shell xahria@shellserver$
As you can see, there's a brief mention of port 9999 being bound and available for tunneling. We haven't made a connection to this port yet, so no tunnel is active yet. You can use the
xahria@shellserver$ (enter) xahria@shellserver$ (enter) xahria@shellserver$ (enter) xahria@shellserver$ ~# The following connections are open: #1 client-session (t4 r0 i0/0 o0/0 fd 5/6) xahria@shellserver$
You can see that there's only one connection, our actual SSH session from which we're typing those unix commands.
Now, in a different window if we do a
xahria@shellserver$ (enter) xahria@shellserver$ ~# The following connections are open: #1 client-session (t4 r0 i0/0 o0/0 fd 5/6) #2 direct-tcpip: listening port 9999 for mailserver port 110, connect from 127.0.0.1 port 42789 (t4 r1 i0/0 o0/0 fd 8/8)
You can see that now we have both the SSH session we're using, plus a tunnel, the second entry. It tells you all you need to know about the connection -- it came from our local machine (127.0.0.1) source port 42789, which we could look up with
SSH Forwards actually come in two flavours. The one I've shown above is a local forward, where the ssh client machine is listening for new connections to be tunneled. A Remote Forward is just the opposite - a tunnel initiated on the server side that goes back through the client machine.
The classic example of using a Remote Forward goes something like this. You're at work, and the VPN access is going to be down for maintenance for the weekend. However you really have some important work to do, but you'd rather work from the comfort of your desk at home, rather than being stuck at work all weekend. There's no way for you to SSH to your work desktop because it's behind the firewall.
Before you leave for the evening, you SSH from your work desktop back to your home network. Your
lainee@work$ tail ~/.ssh/config Host home-with-tunnel Hostname 204.225.288.29 RemoteForward 2222:localhost:22 User laineeboo lainee@work$ ssh home-with-tunnel email@example.com's password: ******** laineeboo@home$ ping -i 5 127.0.0.1 PING 127.0.0.1 (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=0.1 ms 64 bytes from 127.0.0.1: icmp_seq=1 ttl=255 time=0.2 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=255 time=0.2 ms ...
We've set up a tunnel using the
Later that evening, we can sit down on our home machine and see that we're logged in:
laineeboo@home$ last -1 laineeboo pts/18 firewall.my_work.com Tue Nov 23 22:28 still logged in laineeboo@home$ ps -t pts/18 PID TTY TIME CMD 3794 pts/18 00:00:00 ksh 4027 pts/18 00:00:00 ping -i 5 127.0.0.1
Now comes the payoff - our tunnel is listening on our home machine on port 2222, and will be tunneled back through the corporate firewall to our work machine's port 22. So to SSH to work from home, since we have our tunnel ready, we simply point
laineboo@home$ ssh -p 2222 lainee@localhost lainee@localhost's password: ******** lainee@work$
Port Forwarding Cheat Sheet
Remembering how to specify the kind of SSH Forward you want is sometimes tricky. Hopefully, the following table will make it a bit easier.
Forwards can be confusing - we typically think of connections as being made up of four things - the local IP and port, and the remote IP and port. In the forward definition you create, you only have three things because the first port is always either the SSH client or server machine, and thus isn't specified.
Port Forward Security
Port forwards bind a port on either the ssh client (Local Forwards) or ssh server (Remote Forwards). With a default installation, the port will only be bound on the localhost interface, 127.0.0.1. This means that the tunnel is only available to someone on the machine where that port is listening.
In general, you don't want to allow other machines to contact your SSH tunnel so this is the correct setting. If you want to allow these ports to be available to any machine, then use one of the following:
The other important thing you must remember is that the data connection is only encrypted inside the SSH connection. If your
desktop$ ssh -L 8080:www.example.com:80 somemachine
then any connection to
We'll see how you can put further limits on port forwards in a future article, such as rejecting or limiting them based on the SSH Pubkeys/Identity that is used for authentication.
 Some POP server software offers SSL-encrypted POP, by negotiating SSL using STARTTLS on port 110, or wrapped entirely in SSL on port 995. For this example, however, let's assume you have a non-SSL aware POP server.
 Some POP servers support alternate authentication methods, such as S/Key or challenge response, which can keep your password from going across the network.
 Unsniffable options would be localhost, 127.0.0.1 (or any 127/8 address on most unix-like systems) or any local IP address - these should all go through the local machine's TCP/IP stack without hitting the network card at all, and thus would be as secure as the network stack itself.
View more articles by Brian Hatch on SecurityFocus.
|Comments or reprint requests can be sent to the editor.|
This article originally appeared on SecurityFocus.com -- reproduction in whole or in part is not allowed without expressed written consent.