SSH port forwarding can be a very useful tool for working with remote systems. There are two common reasons for doing this:
to add security to an unencrypted connection, or
to access services behind firewalls.
Background
In networking lingo, a host generally refers to a single computer. Each host can have multiple network interfaces, each of which is assigned an (IP) address.
The address suffices to identify the network interface and hence the host, but a single host can provide a variety of services (e.g. web server, mail server, SSH server, etc). Hence, there needs to be a way to distinguish between them. This is where the notion of a port comes in. Each service is allowed to pick its own port, denoted by a 16-bit unsigned integer (0‒65535).
Services can listen to a particular port, waiting for clients to initiate connections to the port. The port numbers for many services are specified by conventions. Clients can then connect to the target port that corresponds to the one chosen by the service.
Example. An HTTP web server such as httpd
would typically listen to port 80 (one can also say the service httpd
“runs on” port 80).
Hence, HTTP clients such as web browsers or downloaders would typically have a target port of 80.
Note: In the process of establishing a connection to a server, the clients themselves also obtain a port on their end. This port, however, is ephemeral and generally irrelevant here. They are not shown in any of these diagrams.
Normally, given the correct port, the client can simply connect directly to the server…
… unless of course, there is a firewall that blocks port 80. However, if the firewall permits SSH connections, port forwarding can be used to bypass the barrier. In this case, the client would connect to, say, port 8000, which is then forwarded to port 80 on the server.
Forwarded ports are generally temporary, so conventionally one would use a large port number to avoid conflicts with the more frequently used ports. (Some systems, usually the Unix-like ones, would also reserve port numbers below 1024 so that one would require superuser privileges to forward them.)
Port forwarding
SSH supports two kinds of port forwarding: local and remote port forwarding. (There is also the so-called dynamic port forwarding, which won’t be discussed here.) The first and most important question here is: what is the difference between local and remote forwarding?
In local port forwarding, the port that is being forwarded resides on the local end, i.e. the host of the SSH client, whereas …
… in remote port forwarding, the port that is being forwarded resides on the remote end, i.e. the host of the SSH server.
In other words, the type of port forwarding depends on the location of the service of interest (in the example earlier, the httpd
server) with respect to the SSH server.
From the manual pages of SSH, the argument syntax for port forwarding is:
# local port forwarding
ssh -L [BIND_ADDRESS:]PORT:HOST:HOSTPORT HOSTNAME
# remote port forwarding
ssh -R [BIND_ADDRESS:]PORT:HOST:HOSTPORT HOSTNAME
The parameters are:
PORT
: the port that is being forwarded. SSH will listen to this port and forward connections made to this port to the other side (i.e.HOST
).In the case of local port forwarding,
PORT
is bound to the host of the SSH client.In the case of remote port forwarding,
PORT
is bound to the host of the SSH server.
HOST
: the host that provides the service of interest.In the case of local port forwarding,
HOST
is on the side of the SSH server.In the case of remote port forwarding,
HOST
is on the side of the SSH client.
HOSTPORT
: the port onHOST
listened by the service of interest.HOSTNAME
: the SSH server (unrelated toHOST
orHOSTPORT
).BIND_ADDRESS
: this is an optional argument that specifies the address thatPORT
should be associated with. By default, this only binds to the loopback interface.
The naming of the arguments here is rather unfortunate as the word “host” is overloaded to mean several things. For this reason, the name sshd-host
will be used to refer to the host of the SSH server (i.e. HOSTNAME
), while the name appd-host
will be used to refer to the host that provides the service of interest. Correspondingly, ssh-host
will refer to the host of the SSH client.
Example. Consider the previous example involving the HTTP server and client. If the HTTP server resides on host of the SSH server and the HTTP client resides on host of the SSH client, then one can use local port forwarding to access the remote HTTP server from the local side.
On the other hand, if the HTTP server resides on the host of the SSH client and the HTTP client resides on the host of the SSH server, then one can use remote port forwarding to access the local HTTP server from the remote side.
Notice that for both cases, the arguments differ only by the flag (-L
/-R
). However, the similarity is somewhat deceptive: the address localhost
is interpreted differently in each case. In the case of remote port forwarding, localhost
refers to the host of the SSH client (ssh-host
) as one would normally expect, whereas in the case of local port forwarding, localhost
actually refers to the host of the SSH server (sshd-host
)! In fact, the HOST
parameter in the case of local port forwarding is always relative to the SSH server.
The above two scenarios are actually special cases of a more general scenario: appd-host
could be a separate host, different from either ssh-host
or sshd-host
. In the more general case, local port forwarding would involve forwarding a port from ssh-host
to sshd-host
and then to appd-host
, while remote port forwarding would involve forwarding a port from sshd-host
to ssh-host
and then to appd-host
.
Example. If the HTTP server is accessible on the remote side and the HTTP client resides on host of the SSH client, then one can use local port forwarding to access the HTTP server from the local side.
On the other hand, if the HTTP server is accessible on the local side and the HTTP client resides on host of the SSH server, then one can use remote port forwarding to access the HTTP server from the remote side.
Note that in both cases, the second part of the traffic is not encrypted by SSH since the SSH tunnel only connects between ssh-host
and sshd-host
.
While the target host (appd-host
) can be almost anywhere, the forwarded port must always reside on either ssh-host
or sshd-host
: this is necessary since SSH has to actually listen to the forwarded port, whereas the target port simply needs to be reachable (not blocked by a firewall).
Lastly, what about BIND_ADDRESS
? By default, SSH will only listen to a forwarded port on the loopback interface, which is a virtual network interface that only responds to internal requests (from the same host). This is for security reasons, since if SSH listened on all interfaces, it could be potentially forwarding traffic from just about anybody on the Internet. Using BIND_ADDRESS
one can override the default behavior so that SSH will listen on other interfaces as well. Use this cautiously! (Note: it may necessary to enable the GatewayPorts
flag for this to work on anything but the loopback interface.)
Chapter 9.2 in the book SSH, The Secure Shell: The Definitive Guide goes into greater detail about port forwarding with SSH.
Show Disqus comments
comments powered by Disqus