486

As I understand this, firewalls (assuming default settings) deny all incoming traffic that has no prior corresponding outgoing traffic.

Based on Reversing an ssh connection and SSH Tunneling Made Easy, reverse SSH tunneling can be used to get around pesky firewall restrictions.

I would like to execute shell commands on a remote machine. The remote machine has its own firewall and is behind an additional firewall (router). It has an IP address like 192.168.1.126 (or something similar). I am not behind a firewall and I know the remote machine's IP address as seen from the Internet (not the 192.168.1.126 address). Additionally, I can ask someone to execute ssh (something) as root on the remote machine first.

Could anyone explain me, step by step, how reverse SSH tunneling works to get around the firewalls (local and remote machines' firewalls and the additional firewall between them)?

What is the role of the switches (-R, -f, -L, -N)?

1

5 Answers 5

555

I love explaining this kind of thing through visualization. :-)

Think of your SSH connections as tubes. Big tubes. Normally, you'll reach through these tubes to run a shell on a remote computer. The shell runs in a virtual terminal (tty) through that tube. But you know this part already.

Think of your tunnel as another tube within a tube. You still have the big SSH connection, but the -L or -R option lets you set up a smaller tube inside it.

Your ssh remote shell actually communicates with you using one of these smaller, embedded tubes attached to stdio.

Every tube has a beginning and an end. The big tube, your SSH connection, started with your SSH client and ends up at the SSH server you connected to. All the smaller tubes have the same endpoints, except that the role of "start" or "end" is determined by whether you used -L or -R (respectively) to create them.

(You haven't said, but I'm going to assume that the "remote" machine you've mentioned, the one behind the firewall, can access the Internet using Network Address Translation (NAT). This is kind of important, so please correct this assumption if it is false.)

When you create a tunnel, you specify an address and port on which it will answer (or "bind"), and an address and port to which it will be delivered. The -L option tells the tunnel to bind on the local side of the tunnel (the host running your client). The -R option tells the tunnel to bind on the remote side (the SSH server).

ssh tunnel directions

So... To be able to SSH from the Internet into a host behind a firewall, you need the target host to open an SSH connection to a host on the outside and include a -R tunnel whose "entry" point is the "remote" side of its connection.

Of the two models shown above, you want the one on the right.

From the firewalled host:

ssh -f -N -T -R22222:localhost:22 yourpublichost.example.com

This tells the client on your target host to establish a tunnel with a -Remote entry point. Anything that attaches to port 22222 on the far end of the tunnel will actually reach "localhost port 22", where "localhost" is from the perspective of the exit point of the tunnel (i.e. your ssh client in this case, on the target host).

The other options are:

  • -f tells ssh to background itself after it authenticates, so you don't have to sit around running something like sleep on the remote server for the tunnel to remain alive.
  • -N says that you want an SSH connection, but you don't actually want to run any remote commands. If all you're creating is a tunnel, then including this option saves resources.
  • -T disables pseudo-tty allocation, which is appropriate because you're not trying to create an interactive shell.

There will be a password challenge unless you have set up a key for a passwordless login.

(Note that if you intend to leave a connection open long term, unattended, possibly having it automatically refresh the connection when it goes down (by parsing ssh -O check <remotehost>), I recommend using a separate, unique SSH key for it that you set up for just this tunnel/customer/server, especially if you are using RemoteForward. Trust no one.)

Now that the -R service tunnel is active, you can connect to it from yourpublichost, establish a connection to the firewalled host through the tunnel:

ssh -p 22222 username@localhost

You'll get a host key challenge, as you've probably never hit this host before. Then you'll get a password challenge for the username account (unless you've set up keys for passwordless login).

If you're going to be accessing this host on a regular basis, you can also simplify access by adding a few lines to your ~/.ssh/config file on yourpublichost:

host firewalledhost
    User firewalleduser
    Hostname localhost
    Port 22222

Adjust firewalledhost and firewalleduser to suit. The firewalleduser field must match your username on the remote server, but firewalledhost can be any name that suits you, the name doesn't have to match anything resolvable, since your connection is governed by Hostname and Port.

Alternately, if you want to reach this from elsewhere on the Internet, you might add the following to your ~/.ssh/config:

host firewalledhost
    ProxyCommand ssh -fWlocalhost:22222 yourpublichost        

The -W option is used to open a connection to a remote host in order to continue the SSH conversation. It implies -N and -T.

See also:

