Port forwarding is a type of interaction between two applications, usually TCP/IP applications, that talk to each other using an SSH connection. SSH intercepts a service request from a client application on a host, creates an SSH session carrying the request to the other side of the SSH connection. The other side decrypts the request before sending it to the application server on the remote host. Port forwarding can be used to secure communications between applications that aren’t secured traditionally. They can also be used for communications that aren’t possible, for instance IT administrators block certain ports on hosts from external access with firewalls to improve security, with port forwarding it becomes possible to access those applications running on the remote machine. In a previous post we talked about a different type of forwarding called ssh-agent forwarding. This lets us create SSH connections from one computer, through a remote host, to a third remote host using public-key authentication without the need to have your private keys on the second remote host. Port forwarding is sometimes refereed to as “tunneling” because it provides a means for which you can secure TCP/IP connections through SSH.
Suppose you have an IMAP server running on a remote host and you want to access the server using an email client on your home machine. Suppose also that the administrators of the remote host are super paranoid and they block all external access except only on port 80, 433 and 22. Unfortunately since IMAP runs on port 143 you can’t access it from your home. All you need to do is to tunnel through to the IMAP server using SSH. The command you need to run on your local machine is:
$ ssh -L2001:localhost:143 remote.net
Lets break down the above command. The -L switch specifies local forwarding; this essentially says the TCP client ( your email client) , is on your local machine. The 2001 represents the port on your local machine you want your email client to connect to. The localhost means the source sockets of the connection on the server appear to come from localhost. This means that the above command could be written as:
$ ssh -L2001:remote.net:143 remote.net
And the source packets would appear as coming from remote.net address. This maybe trivial in some TCP applications but, some servers are configured to do access control and block connections from the loopback address. Or it might be running on a multi-homed host, and have bound only a subset of the addresses the host has, possibly not including the loopback address. It’s generally better to use the first command. Finally 143 represents the port number the IMAP server is running on the remote server.
By default your SSH client only listens to connections from your local machine. That is it only accepts connections from applications running locally on your machine. Any attempt from external applications to attempt to connect over the wire to your SSH client will fail by default. To enable this you need to tweak the command above like so:
$ ssh -g -L2001:localhost:143 remote.net
The -g switch represents the GateWayPorts option in the client configuration file; if set it to yes then there’s no need for the -g option.
An example of remote forwarding is, suppose we have an email client running on a remote server shell.isp.net and we want to access an IMAP server on another remote server remote.host.net with an SSH server installed then remote forwarding is the way to go in this case. The command to establish remote forwarding is:
$ ssh -R2001:localhost:143 remote.host.net
The syntax is similar to local port forwarding with the only difference being the -R switch instead of the -L in local port forwarding.
There are subtle differences between remote and local forwarding. The main difference being that in local forwarding the SSH client listens for communication from the application client and therefore usually resides on the same box as the application client. In remote forwarding the SSH server listens for communication from the application client, the SSH server and application client reside on the same host.
You may ask is it possible to tunnel HTTP connections on port 80 to access an insecure website using local forwarding? The answer is yes but there are many restrictions that come with it. Such as, since the browser is running only on localhost then following an absolute URL such as “http://inseure/web/now.html” would not work since the browser only knows localhost also trying to proxy the request with your browser won’t work with any connections other than port 80. What would work for us in this case is dynamic port forwarding. SOCKS is a dynamic forwarding protocol, it is used by popular browsers such as tor to proxy requests and tunnel through to websites censored in certain countries and protect the users privacy. A SOCKS client connects via TCP, and indicates via the protocol the remote socket it wants to reach; the SOCKS server makes the connection, then gets out of the way, transparently passing data back and forth. The command to enable this with SSH is run like so:
$ ssh -D 1080 remote.host.net
When a user types an absolute URL into the browser including any port such as “http://myweb:1890”. The browser connects to SSH socks proxy on port 1080 and asks for connection to myweb:1890. The SSH client associates the browser’s connection with a new SSH session and then connects to the SSH server. The SSH client and server essentially get out of the way and the browser directly connects to the web server. Each new connection to a different web site gets assigned a new socket by SSH.
Port forwarding is a general TCP proxying feature that tunnels TCP connections through an SSH session. It is useful for securing protocols that may not be secure and tunneling through to TCP connections that may be blocked by firewalls. However there are some TCP protocols that may not work properly with port forwarding such as FTP. It so happens that FTP opens random ports on a client after authentication. This makes port forwarding unnecessarily complex. By and large majority of the TCP protocols do work with port forwarding.