SSH (Secure Shell) is a widely used network protocol to securely log onto remote systems. One of its features is /SSH Port Forwarding/ (or SSH tunneling). This functionality forwards encrypted connections between a local and a remote computer. There are 3 types of port forwarding: local port forwarding, remote port forwarding and dynamic port forwarding.
Local Port Forwarding
Local port forwarding is created with -L
parameter.
ssh -L source_port:forward_to_host:destination_port via_host
This command connects to via_host
from the local machine. via_host
runs a SSH server. It then forwards all connection attempts to source_port
on the local machine (a machine that initiated the ssh
command) to destination_port
port on the remote forward_to_host
machine. forward_to_host
machine must be reachable from the via_host
machine. Forwarding can be also done through Unix sockets.
Here's an example.
ssh -L 8080:zaiste.net:80 acme
In this example we connect with acme
machine. We then forward any connection to port 8080
on the local machine to port 80
on zaiste.net. As a result, opening localhost:8080
in the browser actually makes a request to a HTTP server (port 80) listening on zaiste.net
. You won't be, however, able to see its index.html
page. Read on to find out why.
By default, anyone (even on different machines) can connect to the specified port on the local machine. This can be restricted to programs on the same host by supplying a bind address:
ssh -L 127.0.0.1:8080:zaiste.net:80 acme
The request you are sending when hitting refresh in the browser for localhost:8080
are being built with a Host
destination header of localhost
value. This request reaches the zaiste.net
machine, but it's ignored as invalid virtual host designation (localhost
cannot be a domain name on the server that runs zaiste.net
).
Let's adjust the Host
HTTP header so the remote web server properly identifies the corresponding destination.
curl -H "Host: zaiste.net" -L localhost:8080
-L
parameter for curl
is for following redirects. You should see now the content of index.html
from zaiste.net
as long as you are connected to acme
machine.
forward_to_host
host may also refer to the remote machine through which the ssh
connection is made (i.e. via_host
). In that case we can just say 127.0.0.1
or localhost
as it's local in the context of already established ssh connection with via_host
. In other words, 127.0.0.1
or localhost
refers to via_host
machine.
ssh -L 4000:127.0.0.1:3306 acme
SSH binds to port 4000 on the local machine. Any traffic that comes to this port is sent to the SSH server that listens on acme
(remote machine). Once received by acme
the traffic is then sent to port 3306
of 127.0.0.1
, which is acme
itself.
You can forward multiple ports in a single ssh command:
ssh -L 5544:127.0.0.1:4455 -L 3366:127.0.0.1:6633 via_host
Ports numbers less than 1024
or greater than 49151
are reserved for the system. Privileged ports (ports lower then 1024
) can only be forwarded by root. If you're using local (or remote) port forwarding, you need to specify the destination server, i.e. via_host
.
Let's say you need to connect to a PostgreSQL database on a remote acme
machine. The PostgreSQL server only allows local connection for security reasons. PostgreSQL is running on the port 5432 on the remote acme
machine.
$ ssh -L 9000:localhost:5432 acme
This command forwards the local port 9000
to the port 5432
on the acme
machine. You can connect to that remote PostgreSQL server through the local machine using psql
on localhost:9000
.
Port forwarding is enabled by default. If not, check AllowTcpForwarding
in /etc/ssh/sshd_config
.
Remote Port Forwarding
Remote port forwarding is created with -R
parameter.
ssh -R source_port:forward_to_host:destination_port via_host
This command connects to via_host
. via_host
runs a SSH server. It then forwards all connection attempts to source_port
on the remote via_host
machine to destination_port
port on the local machine (a machine that initiated the ssh
command) . forward_to_host
machine must be reachable from the the local machine machine. Forwarding can be also done through Unix sockets.
Here's an example
ssh -R 10123:127.0.0.1:123 acme
ssh
connects to acme
. -R
makes ssh
listen on the port 10123
of acme
. Once there's a process on acme
connecting to 10123, ssh
server listening on the same acme
machine will transfer that connection to the local machine (a machine that initiated the ssh
command) and then it'll be forwarded to 127.0.0.1
on the port 123
. Contrary to local port forwarding, 127.0.0.1
refers to the local machine i.e. a machine that initiated the ssh
connection. In other words, remote port forwarding allows to map a port of the local machine onto the remote server via SSH.
The command for forwarding port 80 from your local machine (localhost) to the remote host on port 8000 is:
ssh -R 8000:localhost:80 via_host
This form of remote port forwarding requires an adjustment to the SSH server configuration. You need to set GatewayPorts
to yes
inside /etc/ssh/sshd_config
. It allows the SSH server to bind port 8000 on the wildcard address. This way the port becomes available to the public address of the via_host
remote machine.
You can also set GatewayPorts
to clientspecified
. This way the remote port won't be bind on the wildcard address. SSH server binds to the loopback address by default. With this configuration setup, you need to explicitly specify an empty bind address for binding the wildcard address (public access to via_host
on the port 8000
). It is done by prefixing the remote port with the :
sign.
ssh -R :8000:localhost:80 via_host
You can also specify an IP address from which connections to the port are allowed.
ssh -R 7.7.7.7:8080:localhost:80 host147.aws.example.com
In this example, only connections from the IP address 7.7.7.7
to port 8080 are allowed.
If the remote server has GatewayPorts
set to no
with no possibility of changing it, you can achieve the same result by executing with a double forwarding. Execute the remote forwarding above followed by a local forwarding using -g
option, but from the remote via_host
server.
ssh -g -L 8001:localhost:8000 oli@remote-machine
-g
allows remote hosts to connect to local forwarded ports. This will make loopback port 8000
on the server accessible on all interfaces on port 8001
.
How remote port forwarding can be useful? Let's consider the following example. At you work you have a FTP server on port 21
and accessible only from the internal network. From that internal network at work you can only access outside world on port 80
(web browsing). At home you have a SSH server. You configure it to listen on port 80
. Your home IP is 7.7.7.7
. You execute the following remote port forwarding from within your network at work.
ssh [email protected] -p 80 -R 5566:127.0.0.1:21
Once at home, you will be able to access the FTP server at work by pointing your FTP client to localhost
on port 5566
.
Let's consider another scenario. You have a Rails application running locally on your computer on port 3000
. Your Internet provider don't assign public IPs. People from the external world (Internet) cannot access your home network. It's not possible to connect to your machine directly via the internet. You may, however, set up a remote port forwarding to allow this.
ssh -R 7000:127.0.0.1:3000 via_host
People will be able to access your Rails application on port 3000
by pointing their browser to via_host
IP address on the port 7000
.
Dynamic Port Forwarding (SOCKS)
Dynamic Port Forwarding allows a communication not on a single port, but across a range of ports. This port forwarding is created using -D
parameter. This option will make SSH acts as a SOCKS proxy server.
SOCKS5 is an internet protocol which routes packets between a server and a client using a proxy server. SOCKS5 proxy servers use both TCP and UDP protocols (SOCKS4 only uses TCP). A SOCKS proxy is simply a SSH tunnel in which specific applications forward their traffic down the tunnel to the remote server, and then on the server end, the proxy forwards the traffic out to the general Internet. Unlike a VPN, a SOCKS proxy has to be configured for each application separately on the client machine. There is, however, no need to install 3rd party applications to use it.
Proxies usually rewrite data packet headers. This may leads to decrease performance and mislabeling errors. SOCKS5 proxy servers do not rewrite data packet headers. They are more performant and less prone to data routing errors. Unlike HTTP proxies which can only interpret and work with webpages, SOCKS5 proxies can work with any kind of traffic. This is because SOCKS proxy servers are low-level proxies that can handle any program, protocol and any type of traffic.
Dynamic Port Forwarding can handle connections from multiple ports. It analyzes the traffic to determine the proper destination for the given connection. For example, a browser configured to use it as a SOCKS proxy can then access HTTP, HTTPS, FTP, etc. over the same connection. If you're using dynamic port forwarding, you need to configure programs to use a SOCKS proxy server.
Here's an example.
ssh -D 8123 -f -C -q -N via_host
-D
tells SSH to create a SOCKS tunnel on the the port 8123
. -f
forks the process to the background. -C
compresses the data before sending it. -q
enables quiet mode. -N
tells SSH that no command will be sent once the tunnel is up.
Tips & Tricks
The -nNT
flags will cause SSH to not allocate a tty and only do the port forwarding.
$ ssh -nNT -L 9000:imgur.com:80 [email protected]