Intro

In this blog, we’ll dig into three different types of port forwarding you can do with SSH. We’ll look at what use cases each is good for, and how to actually use it in practice.

Generally, SSH port forwarding allows you to securely forward traffic between 2 or more ports. There are three major types of port forwarding offered by OpenSSH and other popular SSH clients like PuTTY. Let’s give them some vague definitions and then jump into each one and get some more depth. Assume you have your client machine (the one you connect from), and a remote SSH server (the server you connect to and tunnel through).

Local Port Forwarding

Opens a listening socket on the client and forwards it to a specified host and port, through the remote SSH server.

Remote Port Forwarding

Opens a listening socket on the remote SSH server and forwards it to a specified host and port, through the client.

Dynamic Port Forwarding

Opens a listening socket (SOCKS server) on the client and forwards to a dynamically specified address based on the application request.

Digging Deeper

OK, now that we’ve got the vague definitions out of the way, let’s look at some examples of how you would use these, and when you would pick one over the other.

Local Port Forwarding

Let’s pretend we have a webserver running on our remote SSH server. It’s running on port 80, but it’s only bound to localhost because the administrator is extremely cautious, and everyone who needs to use the webserver on that port already has SSH access. Oh yeah, it’s also port 80, as you might have noticed. Again, the admin knows everyone is going to be tunneling over SSH, so he was too lazy to setup a certificate.

This administrator may or may not be me. Anyways…

How do we do this with SSH? Well, with a simple local port forward:

local:~$ ssh -L localhost:8080:localhost:80 user@remote

OK, let’s break this down. We indicate we want a local port forward with -L. The first host:port pair is where the socket will listen. In this case, it’s port 8080 on our client. The second host:port pair (after the ‘:’), is the other end of the tunnel. So, whenever we send a packet to localhost:8080 on the client, it gets forwarded to localhost:80 on the remote server. The response comes back through the tunnel.

Now, just point your browser at localhost:8080, and you’ll see the remote service!

You can set the tunnel endpoint to another host, you’re essentially using the remote SSH server to proxy that one port to that one specific host and port. Let’s see an example:

local:~$ ssh -L localhost:8080:vncserver:5100 user@remote

Above, we have a 3rd host, vncserver, whose firewall is configured to allow remote, but not our client, to connect to port 5100 for VNC. The tunnel allows our client to appear as if it’s coming from the trusted remote host.

On our client, we just connect our VNC client to localhost:8080 instead of vncserver:5100.

Remote Port Forwarding

If you understand local port forwarding (you should if you’re this far into the article), remote port forwarding should come easy. It’s essentially the same as local port forwarding, but the listening socket is opened on the remote host, and the traffic is tunneled through the client. It’s a local port forward, but in reverse.

When is this useful? Well, say you’re that admin from the local example. Now, say someone wants to connect to your private webserver, but they don’t have SSH access to your machine, and don’t even know how to operate SSH. You have SSH access to their machine, so why not just setup the forwarding for them? By remote forwarding a port, you just have to tell the user to connect to localhost:8080 after you setup the tunnel:

local:~$ ssh -R localhost:8080:webserver:80 user@remote

Note, the first argument to -R and -L is always the tunnel entrance, relative to that host.

You also technically don’t have to specify the initial bind address (the first occurrence of “localhost” in both examples), but I think it makes things more clear. Generally you’ll be using “localhost” unless you want other hosts to be able to use your tunnel entrance. You can specify a blank host, or a “*” to bind to all available interfaces. This behavior can be governed by the SSH server with the GatewayPorts config directive.

Okay, onto the third kind!

Dynamic Port Forwarding

Now, our above examples used a webserver example, but local and remote port forwarding can be applied to any TCP services. They’re great when you know the host and port you want to forward to, and when it’s not going to change frequently.

Maybe you want to forward all of your traffic through a remote SSH server, not just traffic bound to a specific private service?

Maybe you’re out on public wifi somewhere, and you want ot proxy your traffic securely through your SSH server?

You can do this with dynamic port forwarding! Essentially, it just sets up a SOCKS proxy listening socket on the client, that gets forwarded through the remote server. You can start one up like this:

local:~$ ssh -D 8080

Now just open the network settings in your application or operating system, and point them towards localhost:8080 where the SOCKS server is waiting. I use this all the time for private browsing on the go without setting up a whole VPN.

Summary

So, when to use each type:

  1. Use Local Port Forwarding when you need to forward one local port to another port.
  2. Use Remote Port Forwarding when you need to forward one remote port to another port.
  3. Use Dynamic Port Forwarding when you need to forward to multiple destinations and your application or operating system supports SOCKS proxy.