SSH - tunnels, X forwarding

From Helpful
Revision as of 01:13, 13 June 2021 by Helpful (Talk | contribs)

Jump to: navigation, search
Security related stuff.




These are primarily notes
It won't be complete in any sense.
It exists to contain fragments of useful information.

Introduction (by example)

SSH tunnels mean that, given an SSH connection,

  • you make one side listen to a specified port (you choose which side, and which port)
-L is 'listen from the local/client side, make connection on server side' (relative to to how you're connecting SSH-wise)
-R is 'listen at remote/server side, make connection on our client's side'
  • If a connection is made to that port, SSH will make a new (non-secured!) connection from the other end, to the a host:port you also specified.

That's still pretty abstract, so some introduction by example:

  • Say I have a database GUI on my laptop, and want to connect to a firewalled database server, which exposes only SSH, so I might do (e.g. for postgres, so port 5432):
ssh -L 5432:localhost:5432
so now, when something connects to my laptop's port 5432, it actually goes to what to the SSH server side is localhost:5432
One of the nice things about this is as far as the dbserver is concerned it's seeing a connection from (its) localhost. In other words, the database server it need not trust my laptop by IP, or be open to the LAN at all.

  • I sometimes want to remote-desktop to an internal Windows server at work. And have an exposed linux server to help.
Same idea as the last, but on the work side, the connection is not to itself but to a given IP address
ssh -L 3389:

A different example:

  • I have a webapp on a work server that I want to check from home, but it's intentionally firewalled,
It would requires multiple steps of SSH to get to that host
It would be possible to tie multiple tunnels together, but that's a bunch of work, and sort of fragile. Instead...
I have a ssh server at home, so I have a simpler option. Once I'm on that work host:
ssh -R 5000:localhost:5000
This is functionally much the same as a -L into work
in that at home I can connect to localhost:5000 and get something from the other side
except the SSH connection is easier to set up in this direction
More flexibly, I've used this like -R 2222:localhost:22 (and in tmux to have that SSH and tunnel connection live longer) to very-temporarily open up SSH to a work server (from a port on my home host), just to finish up some admin stuff in the evening

some technical notes

The typical syntax is


This is a shorthand implying it binds to ('all interfaces'), so for:

Keep in mind that it can be more secure if that's localhost instead, in that on the listening side, you now need to be on the host itself (without this, you might be open to your LAN, and even things like VPN).

More recently, one or both sides can also be a named socket, by its path, which is similarly host-only.

Limitations to use

  • while the data over the tunnel is encrypted, the extra connection is not
this doesn't matter too much if it's a connection to localhost, but can matter if not.
  • encryption adds a bit of latency, and isn't very high-bandwidth
  • Forwards a single fixed port at at a time.
You can do multiple, but it's certainly not as flexible or user-friendly as VPN, and other generic tunneling (...once you've set those up, that is)
  • only one of these unsecured connections per port(verify)
  • You need to keep the SSH connection open, so need to keep the shell open to keep the tunnel open - or use a way around that
  • tunneled connections drop when SSH connection drops, for any reason
In some situations this can be prohibitively annoying.
  • unless you're root, you probably can't listen to ports under 1024
...which is intentional, and usually a good thing

As such, it's most useful for quick, one-time, temporary use.


This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, fix, or tell me)

Server configuration

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, fix, or tell me)

Practicalities to the open SSH connection

Mostly "Keeping the connection going" / "Ensuring no commands at the remote end"


Firewalls may drop idle connections. A quick and dirty fix is to not be idle, e.g. on the remote shell run something like Template:Inline.

A better solution to that problem is to configure ssh/sshd's keepalive.

For the client-side tweak, look for ServerAliveInterval.
for the server-side configuration, look for ClientAliveInterval

The tunnel will work as long as the SSH connection is open, which means keeping the terminal open (unless you do some extra-finicky things). Depending on how you connected, that terminal might be found by other people.

You can hide the ssh command in the backgrounds somewhere (see e.g. screen) but for more structural or less trusted cases you may want to be surer that this remote login could not do harm.

One thing you can do is make it do something useless (e.g.
watch date
or htop or whatnot) instead of (not on top of) start an interactive shell.

On a tunnel you set up temporarily and need to enter a passphrase for anyway, you can give that command on the client side.

A more secure way is to configure an entire account on the server side to always run such a command, so a shell never gets run. It turns out you can even tie this to specific authorized keys - see SSH_jails#via_authorized_keys.

You could also look at SSH jails in general.

Automatic re-establishing

Your home internet breaking for a minute.

You can't work around that, so you may want it to reconnect if it breaks.

On *nix:

Use autossh, (and/)or abuse your service manager's features.

You'll also need a passphraseless keypair (or you'll need human interaction at reconnection time). (again, look at ssh jail)


