Rate this page

Flattr this

Forward a TCP port from an SSH client to an SSH server

Tested on

Debian (Etch, Lenny, Squeeze)
Ubuntu (Hardy, Intrepid, Jaunty, Karmic, Lucid, Maverick, Natty, Precise)

Objective

To forward connections on a particular TCP port from an SSH client to an SSH server.

Scenario

Suppose that an organisation has two networks: a private, internal network (192.168.0.0/24) and a DMZ (203.0.113.0/24). Machines on the internal network are accessible from the DMZ, but not from the public Internet. Machines in the DMZ are accessible from anywhere.

There is an webserver www.private.example.com (192.168.0.1) located on the internal network. You wish to access this machine from the public Internet (198.51.100.1). You cannot connect to it directly, but you can connect using SSH to the machine ssh.example.com (203.0.113.1) which is located in the DMZ.

diagram showing network connections

Method

Overview

When you connect to a machine using SSH you can ask for one or more TCP ports to be forwarded through the secure channel. This does not interfere with the normal operation of SSH, and if required you can multiplex several ports through the same channel. The endpoints of a forwarded connection need not be on the machines running SSH.

In addition to forwarding the port, it may be beneficial to add an entry to /etc/hosts so that the destination can be referred to by name. Some protocols (such as HTTP with name-based virtual hosting) may be unusable without this.

Some protocols will not work at all when forwarded, with or without an /etc/hosts entry. The issues are similar to those which arise when performing NAT, so if a protocol cannot traverse a NAT gateway (or can do so only with explicit support from the gateway) then it is unlikely to work with port forwarding.

Establish the SSH connection

To forward a port from the client to the server use the -L option when establishing the connection:

ssh -L 127.0.0.1:8000:192.168.0.1:80 ssh.example.com

The four colon-separated arguments following the -L option have the following meaning:

Binding to the loopback address prevents other machines from connecting to the port, which is almost certainly the right behaviour for the scenario described here. If you want to allow connections from other machines then leave this field empty:

ssh -L :8000:192.168.0.1:80 ssh.example.com

If you want the SSH client to bind to a privileged port then the simplest (but least secure) method is to run it as root. Alternatives include using authbind, setting the cap_net_bind_service capability, or redirecting the port before it reaches SSH using iptables.

Optionally, add the final destination to /etc/hosts

Because www.private.example.com is not accessible from the public Internet there is a good chance that it will not be listed in the public DNS. Even if it is, the address will be wrong for connecting through SSH (it will be listed as 192.168.0.1).

The ability to refer to the server by name could be more than just a matter of convenience. For example, HTTP version 1.1 allows a webserver to host many websites on a single IP address using a technique called name-based virtual hosting. This does not work if you use an IP address in the URL: you must use a hostname in order to identify the website to be served.

The simplest solution is to temporarily add the hostname to /etc/hosts:

127.0.0.1 www.private.example.com

Note that the name has been mapped to the loopback address, not the public address of the client (198.51.100.1). This allows the SSH client to be bound to the loopback address in order to prevent connections from other machines (see above).

Remember to remove this entry from /etc/hosts once you have finished using it, especially if the client machine is ever likely to be connected to the internal network.

(One way to avoid the issue described above is to connect to a proxy server on the internal network instead of the webserver directly. This has the additional benefit of allowing access to any webserver on the internal network through a single port, but the disadvantage of forwarding all of your web traffic indiscriminately regardless of whether it is destined for the internal network or the public Internet.)

Testing

If you have added the webserver hostname to /etc/hosts then you should be able to fetch the home page of the website from the URL http://www.private.example.com:8000/ using a web browser running on 198.51.100.1.

Otherwise, you will need to fetch it from http://127.0.0.1:8000/. As noted above, this will probably not give the intended result if the webserver uses name-based virtual hosting.

Troubleshooting

See Troubleshooting SSH port forwarding.

Variations

Enable automatic port forwarding for all SSH connections

If you want every SSH connection to forward a particular port automatically then this can be arranged using the LocalForward configuration option. For example, the equivalent to -L 127.0.0.1:8000:192.168.0.1:80 on the command line would be:

LocalForward 127.0.0.1:8000 192.168.0.1:80

This line can be placed either in the configuration file for a particular user (typically ~/.ssh/config) or the global configuration file (typically /etc/ssh/ssh_config). Note the space between the first port and the second IP address: on the command line this was a colon.

See also

Tags: ssh