Difference between revisions of "SSH - tunnels, X forwarding"

From Helpful
Jump to: navigation, search
m (Introduction by example)
m (Syntax and some security notes)
 
Line 69: Line 69:
  
  
Security-wise, it is often better to make that listening IP a specific interface, preferably localhost, in that on the listening side, only people on the same host can connect -- and not the entire LAN, or even things like VPN).
+
Security-wise, it is often better to make that listening IP localhost, so that on the listening side, only people on the same host can connect -- and not the entire LAN, or even things like VPN).
  
  

Latest revision as of 00:44, 8 July 2021

Security related stuff.

Practical


Theory


Unsorted


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 that you are establishing a SSH connection,

  • you make one side listen to a specified port (you choose which side, and which port)
-L is "once the SSH connection is established, listen from the connecting client's side"
-R is "once the SSH connection is established, listen at remote/server side"
  • If a connection is made to that litening port, SSH will make a new (non-secured!) TCP connection from the other end, to the a host and port you also specified.


That's still pretty abstract, so here's some introduction via real uses I've had for it:

  • Say I have a database-exploring GUI on my laptop, and a database server that has SSH open but is otherwise firewalled
I might do (e.g. for postgresql, so port 5432):
ssh -L 5432:localhost:5432 dbserver.example.com
so now, when something connects to my laptop's port 5432, like my own GUI, it actually goes to what on the the SSH server's networking side is localhost:5432
As far as the dbserver is concerned, it's seeing a connection from its localhost. Which also means
my laptop's doesn't need to be trusted (the database never even sees its IP)
the server doesn't need to listen to its LAN, and can stay tightly firewalled


  • 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 the connection on the work side is now not to itself, but to a given IP address
ssh -L 3389:192.168.1.200:3389 ssh.work.example.com
note that on the work side, that's still an private network - unroutable beyond that subnet


A different example:

  • I have a monitoring web-app on a work server that I want to check from home, but it's intentionally firewalled as internal-only
It would requires multiple steps of SSH to get to that host
It would be possible to tie multiple tunnels together to get a HTTP connection through
but that's a bunch of thinking, typing, and sort of fragile. Instead...
I have a ssh server at home, so I have a simpler option. While I'm still at work, on that work host:
ssh -R 5000:localhost:5000 ssh.home.example.com
This is functionally much the same as a -L into work
in that it listens at what I call home, I can connect to localhost:5000 and get something from the other side, the work side
the difference is that the SSH connection is easier to set up in this direction
  • Similarly but more flexibly, I've used this like -R 2222:localhost:22 just to leave the option of SSHing open to finish up some admin stuff in the evening
so that once home, SSH to localhost port 2222 is actually the SSH server at work (which has SSH open to the work subnet but not to the world)
I'd run this in tmux to
have that SSH and tunnel connection live longer (see notes below)
and stay connected without me having to leave a graphical terminal open with an shell to my home server


Syntax and some security notes

The typical syntax is

listenPort:toHost:toPort

The full form is

listenIP:listenPort:toHost:toPort

and the first was a shorthand for

0.0.0.0:listenPort:toHost:toPort

i.e. to listen on all interfaces.


Security-wise, it is often better to make that listening IP localhost, so that on the listening side, only people on the same host can connect -- and not the entire LAN, or even things like VPN).


One of the nice things is that the tunneled connection does not appear as a separate connection between the hosts, only at each side, which means each host's firewall can be closed to everything but SSH, and this will still work.

Meaning this is preferable for general security, and means you do not have to bother your network admins to open ports - which they will probably not want to for good security reasons.


There are some practical footnotes, though, like that you'd have to keep the SSH connection open, which by default implies a shell on the other host. You can work around that, though, see notes below.



More recently, you can also listen and connect to a named socket, by its path, which is similarly host-only, but remember there is nothing to shield other users on that host from connecting.

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 (see below)
  • tunneled connections drop when SSH connection drops, for any reason, and don't get re-established unless you've set up something to do that
In some situations this can be prohibitively annoying/fragile
  • you probably can't listen to ports under 1024 (unless you're root)
...which is intentional, and usually a good thing


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

X11 forwarding

SSH clients such as the linux one can forward X windowing, so that you can get remote X clients to display on your SSH client side (...yeah, the X terminology makes that sentence a little confusing).

Basically, from another *nix host you can do: (or from windows, e.g. with putty and Xming)

ssh -X otherhost.com

roughly amounts to:

set up a SSH tunnel
pointing DISPLAY in the shell to that tunnel
handling X authentication


Now, in that shell, you can run any X client, and it'll come to you.

The quickest test of whether it works is often
xclock
or
xeyes
as they're likely to be installed, and lightweight to draw.

For this to work, the ssh server (and client) must have X support, and the server must allow it - which may be off by default for security reasons.

