Ok, so like a bunch of other sites out there, I’m going to start this post with a complaint about the lack of well-written documentation on this subject on the Internet. Even Ubuntu’s instructions leave a lot to be desired (see the “Why Another Port Forwarding with OpenSSH Guide?” section below). I will attempt to remedy this situation by first providing the quick how-to for interested parties, but I will also perform some useful analysis at the end of the kind of inadequate documentation I find on the Internet regarding this process. I think it’s worth reading.
With OpenSSH, one can perform port forwarding (also known as “SSH tunneling”) using the SSH protocol to establish the connection between ports. There are three ways of doing this: Local Port Forwarding, Remote Port Forwarding, and Dynamic Port Forwarding. The first is by far the most common and useful, so I’m going to discuss it below:
Local Port Forwarding
Using the -L option with the ssh command, one can specify (in this order) a local port to be created, the host and the host’s port to which traffic sent to the local port will be forwarded, and the machine to which an SSH connection is being established. The machine receiving the SSH connection can be a machine with access to the host machine, or it can be the host machine itself (if it allows SSH connections and is accessible to the user). Once configured, traffic sent to the local port will be encapsulated in the SSH protocol and routed to the machine receiving the SSH connection. The machine receiving the SSH connection will then resolve the provided hostname (or use an IP address, if provided) and route unencapsulated traffic to the designated host and host port. It will route responses back to the local port so that this can be used as a full-fledged point of access for the service running on the host machine.
The syntax for the command is given in its somewhat inadequate man page (if I had an easy route to modifying these damn things, I’d personally author a very large percentage of them):
ssh -L [bind_address:]port:host:hostport [user@]hostname
The bind address is optional (if you don’t know your man page syntax standards, learn ’em!), but used to specify the local IP address to be used if you have a multi-homed system or something. Being rarely necessary, that’s the only portion of the command I won’t address below, so I thought I’d mention it here.
You have a user account named “user.” There is a machine named “webserver” which hosts a website using HTTP on port 8080 on a private network. You can only access the website if you are connected to the private network. You have a client machine named “client” to which your user account has access and which needs to browse said website, but it is not connected to the private network. You know, however, of a machine named “gateway” to which your user account has access, which is connected to the private network, and which is reachable by client.
You can configure a port forwarding arrangement whereby client can access the website through an SSH connection established with gateway. From client, you would perform:
[user@client ~]$ ssh -L 7000:webserver:8080 user@gateway
Reading the syntax from left to right, this causes the ssh facility on the client system to create local (that is, local to client) socket 7000 and forward communication sent to that socket (client:7000) to webserver:8080 through an SSH connection between client and gateway using the “user” account to log into the gateway machine and establish the connection.
Now, when you connect from client to local socket 7000 (with a browser, perhaps), you will be able to interact with webserver:8080. Communication sent to client’s socket 7000 will be redirected to gateway where the sshd daemon (on gateway) will proxy the traffic to webserver:8080. The connection between client and gateway will be HTTP “tunneled” through (or encapsulated by) SSH and the connection between gateway and webserver will be plain old HTTP.
That all looks like the following quickly-constructed, minimally acceptable diagram:
Some Useful Command Line Options
When tunneling traffic between systems, you probably only want to do just that. That is, you don’t want to actually connect to the gateway system over SSH and spawn a shell session. So, if you establish the tunnel as follows:
[user@client ~]$ ssh -f -N -L 7000:webserver:8080 user@gateway
The -f option instructs the ssh client to run in the background, so you aren’t placed into a shell session on gateway. The -N option instructs the ssh client not to execute any commands on the remote system (gateway), so that covers all of your bases: the SSH tunnel will only be used to establish the specified port-forwarding relationship.
Further, if you want to be able to use a browser on an entirely separate machine to access the website through the client system, throw in the -g option. As long as port 7000 is configured to accept connections through client’s firewall, you can use another system (say, client2) with access to client to browse to http://client:7000 and you will be able to navigate the website served up on http://webserver:8080 even though webserver (and gateway, even) is totally inaccessible to client2.
Why Another Port Forwarding with OpenSSH Guide?
This subject just seems to confuse people on the Internet left and right. As I said, even the Ubuntu instructions are seriously flawed*. Check out the following portion of their guide:
For example, say you wanted to connect from your laptop to http://www.ubuntuforums.org using an SSH tunnel. You would use source port number 8080 (the alternate http port), destination port 80 (the http port), and destination server http://www.ubuntuforums.org. :
Ok, let me stop you right there, Ubuntu guys. As I wrote above, the SSH tunnel does not extend to Ubuntu’s website. That doesn’t make any sense. Ubuntu’s website does not respond to SSH connection attempts made against port 80 (or at least, it better not!). If you remember our minimally acceptable diagram above, you remember that the SSH connection is made to a system whose sshd daemon will then establish a connection with the target host and port. As was stated, the target host itself can be used as that SSH endpoint only if the target host is accepting SSH connections and the user has access to the system over SSH.
But either way, that’s not what the Ubuntu guys are instructing the user to do. The command provided by the Ubuntu instructions is as follows:
ssh -L 8080:www.ubuntuforums.org:80 <host>
Where <host> should be replaced by the name of your laptop. The -L option specifies local port forwarding. For the duration of the SSH session, pointing your browser at http://localhost:8080/ would send you to http://www.ubuntuforums.org/.
That’s just incorrect. The command they are providing will create local port 8080 on the system on which the command is being typed (your laptop). It will then connect to the sshd daemon on <host> (your laptop, again!) and, from there, resolve and seek to connect to port 80 on the machine whose domain name is www.ubuntuforums.org. These instructions will cause the user to establish an ssh connection with his own system, encapsulate HTTP traffic and send it to himself, then have his sshd daemon unencapsulate that traffic and pass it on to http://www.ubuntuforums.org over HTTP.
That’s just silly. It is also misleading; you are not (nor can you, unless you have access to that machine over SSH) establishing a secure SSH tunnel with http://www.ubuntuforums.org:80. The SSH protocol is not a magical encryption system, bringing security to plaintext protocols across the Interwebs. However, if you do have SSH access to the host system which is providing the website you’d like to view, you could establish an SSH connection with the host system and have its sshd daemon communicate with the website over its loopback interface with a command like:
[user@client ~]$ ssh -f -N -L 7000:localhost:80 email@example.com
This would establish an SSH connection with http://www.ubuntuforums.org using the user account “user.” The “localhost” value actually refers to the loopback interface on the system to which the SSH connection is being made (which is, in this case, http://www.ubuntuforums.org). Any value you place in the host portion (which reads “localhost” in our example) of the command will be resolved by the system to which the SSH connection is being made, and that is how the entire port forwarding arrangement will be constructed.
So, the example command above would create socket 7000 on the client system and forward traffic sent to client:7000 to http://www.ubuntuforums.org:80. It will accomplish this by encapsulating that traffic in an SSH connection made between client and http://www.ubuntuforums.org, and then, upon reaching http://www.ubuntuforums.org, accessing port 80 on http://www.ubuntuforums.org using the loopback interface (and therefore never leaving the system) on http://www.ubuntuforums.org.
This would mean your connection to the ubuntuforums.org website would be entirely encrypted over SSH save for the traffic local to the http://www.ubuntuforums.org system. But, of course, neither you nor any of the intended audience members of that Ubuntu help page have SSH credentials permitting access to http://www.ubuntuforums.org from the Internet. Even if you did, the command provided by the help page would still fail because it’s directing you to connect to yourself and not http://www.ubuntuforums.org.
The scary part of this sort of misinformation is that the command won’t fail outright, but rather the unsuspecting user who executes it will mistakenly believe that he has configured a secure connection with http://www.ubuntuforums.org when all he’s done is to inject a superfluous step of talking to himself with an encrypted protocol before he actually talks to the http://www.ubuntuforums.org system over the same unencrypted protocol he would have used had he not messed with any of this SSH stuff to begin with.
And this brings me to a final point: Lots of people like to talk about SSH Tunneling as though it is an option for securing traffic which is otherwise insecure. This is not entirely accurate, as seen above. While the traffic encapsulated by the SSH tunnel will be encrypted, the traffic from the SSH endpoint to the ultimate destination host will not be encapsulated by SSH, so it will not gain this benefit.
Hopefully, this brings clarity to the matter for inquiring minds.
*I’m not trying to claim ultimate authority on this matter or anything, and I’m entirely open to being corrected, but this is the fruit of my research thus far and I believe it to be correct.