Example NetworkTake the example scenario pictured at right. Client A wishes to talk to Server C through the internet... but Server C has no direct internet connection of its own!

In fact, the ONLY machine on the destination network that can talk to the internet is Server "B". So, if Client A is going to talk to Server C, it HAS to be through Server B.

This should be possible because Client A can get to Server B through the internet, and Server B can can talk to Server C on their shared local network. All Server B has to do is agree to relay, or "tunnel", messages between A and C for us.

But how do we get Server B to act as our relay/tunnel?

Building A Tunnel

We need to let Server B know (1) that we want it to relay for us, (2) What we want it to relay and (3) Where we want it to relay to.

What to Relay

Remember that Server B may be handling lots of traffic from lots of different computers - and lots of different types of traffic as well. Perhaps a video chat session with one machine and an HTTP "web" session with another. In fact, Server B may be having multiple, simultaneous conversions with just Client A - only some of which should be tunneled. So, as it sends data, Client A has to clarify which traffic is intended for Server B itself, and which traffic it would like Server B to forward on to another "remote" destination.

The way that computers keep this all straight is by using "Ports". E.g. web sites typically accept requests on port 80 for plain text traffic, or port 443 for encrypted traffic. To make this work, we'll "open up" our own new, dedicated port through which we will send all traffic that we want Server B to tunnel/forward for us. The relaying host, Server B, will know that anything arriving from that port should be forwarded on our behalf.

This will be a local "forwarded" port number that we can pick arbitrarily, maybe 1117 because we happen to like prime numbers, or 1225 for Christmas Day, or even our birthday, etc. The number is immaterial except for these caveats:

  1. The port must not already be in use for anything else
  2. The number should be greater than 1024, because:
    • On linux machines, port numbers below 1024 can only be used by "root", or administrative, users
    • These lower port numbers are reserved for specific functions and using them for anything else is generally a bad idea anyway
  3. The number must be less than 65,536 (2^16), which is the most ports any computer can have active at a time.

Where to Relay It

If Server B agrees to relay for us (and it may not, for security reasons), then we have to let it know exactly where we want the data sent. Now, it is possible that we think of this "target" computer as "smalliron.somedomain.com". But Server B may think of it as simply "griddle". Which name/address should we use? That's easy, because we have no choice - we have to supply a name that Server B understands or it can't know where to send our data! So, we will ask Server B to relay to another machine, always identified by whatever name Server B uses for that machine. Note that in many cases that name will not work for us, but all that matters is that it works for Server B.

Furthermore, we need to tell the relaying machine what port number to specify as the target when it sends data on to it's final destination machine for us. This does not have to be the same as the port from which we will send it from our own machine. That is, data may leave our machine via port 1234 and end up at a target machine on port 9876.

Defining The Tunnel

To summarize, we're going to construct a command that uses this information:

  1. What port will we send from? (This is the "tunnel" port - all traffic sent here is relayed for us.)
  2. The name of the "remote", aka "target", machine we want to forward to as our final destination
  3. The port number on that target machine
  4. The host that we are going to ask to "tunnel" for us.
Here's the command format that contains all that information:
ssh -L Lport:targetHostname:Rport Rhost

where:

  • "-L" (for Local): This just tells SSH that we want to establish a "Local Tunnel"
  • "Lport": is the number of our LOCAL PORT, to which we will send all data we want to be tunneled
  • "targetHostname": the name that our internet connected machine (Server B) can use to contact the remote machine (Server C).
  • "Rport": is the number of a REMOTE PORT on the machine we are tunneling our traffic to - the final destination port.
  • "Rhost": the ssh server that we talk to directly, and are asking to forward traffic for us... our "Relay Host".
Here's a specific example:
ssh -L 1234:griddle:443 smalliron.somedomain.com

This causes:

  • an ssh connection to "smalliron.somedomain.com"
  • our local port 1234 to be forwarded for us, by smalliron.somedomain.com
  • data will be forwarded to the host that smalliron.somedomain.com refers to as "griddle",
  • and arrive at "griddle" on port 443
Therefore, we can open a web browser and go to "https://localhost:1234" with the result that:
  • data we send to our own port 1234 (such as this example web request: https://localhost:1234)
  • will be passed to the machine we have an ssh connection with (smalliron.somedomain.com)
  • and be forwarded from there to griddle on port 443, resulting in a secure web connection.
This gives the same result as if we had been able to establish a direct connection to "griddle" (without the tunneling) and then gone to the webpage "https://griddle:443" directly.

In fact, in this command,

ssh -L 1234:griddle:443 smalliron.somedomain.com 

you can think of

  • "griddle:443" as what you really want, and
  • "-L 1234" as the "Localhost:Port" you'll have to use instead.
Good luck!