Find the server configuration (probably at /etc/ssh2/sshd_config or /etc/ssh2/sshd2_config) and add/set:

AllowX11Forwarding        yes

As an admin, you may like to restrict it to a few users, e.g.

Match User myusername
  X11Forwarding yes
  AllowTcpForwarding yes

Notes:

  • Don't forget to restart sshd.
  • SSH may not allow this for root, and even if you coerce it, this may not be ideal security-wise
...and you often don't want to su, because you'll get an auth error "MIT-MAGIC-COOKIE-1 data did not match"
...so set it up for the user you want the first time




On security and broken programs

Apparently, X clients (=GUI progams) don't always deal with authentication properly, which means they may break specifically when X11 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,
ForwardX11Trusted
, 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



Proxy tunnel

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" and "Ensuring nothing can get executed at the remote end"


Not having a shell open on it

The tunnel will work as long as the SSH connection is open, which basically implies you need to keep the terminal open (unless you do some extra-finicky things).

Depending on how you connected, that terminal might be found by other people (and the admin could potentially take control of its terminal), which may be a security issue for you.

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


For example to make it something other than a shell.

For a one-time use where you manually need to enter a passphrase to establish each connection anyway, you can give that not-shell command on the client side. (note that something like
watch date
is also effectively keepalive without any config)


For more-than-one-time use, consider making an account on the server side, and configuring ssh on the server side to always run a specific command instead of a shell.

It turns out you can even tie this to specific authorized keys, which may be a nice way of doing a more permanent tunnel anyway, so see SSH_jails#via_authorized_keys.


You could look at SSH jails in general.


Idleness, disconnects, and automatically re-establishing

Idlenes and disconnects

You should assume that firewalls and modems will drop idle connections.

A quick and dirty fix is to not be idle, e.g. on the remote shell run something like
watch date
.

A more structural fix is configuring ssh/sshd's keepalive, which ensures it occasionally sends a do-nothing packet.

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


But in various cases, you must accept that disconnects happen, and you care about...


Automatic re-establishing

Your home internet breaking for a minute will break any one-time tunnel.


So you may want it to reconnect if it breaks.


On *nix:

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

This means you probably want to configure a single user's SSH config to do nothing more than accepting a connection, so that the one useful thing it does for you is carry a tunnel over that same SSH connection. Again, look at ssh jail.


Then:

If you want a tunnel at bootup, keep in mind that modern service management (systemd, upstart, etc) 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
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
respawn limit 0 60
exec ssh -t -R 2221:localhost:22 -t -o "StrictHostKeyChecking=no" -o "BatchMode=yes" -o "ExitOnForwardFailure yes" tunneler@example.com
using systemd
[Unit]
Description=Temporary tunnel for remote access
After=network-online.target

[Service]
User=worker
ExecStart=/usr/bin/ssh -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -o "StrictHostKeyChecking=no" -o "BatchMode=yes" -o "ExitOnForwardFailure=yes" tunneler@example.com -R localhost:2221:localhost:22
Restart=on-failure
RestartSec=1min

[Install]
WantedBy=multi-user.target


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

[Service]
User=worker
Type=forking
ExecStart=/usr/bin/autossh -M 0 -f -t -R localhost:2221:localhost:22 -q -o "StrictHostKeyChecking=no" -o  "BatchMode=yes" -o "ExitOnForwardFailure=yes" tunneler@example.com 
Restart=on-failure
RestartSec=1min

using autossh

The simplest autossh command is

autossh -M 0 user@example.com

Notes:

  • -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 shell script like:

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

Options you may want on a tunnel

Avoid the idle-disconnect problem:

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


By default, a tunnel failing to establish won't prevent a SSH connection from being made. But if the only point of this connection is that tunnel, then you probably do want it to fail if a tunnel fails:

-o "ExitOnForwardFailure yes"


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

-i SSH_KEY_FILE_PATH

Fail if the login isn't passwordless (that is, if you get a password question, don't hang waiting for timeout - useful for background jobs):

-o BatchMode=yes

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)

Errors

X11 forwarding request failed on channel 0

possible causes include

  • xauth not installed, e.g. because it's a server with a minimal install withouth X's basis
  • 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 try forcing SSH to do IPv4 only with
AddressFamily inet


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

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

In other cases not. Even -vvv said nothing useful when 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 only.

Authentication refused: bad ownership or modes for directory /home/someone

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

I mostly run into this when I run ssh from things other than a shell (e.g. ssh/scp from cron, a service, and such), so a (pseudo-)terminal does not apply.


You can disable or 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 in a more interesting case, where I was trying to automate going to a firewalled host in two ssh 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.

There are likely other reasons.

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)

...plus the actual port number.

Often seems to mean there is something already listening.

Which may be something else, but for me was usually just another copy of my own SSH trickery.

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

Semi-sorted

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