If you want a tunnel at bootup, keep in mind that upstart can watch and respawn things, and you can avoid autossh (upstart's behaviour around executables stopping is well defined and works much the same).
Otherwise you probably want to look at autossh (or perhaps its inspiration, rstunnel).

On windows, look at things like tunnelier.

using upstart

upstart example
description "Temporary tunnel for remote access"
start on (local-filesystems and net-device-up IFACE=eth0)
stop on runlevel [016]
setuid myuser
setgid myusergroup
respawn limit 0 60
exec ssh -t -R 2221:localhost:22 -t -o "StrictHostKeyChecking=no" -o "BatchMode=yes" -o "ExitOnForwardFailure yes"

using systemd

Description=Temporary tunnel for remote access

ExecStart=/usr/bin/ssh -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -o "StrictHostKeyChecking=no" -o "BatchMode=yes" -o "ExitOnForwardFailure=yes" -R localhost:2221:localhost:22


autossh apparently wants a shell so makes sense in forking mode, but the following should work(verify):

ExecStart=/usr/bin/autossh -M 0 -f -t -R localhost:2221:localhost:22 -q -o "StrictHostKeyChecking=no" -o  "BatchMode=yes" -o "ExitOnForwardFailure=yes" 

using autossh

The simplest autossh command is

autossh -M 0


  • -M (monitoring) is a required argument. When you want no monitoring, use -M 0
  • -f tells autossh to sit in the background (forks to init?)
  • options not understood are passed through to ssh, so you can add any of your usual options, say,
-L 3306:localhost:3306 
-o "ServerAliveInterval 60″ -o "ServerAliveCountMax 3″ 

See also:

autossh imitation

A quick-and-dirty imitation of autossh could be based off a script like:

while true
  ssh user@hostname # with whatever further options you need
  sleep 5   # possibly longer if the server has denyhosts/fail2ban

Options you may want on a tunnel

Avoid the idle-disconnect problem:

-o "ServerAliveInterval 60″
-o "ServerAliveCountMax 3″ 

If the tunnel can't be made, consider that a failure to connect (useful when they tunnels the point -- without this, you'll have a connection and autossh/upstart thinks it's doing its job, but the result is useless)

-o "ExitOnForwardFailure yes"        # to fail if tunnels fail to establish 

Fail if the login isn't passwordless (don't hang on password question):

-o BatchMode=yes 

Use a specific keyfile, rather than relying on things implied by account:


More details

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, fix, or tell me)

Tunnels on restricted connections

See SSH jails

X11 forwarding

SSH clients such as the linux one can do X forwarding (X as in the X windowing system).

ssh -X

This is not only a port forwarding shortcut, but it also does things for you like setting DISPLAY and handling X authentication

For this to work, the ssh server (and client) must have X support, and the server must allow it. Find the server configuration (which is probably at /etc/ssh2/sshd_config or /etc/ssh2/sshd2_config) and add/set:

AllowX11Forwarding        yes

In some cases you may like to restrict it, e.g.

Match User username
  X11Forwarding yes
  AllowTcpForwarding yes

Don't forget to restart sshd.

On security and broken programs

Apparently, X clients don't always deal with authentication properly, which means they may be broken when forwarding actually does authentication properly.(verify)

This seems to be the reason that in addition to
ssh -X
, which adheres to X11 SECURITY extensions, there is also
ssh -Y
(and its configuration counterpart,
, which is basically the overly trusting version.

More programs work with -Y, but -Y also means clients are allowed to do more potentially evil things. This is a client option, because all the risk is for the client.

So if you don't trust the remote host, don't use -Y. If you don't trust it at all, don't even use X forwarding at all, considering that your X server has relatively wide effective permissions.

Also, it seems a few things have problems with -Y. I had one case of "could not connect to session bus” (dbus, GNOME stuff) that I seemed to fix by switching from -Y to -X.

See also X_notes#X_Authentication


X11 forwarding request failed on channel 0

possible causes include

  • xauth not installed, e.g. because it's a server with a minimal install, no bits of X yet
  • sshd_config says X11Forwarding off (also check user/group specific rules)
  • a specific client doesn't understand the localhost trick, in which case you need(verify)
X11UseLocalhost no
  • IPv6 weirdness,, in which case you can force IPV4 only with
AddressFamily inet

Some of these are easily diagnosed using -v on the client, you may see a message like

debug1: Remote: No xauth program; cannot forward with spoofing

For me even -vvv said nothing useful. My issue was the IPv6ness.

channel 2: open failed: administratively prohibited: open failed

Typically means AllowTCPForwarding is not enabled in the server's /etc/sshd_config

If that system isn't yours, then the sysadmin may never have enabled it, or may have specifically disabled it.

Sysadmins: you can conditionally enable this, e.g. for specific users.

Authentication refused: bad ownership or modes for directory /home/someone your logs, and keypairs not working.

The following should fix it.

chmod go-w ~/
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

These exact instructions may be a little overzealous (verify)

Pseudo-terminal will not be allocated because stdin is not a terminal

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, fix, or tell me)

Typically means you're using ssh but not from within a shell, so a (pseudo-)terminal does not apply.

Examples where this pops up is using ssh (and scp and such?(verify)) from cron, from a script, and such.

You can disable and force pty allocation. Which of the two you want depends on your wishes. It seems the options for ssh are:

  • -T
    no pty allocation
  • -t
    force pty
  • -t -t
    force pty even if there is no local tty

I ran into this when I was trying to automate going to a firewalled host in two steps by trying another ssh command as the command to the first like:

ssh firsthost ssh secondhost

This basically means the inner ssh doesn't need (or get) a pty at all. In short, I needed:

ssh -t firsthost ssh secondhost

PuTTY X11 proxy: wrong authorisation protocol attempted

I got this when the system disk was full, which meant the negotiated secret couldn't be stored in .Xauthority.

remote port forwarding failed for listen port

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, fix, or tell me) the actual port number.

Seems to mean there is a stale connection which is still actively using the forwarding, which prevents a new process from listening.

In my case, stopping the autossh servive for a few minutes solved the issue. A more understanding fix may come later :)


X11UseLocalhost no

means it will accept connections from everywhere, not just from localhost. Useful when you're doing more complex tunneling than "from the host I ssh'd to to the client side".