16
  • 8
    SOCKS proxies are not the same as tunnels. If you have a question about how to use them, please ask it.
    – ghoti
    Jan 29, 2014 at 16:45
  • 32
    I'm having a very difficult time following this explanation due to the loose usage of terms: server, client, local machine, remote machine, host, yourpublichost, localhost, remotehostname. With all these loose and undefined terms, it could be assumed that someone would need as much as 8 computers to set this up. I state this because in all other aspects it seems a very good explanation. Please reduce and define terms.
    – Rucent88
    Sep 18, 2015 at 16:21
  • 5
    @Rucent88, I'm not sure what reduction would be possible. A client establishes a connection to a server. That's common terminology all across the world of networking. Local and remote machines seem pretty self evident. If you're confused about SSH or general unix terminology after reading the documentation, I'm sure you'll have no trouble finding people here very willing to answer any questions you may have.
    – ghoti
    Sep 18, 2015 at 16:44
  • 18
    @ghoti It's indeed confusing, because local and remote machines are relative. When I sit at the workplace my home computer is the remote, when I sit at home, the workplace computer is remote. Server and client is also confusing when speaking of tunneling. For example If I connect to home from work with ssh -R. I connect to my work from the home computer by connecting localhost. So from sockets perspective the server is home computer itself. But logically I connected to my work computer. That's confusing.
    – Calmarius
    Nov 12, 2016 at 15:12
  • 3
    @Calmarius, a client initiates a connection, a server receives that connection. A tunnel is a product of an SSH connection. Make a tunnel with -R, and you provide something through which some other client (on the remote side from the perspective of the SSH client) might establish a connection to a server that is "local" to the SSH client. You're right that things are relative; terminology is necessarily dependent on perspective and scope. It becomes easier the more you work with it.
    – ghoti
    Mar 8, 2017 at 19:16
531

I have drawn some sketches

The machine, where the ssh tunnel command is typed is called »your host«.

ssh tunnel starting from local


ssh tunnel starting from remote

Introduction

  1. local: -L Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side.

    ssh -L sourcePort:forwardToHost:onPort connectToHost means: connect with ssh to connectToHost, and forward all connection attempts to the local sourcePort to port onPort on the machine called forwardToHost, which can be reached from the connectToHost machine.

  2. remote: -R Specifies that the given port on the remote (server) host is to be forwarded to the given host and port on the local side.

    ssh -R sourcePort:forwardToHost:onPort connectToHost means: connect with ssh to connectToHost, and forward all connection attempts to the remote sourcePort to port onPort on the machine called forwardToHost, which can be reached from your local machine.

Additional options

  • -f tells ssh to background itself after it authenticates, so you don't have to sit around running something on the remote server for the tunnel to remain alive.
  • -N says that you want an SSH connection, but you don't actually want to run any remote commands. If all you're creating is a tunnel, then including this option saves resources.
  • -T disables pseudo-tty allocation, which is appropriate because you're not trying to create an interactive shell.

Your example

The third image represents this tunnel. But the blue computer called »your host« represents the computer where someone starts the ssh tunnel, in this case the firewalled machine.

So, ask someone to start a ssh tunnel connection to your machine. The command should basically look like

ssh -R 12345:localhost:22 YOURIP

Now the tunnel is opened. You can now connect via ssh to the firewalled machine through the tunnel with the command

ssh -p 12345 localhost

which will connect to your own localhost (your machine) on port 12345, but port 12345 is forwarded through the tunnel to port 22 of the localhost of the firewalled computer (i.e. the firewalled computer itself).

32
  • 4
    @erik, How did you draw these images?
    – Pacerier
    Jan 16, 2015 at 7:41
  • 4
    I used the open source vector drawing tool called Inkscape and my mice (well, actually it is a trackball: Marble FX from Logitech).
    – erik
    Jan 19, 2015 at 0:19
  • 1
    @ghoti: By default, the listening socket on the server (in my -R image the yellow remotehost) will be bound to the loopback interface only. This may be overridden by specifying a bind_address. An empty bind_address or the address ‘*’ indicates that the remote socket should listen on all interfaces. — So just try it: On a blue computer start ssh -R 1234:localhost:8000 yellowcomputer. And also on the blue machine start python -m SimpleHTTPServer (a simple webserver on port 8000). Then in a webbrowser on the yellow machine open the URL http://localhost:1234 and you will see it works.
    – erik
    May 6, 2015 at 7:45
  • 1
    @erik, ah, the confusing part is because my eyes are bad -- I wasn't seeing the space before remotehost in your commands. Upon closer inspection, I see that your diagrams don't actually address the optional bind_address at all. Sorry for the confusion.
    – ghoti
    May 29, 2015 at 18:53
  • 1
    @briankip Yes, of course. At least if you want to make a ssh connection to the computer behind the firewall (through the tunnel or directly, if the firewall permits that). I.e.: The ssh-command you write on the firewalled computer only opens a tunnel, but does not start or work as a ssh-daemon/-server. Your next question should be, how to start a sshd without root privileges (if that is your problem). :-)
    – erik
    Mar 18, 2016 at 15:17
29

ssh tunneling works by using the already established ssh connection for sending additional traffic.

When you connect to a remote server, you usually just have 1 channel for the normal user interaction (or 3 channels if you consider STDIN/STDOUT/STDERR separate). At any time, the local or remote ssh process can open additional channels on the existing connection. These channels then send/receive the tunnel traffic. When sending or receiving any traffic, the ssh process simply says "this traffic is for channel foobar".

