Thursday, October 15, 2015

SSH tunneling: handling your mom's linux box, remotely and anywhere.

I want to be able to log on the linux box of my mom, wherever she is.

This helps both me and her, so I can administrate, debug or backup her computer without having to tell her bad words. Here is what I did, thanks to an automatic SSH tunneling from her to me.

The fact that she is here and there all the times makes it impractical to configure the different internet boxes, routers and NATs. I want to be able to reach here even when she is in a cyber-café (not her kind, but this is out of topic).

Server side (configuration)

This is to be done on one of my server, or on my own computer as long as it has ssh server and a known IP address

Create a new user called, e.g.''@myclass:revssh-mom'':
root@myserver:~# useradd revssh-mom
root@myserver:~# mkdir -p /home/revssh-mom/.ssh

Still, I want this new user only to install a tunneling back to him. I certainly do not want that a hacker gets access to my mom's PC and use this account to log in my system and issue arbitrary commands. To do so, just add the following to your server's ''/etc/ssh/sshd_config'':
Match User revssh-mom
  #AllowTcpForwarding yes
  #X11Forwarding no
  #PermitTunnel no
  #GatewayPorts no
  AllowAgentForwarding no
  PermitOpen localhost:65432
  ForceCommand echo "Sorry, this account can only be used for tunneling"

65432 is the port that will be left open locally on your server for you to log in and "teleport" to your mom's linux (you can add more ''localhost:<port>'' entries on this same line, in case you are using a single user for multiple remote tunneling).

By the way, if you wonder hos I format the code above, just check this post. It is as easy as putting two single quotes around it (no need for SyntaxHighlighter or other heavy stuff).

Mom's side

Now on her linux box:
root@mymom:~#sudo su -
root@mymom:~#ssh-keygen # but no need to override existing SSH keys, you will be prompted

Then add this public key ''/root/.ssh/'' to the list of allowed connections to the remote user, on the server in ''/home/revssh-mom/.ssh/authorized_keys''. You may want to restrict further the use of this access by tuning prefixed parameters (see ssh/sshd man pages).

On your mom's PC, configure ''/home/mymom/.ssh/config'' for the appropriate login information:
  User revssh-mom
  Port 12345

Here, 12345 would be the usual SSH port of your server if you are not using the default 22, and the specific user you created for the tunneling - this config makes life easier and simpler.
Make this file private with ''chmod -R go-rwx /home/mymom/.ssh''.

Idem on the server: ''chmod -R go-rwx /home/revssh-mom/.ssh''. Now restart the service with ''service ssh restart''.

At this point, check that you can log from your mom to your server: you should be authenticated, but thrown out immediately like this:
root@mymom:~# ssh
Sorry, this account can only be used for tunneling Connection to closed.

This is good: only tunneling can be done by your mom, and no other command on your server. First, install these:

root@mymom:~# sudo apt-get update
root@mymom:~# sudo apt-get install openssh-server autossh

Now, typing ''ssh -N -R 54321:localhost:80 revdust'' is enough to establish the reverse ssh tunnel (you need to exit with <Control-C>). But we are going to use a robust wrapper named ''autossh'', which enhances the stability of the connection:

Using it, we open a tunnel from the server back to your mom's in the background this way:
root@mymom:~# autossh -fN -M 3986 -R 65432:localhost:22

Check again the port number. This will log on the server to create a local socket to listen to (on the server), which tunnels back to your mom. Obviously, make sure to match the same port as ''PermitOpen'' on the server sshd configuration.

To check if the tunnel is there, you can type in this command on the server:
root@myserver:~# netstat -tanp | grep 65432

It should reply something like this:
tcp  0  0*  LISTEN  9627/sshd: revssh-mom

Log from the server to your mom's

Eventually, to log as root on your mom's PC, you just log on your server local tunneling port:
root@myserver:~# ssh root@localhost -p 65432
Welcome to Ubuntu 15.04 (GNU/Linux 3.19.0-30-generic x86_64)
   * Documentation:
Last login: Thu Oct 15 10:57:05 2015 from localhost
Here we are. You can add the source server user '''' key to the ''authorized_keys'' of the target user on your mom's PC, so it does not ask your mom's password (as above).

As a one-liner you can type this instead (still from the server), in case you want to access to your mom's only via the dedicated server user:
su -c 'ssh root@localhost -p 65432' revssh-mom

Note that you can also use ''rssh'' to restrict what the server can do on the PC. If you only want to backup her PC, then ''rsync'' is enough, and no interactive login is required. Use the ''-e'' option then, e.g.:
rsync -e "ssh -p 65432" sourcedir root@localhost:/remotedir

Note: restricted shells are often useful, for example as I did here in order to upload data via a ''sshfs''-mounted directory towards a remote apache website.

Establish the SSH tunneling whenever possible

There is still something to be done: make my mom's PC automatically establish the tunnel on my server so I can log on her PC from my server. Either I create a bash shortcut in her menu, so she keeps the control, or I do it without telling her in a cron:

On her PC, you can rely on ''crontab -e'' and create this line:
@reboot sleep 120 && /usr/bin/autossh -fN -M 3986 -R 65432:localhost:22

This cowardly waits for 2 minutes before establishing the tunnel. You can otherwise set in in ''/etc/rc.local'', which is considered a better pratice.

If you want to do it more properly, you should handle the dependency on the availability of the network this way: create the file ''/etc/network/if-up.d/666tunnel_myserver'' with this content:
autossh -fN -M 3986 -R 65432:localhost:22

And make it executable with ''chmod +x 666tunnel_myserver''. This way, when the network is up, the tunnel will be made.

Using systemd in case it does not work, add this file in ''/etc/systemd/system/ssh-tunnel.service'':

Description=Upgrade context reverse ssh

ExecStart=/usr/bin/ssh -i /root/.ssh/id_rsa -o "ExitOnForwardFailure yes" -N -p 12345 -R 65432:localhost:22


This is better as she often works offline anyway :D

One final note, use ''-t'' to allocate a pseudo terminal when jumping directly from your machine to the target via the server tunnel:

ssh -t "ssh remoteuser@ -p tunnelport"
Note: autossh probably benefits a lot from re-using existing ssh connections. To do so, on the client, run ''mkdir ~/.ssh/sockets; chmod go-rwx  ~/.ssh/sockets'', and add this preable to ''~/.ssh/config'' :

# Re-use existing SSH connection
Host *
ControlMaster auto
ControlPath  ~/.ssh/sockets/%r@%h-%p
ControlPersist 600

Nasty bits

These are work-arounds to try and keep a stable tunnel. It used to break every 10-15 minutes while I was using heavy ''rsync'' to push system backups to a remote Raspberry Pi.

Some advertise the use of ''sysctl -w net.ipv4.tcp_sack=0'' but it had no benefit for me (this switch can be made permanent by adding it to ''/etc/sysctl.cong'').

In my case, it eventually worked this way: I added or modifed the following lines in ''/etc/ssh/sshd_config'':
TCPKeepAlive yes
ClientAliveInterval 30
ClientAliveCountMax 9999

I then rewrote, the ''/etc/network/if-up.d/666tunnel_myserver' to disable its own keep-alive and rely on the above TCP-level protocol instead. Only then the tunnel became much more stable.
autossh -fN -M 0 -R 65432:localhost:22

I also found a post with scripts for additionnal tuning of ''autossh'', via environment variables, but it was not useful to me so far.

No comments:

Post a Comment