When I first saw port forwarding in action it seemed like magic, and I was immediately captivated. I’ve since been using SSH
to forward ports every single day for years.
What is it?
Essentially, it is tunneling traffic over an SSH
encrypted connection. Using the client/server paradigm, an SSH
client is started on the local machine which connects to an SSH
server on the remote machine.
How does it work?
When issuing an SSH
command, an encrypted tunnel is created between the initiating machine and the remote machine. Applications will then send their data into this tunnel to the other side. The tunnel can be thought of as a conduit for the traffic, and it doesn’t care what type of application-layer traffic it is forwarding, as it is merely passing the traffic between the communicating applications on either end.
When remote port forwarding, the remote machine sends traffic to the SSH
daemon listening on the remote machine, which in turn forwards the packets to the SSH
daemon on the other end. That daemon then sends the packets to the destination, the local application.
When local port forwarding, the local machine sends traffic to the SSH
daemon listening on the local machine, which in turn forwards the packets to the SSH
daemon on the other end. That daemon then sends the packets to the destination, the remote application.
So, these operations are inverses of each other, and all types of SSH
port forwarding are only one command away. Beautiful.
The applications don’t have to be on the same machine as the
SSH
daemons on either end of the tunnel, I’m simplifying to (hopefully) illustrate the point.
Why use it?
- Hide traffic on a public wifi
- Encrypt legacy applications
- Circumvent firewall rules
- VPN
- Do bad things
Examples
There are three types of port forwarding:
The examples use the following options (consult the man page):
f - Requests `ssh` to go to background just before command execution. C - Requests compression of all data. N - Do not execute a remote command. R - Remote port forwarding. L - Local port forwarding. D - Dynamic port forwarding.
If you get the error:
ssh: connect to host localhost port 22: Connection refused
Start the
ssh
daemon:# systemctl start ssh
Recall that the application connects to the local
SSH
client, which in turn forwards the request (and passes back the response), soSSH
needs to be running as a daemon.
Note that I’m making several assumptions here:
- You have access to the domain that is the destination and have root access or are in the
sudo
group (to open a port in the firewall). You have an account on the remote machine(not always necessary, depends on how theSSH
server is configured).SSH
is installed on both the local and remote machines and is configured to allow remote port forwarding.
Remote Port Forwarding
Forward a port from the server machine to the client machine.
$ ssh -R port:host:hostport [user@]hostname
Scenario
I’m starting a blog about JavaScript because I’m a Senior JavaScript Engineer. While still in the development phase, I’d like to share the home page with a friend who is a designer to get some feedback about fonts.
Unfortunately, I’m tethering to get my internet connection, and I can’t make my local web server (on port 80) publicly accessible. Luckily, I know someone who is tech-savvy, and they give me the following command to run from a terminal:
$ ssh -fCNR 31137:localhost:31337 example.com
After doing an Internet search, I’m able to find the Terminal app on my Mac and paste in the command. I can then tell my friend to point their browser at
www.example.com:31337
to view my site, which is actually running on my local machine.
Local Port Forwarding
Forward a port from the client machine to the server machine.
$ ssh -L port:host:hostport [user@]hostname
Scenario
I’m now working on another post that’s going to add tremendous value for my followers and help to promote my brand. The development server is remote, and I haven’t been shown how to install a TLS certificate yet. So, in order to protect the precious bits from prying eyes, I’m going to tunnel my traffic by locally forwarding a port. Again pasting in the command I was given, I run:
$ ssh -fCNL 31337:example.com:80 localhost
I now point my browser at
localhost:31337
and have an encrypted connection toexample.com
!
Dynamic Port Forwarding
Create a local SOCKS proxy that will forward ports on an application level. This can be any application-layer protocol (HTTP, FTP, etc.).
port [user@]hostname
Scenario
I’m at a coffee house, and I have my coolest Mac with all the stickers. I want to protect my traffic, because anyone in here could be a hacker. I first paste in the command that establishes the SOCKS server:
$ ssh -fCND 1080 example.com
In the meantime, I’ve already had my applications configured to dynamically forward all my ports through this SOCKS5 proxy. My tech-savvy contact showed me how to do this in the preferences in Firefox, and for browsers that would set the proxy settings globally, I’ll type into the Terminal the command I had him write down on a Post-it note:
$ chromium --proxy-server="socks5://localhost:1080" &> /dev/null &
This seems to be so much better than local port forwarding. With that, I’m limited to only communicating with a single machine; that which is specified on the command line. However, with dynamic port forwarding, all my traffic is forwarded, regardless of its destination, as long as the application knows to send it into the
SSH
tunnel (and the browsers do know to do this, since I configured them above).I just browse the web using Firefox or Chromium like I normally do, and I don’t have to fiddle with the address bar and add any port numbers!
Weeeeeeeeeeeeeeeeeeeeee