It essentially works like this:

  1. You tell ssh to start listening on port XXXX and that any traffic received should be tunneled, and then sent to Y.Y.Y.Y on port ZZZZ.
  2. The local ssh starts listening on port XXXX (generally on 127.0.0.1, but can be changed).
  3. Some application opens a connection to port XXXX on the local machine.
  4. The local ssh opens a channel to the remote ssh and says "any traffic on this channel goes to Y.Y.Y.Y:ZZZZ
  5. The remote ssh connects to Y.Y.Y.Y:ZZZZ and sends back the "OK, channel is open"
  6. Now any traffic sent on the connection to port XXXX on the local machine is proxied by ssh to Y.Y.Y.Y:ZZZZ using the channel it created.

This process is the exact same for both forward and reverse tunneling (just swap the words 'local' and 'remote' in the above procedure). Either side can start the tunnel. It doesn't even have to be when you first start ssh. You can open tunnels while ssh is already running (see man ssh section ESCAPE CHARACTERS, specifically ~C).

For the role of -R, -f, -L, and -N, you really should just consult the man page, it gives you the best possible explanation. But I'll mention -R and -L.
-R tells the remote ssh to listen for connections, and that the local ssh should connect to the real destination. -L tells the local ssh to listen for connections, and that remote ssh should connect to the real destination.

Note, this is a very crude description, but it should give you enough info to know what's going on

2
  • I liked your explanation. Only doubt though, on point 6: Y.Y.Y.Y receives traffic on port ZZZZ. In other words, dest-IP is Y.Y.Y.Y and dest-port is ``ZZZZ. Which are the source IP address and source port of the receiving packet? May 25, 2021 at 22:20
  • The source IP will be whatever the appropriate route is to the destination IP. If you have a shell on the remote system, and you run ip route get Y.Y.Y.Y, you'll see a src value. That value is the source IP address. The source port is effectively random.
    – phemmer
    Mar 23, 2023 at 3:14
25

This is explained in SSH manual, especially the differences between -L (local) and -R (remote).


-L

-L [bind_address:]port:host:hostport

Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side.

This works by allocating a socket to listen to port on the local side, optionally bound to the specified bind_address.

Whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to host port hostport from the remote machine.

The following example tunnels an IRC session from client machine 127.0.0.1 (localhost) using port 1234 to remote server server.example.com:

$ ssh -f -L 1234:localhost:6667 server.example.com sleep 10

Note: The -f option backgrounds ssh and the remote command sleep 10 is specified to allow an amount of time to start the service which is to be tunnelled.

Example:

ssh `-N` -L 22000:localhost:11000 remote.server.com
  • -N After you connect just hang there (you won't get a shell prompt)

    Do not execute a remote command.

  • -L 22000 The connection will originate on port 22000 of your personal, Local machine

  • localhost:11000 - remote.server.com will make sure that the other end of the tunnel is localhost, port 11000

ssh -N -L 22000:192.168.1.2:11000 remote.server.com

Source: An illustrated guide, tutorial, how-to, on ssh tunneling.


-R

-R [bind_address:]port:host:hostport

Specifies that the given port on the remote (server) host is to be forwarded to the given host and port on the local side.

This works by allocating a socket to listen to port on the remote side, and whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to host port hostport from the local machine.

Example:

ssh -N -R 22000:localhost:11000 remote.server.com
  • -N After you connect just hang there (you won't get a shell prompt)

    Do not execute a remote command.

  • -R 22000 The connection will originate on port 22000 of the Remote computer (in this case, remote.server.com)

  • localhost:11000 your personal, local computer will make sure that the other end of the tunnel is localhost, port 11000

ssh -N -R 22000:localhost:11000 remote.server.com

Source: An illustrated guide, tutorial, how-to, on ssh tunneling.

3
  • 11
    the ear and mouth make it crystal clear who's talking and who's listening!
    – Thufir
    Feb 10, 2018 at 7:17
  • 2
    I love your illustrations!
    – mcantsin
    Jun 4, 2018 at 15:23
  • Thank you for this! Nov 16, 2021 at 3:36
1

KISS - Your computer = your_computer_ip / your_user_name_at_your_computer REMOTE SERVER = REMOTE_ip / remote_user_name

ON - VPN PROTECTED COMPUTER (REMOTE SERVER) in /etc/ssh/sshd_config file:

AllowTcpForwarding all
PermitTunnel yes

then restart ssh:

sudo service ssh restart

(we are configuring port 2222 as the incoming ssh transmission) (you need it persistent you can use tmux or -f flag -> ssh -f -R) ** Still on VPN PROTECTED **

ssh -R 2222:localhost:22 your_user_name_at_your_computer@you_computer_ip
  • ** 2222 is not random port!!**
  • ** only root users can open ports below 1024 !!!**

you can now disconnect VPN or anything else that was used

open a terminal in your computer

your 2222 port on your computer is now mapped to the remote host!

ssh localhost -p 2222 -l remote_user_name

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .