Home » How does reverse SSH tunneling work?

How does reverse SSH tunneling work?

Solutons:


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). But you know this part already.

Think of your tunnel as a 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.

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, and an address and port to which it will be delivered. The -L option tells the tunnel to answer on the local side of the tunnel (the host running your client). The -R option tells the tunnel to answer on the remote side (the SSH server).

ssh tunnel directions

So… To be able to SSH from the Internet into a machine behind a firewall, you need the machine in question to open an SSH connection to the outside world and include a -R tunnel whose “entry” point is the “remote” side of his 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 your client 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).

The other options are:

  • -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.

There will be a password challenge unless you have set up DSA or RSA keys for a passwordless login.

Note that it is STRONGLY recommended that you use a throw-away account (not your own login) that you set up for just this tunnel/customer/server.

Now, from your shell on 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:

host remotehostname
    User remoteusername
    Hostname localhost
    Port 22222

Adjust remotehostname and remoteusername to suit. The remoteusername field must match your username on the remote server, but remotehostname can be any hostname that suits you, it doesn’t have to match anything resolvable.

See also:

  • Expose the reverse endpoint on a non-localhost IP
  • Tips on using ControlMaster to maintain your tunnel

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).

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 set 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 along the connection to port XXXX on the local machine is proxied by ssh to Y.Y.Y.Y:ZZZZ.

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 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

Related Solutions

Joining bash arguments into single string with spaces

[*] I believe that this does what you want. It will put all the arguments in one string, separated by spaces, with single quotes around all: str="'$*'" $* produces all the scripts arguments separated by the first character of $IFS which, by default, is a space....

AddTransient, AddScoped and AddSingleton Services Differences

TL;DR Transient objects are always different; a new instance is provided to every controller and every service. Scoped objects are the same within a request, but different across different requests. Singleton objects are the same for every object and every...

How to download package not install it with apt-get command?

Use --download-only: sudo apt-get install --download-only pppoe This will download pppoe and any dependencies you need, and place them in /var/cache/apt/archives. That way a subsequent apt-get install pppoe will be able to complete without any extra downloads....

What defines the maximum size for a command single argument?

Answers Definitely not a bug. The parameter which defines the maximum size for one argument is MAX_ARG_STRLEN. There is no documentation for this parameter other than the comments in binfmts.h: /* * These are the maximum length and maximum number of strings...

Bulk rename, change prefix

I'd say the simplest it to just use the rename command which is common on many Linux distributions. There are two common versions of this command so check its man page to find which one you have: ## rename from Perl (common in Debian systems -- Ubuntu, Mint,...

Output from ls has newlines but displays on a single line. Why?

When you pipe the output, ls acts differently. This fact is hidden away in the info documentation: If standard output is a terminal, the output is in columns (sorted vertically) and control characters are output as question marks; otherwise, the output is...

mv: Move file only if destination does not exist

mv -vn file1 file2. This command will do what you want. You can skip -v if you want. -v makes it verbose - mv will tell you that it moved file if it moves it(useful, since there is possibility that file will not be moved) -n moves only if file2 does not exist....

Is it possible to store and query JSON in SQLite?

SQLite 3.9 introduced a new extension (JSON1) that allows you to easily work with JSON data . Also, it introduced support for indexes on expressions, which (in my understanding) should allow you to define indexes on your JSON data as well. PostgreSQL has some...

Combining tail && journalctl

You could use: journalctl -u service-name -f -f, --follow Show only the most recent journal entries, and continuously print new entries as they are appended to the journal. Here I've added "service-name" to distinguish this answer from others; you substitute...

how can shellshock be exploited over SSH?

One example where this can be exploited is on servers with an authorized_keys forced command. When adding an entry to ~/.ssh/authorized_keys, you can prefix the line with command="foo" to force foo to be run any time that ssh public key is used. With this...

Why doesn’t the tilde (~) expand inside double quotes?

The reason, because inside double quotes, tilde ~ has no special meaning, it's treated as literal. POSIX defines Double-Quotes as: Enclosing characters in double-quotes ( "" ) shall preserve the literal value of all characters within the double-quotes, with the...

What is GNU Info for?

GNU Info was designed to offer documentation that was comprehensive, hyperlinked, and possible to output to multiple formats. Man pages were available, and they were great at providing printed output. However, they were designed such that each man page had a...

Set systemd service to execute after fstab mount

a CIFS network location is mounted via /etc/fstab to /mnt/ on boot-up. No, it is not. Get this right, and the rest falls into place naturally. The mount is handled by a (generated) systemd mount unit that will be named something like mnt-wibble.mount. You can...

Merge two video clips into one, placing them next to each other

To be honest, using the accepted answer resulted in a lot of dropped frames for me. However, using the hstack filter_complex produced perfectly fluid output: ffmpeg -i left.mp4 -i right.mp4 -filter_complex hstack output.mp4 ffmpeg -i input1.mp4 -i input2.mp4...

How portable are /dev/stdin, /dev/stdout and /dev/stderr?

It's been available on Linux back into its prehistory. It is not POSIX, although many actual shells (including AT&T ksh and bash) will simulate it if it's not present in the OS; note that this simulation only works at the shell level (i.e. redirection or...

How can I increase the number of inodes in an ext4 filesystem?

It seems that you have a lot more files than normal expectation. I don't know whether there is a solution to change the inode table size dynamically. I'm afraid that you need to back-up your data, and create new filesystem, and restore your data. To create new...

Why doesn’t cp have a progress bar like wget?

The tradition in unix tools is to display messages only if something goes wrong. I think this is both for design and practical reasons. The design is intended to make it obvious when something goes wrong: you get an error message, and it's not drowned in...