SSHTunnel

Tunnel the html through the proxy

In a shell on the local machine

ssh -L 1080:proxymachine:80 user@server_behind_firewall

and then in a browser (dedicated browser for tunneling so you dont have to keep changing proxies) you set the proxy to localhost, port 1080, and have no exceptions.

SSH: a temporary back-tunnel

If you are at a remote location where the network is blocked by a firewall (iThemba, for example) it's impossible for someone at UJ, or anywhere else, to connect to your PC - which is the security that you want from a firewall, in general. But if you, for example, need someone from UJ to run a program on that PC - then it becomes a problem.

A way to circumvent this is to use SSH (Secure SHell) to make a "tunnel":

 ssh -R 11022:localhost:22 myserver

In this way port 11022 (or your choice of a random high port, >1024) on myserver (a server where ssh in is possible) will be temporarily be connected to port 22 (which is the standard ssh port) of your remote PC. So your colleague at UJ can just login on to myserver, and do

 ssh -p 11022 user@localhost

and this will actually connect him to your remote PC at iThemba !

HOWTO: Create a permanent ssh back-tunnel

This is for a permanent setup. For a once off case, the recipe above is sufficient.

Warning: if this looks complicated, it's because it is complicated! Actually, it's not that complicated, but it takes quite a few steps. Some of these steps could be avoided if OpenSSH on SL4 and SL5 supported the ExitOnForwardFailure option, but it doesn't; becuse of this, it's necessary to arrange an automatic reverse connection so that the script can check periodically that the tunnel is actually alive, and it's not just a plain ssh connection without the tunnel.

  • host1.example.com - incoming open
  • host2.example.com - incoming closed
  • Create a user
root@host1# useradd tunnel
root@host1# su - tunnel
  • Create ~/.ssh/ with the right permissions
tunnel@host1$ ssh localhost
  • Now generate the id-dsa key
tunnel@host1$ ssh-keygen -t dsa
  • Repeat steps 1-3 on host2
root@host2# useradd tunnel
root@host2# su - tunnel
tunnel@host2$ ssh localhost
tunnel@host2$ ssh-keygen -t dsa
  • Exchange keys
tunnel@host2$ cat .ssh/id-dsa.pub # then copy from one host
tunnel@host1$ cat >> .ssh/authorized_keys # and paste on the other (ctrl-D to end)
tunnel@host1$ chmod go-w .ssh/authorized_keys 
tunnel@host1$ cat .ssh/id-dsa.pub # then copy from one host
tunnel@host2$ cat >> .ssh/authorized_key # and paste on the other (ctrl-D to end)
tunnel@host2$ chmod go-w .ssh/authorized_keys 
  • Connect from host2 to host1 to test and accept key fingerprints:
tunnel@host2$ ssh host1.example.com
  • Configure the tunnel with host2:~tunnel/.ssh/config:
Host host2_host1_tunnel
     Hostname host1.example.com
     User tunnell
     Compression no
     ForwardX11 no
     KeepAlive yes
     GSSAPIAuthentication no
     RemoteForward 10001 localhost:22
     #GatewayPorts yes # uncomment this to make this tunnel available to other hosts
     BatchMode yes
     #ExitOnForwardFailure yes # not implemented in OpenSSH 4.3
     ServerAliveInterval 3

Host host1_tunnel_test
     Hostname host1.example.com
     User tunnell
     Compression no
     ForwardX11 no
     BatchMode yes
     ConnectTimeout 30
  • Now run the tunnel manually from host2:
tunnel@host2$ ssh host2_host1_tunnel
  • On host1, Connect from host2 to host1 to test and accept key fingerprints:
tunnel@host2$ ssh host1.example.com
  • Append to host1:/etc/ssh/ssh_config:
Host host2
     Hostname localhost
     Port 10001
     HostKeyAlias host2_tunnel
     CheckHostIP no

This makes it easy for a user on host1 to use the tunnel. The HostKeyAlias and CheckHostIP options prevent problems with conflicting keys stored in .ssh/known_hosts, especially if you use multiple tunnels.

  • Now, to make this fully automatic, we need a script that runs the connection, and also checks that it actually works - because OpenSSH 4.3 only issues a warning if it cannot make the tunnel.
    Script /usr/local/sbin/host1_tunnel.sh on host2:
#!/bin/bash
#set -x
#while true; do

T=host2_host1_tunnel
H1test=host1_tunnel_test
H2test=host2

LOG="logger -i -p authpriv.info -t tunnel "

if [ -z "$(pidof sshd)" ]; then
   $LOG "Waiting for sshd to come up"
   sleep 30
   exit 1
fi

SPID=$(ps xa | grep ${T} | grep ssh | cut -b-5)
if [ -z "$SPID" ]; then
   #echo "Starting tunnel"
   $LOG "Starting tunnel $T"
   ssh -f -N ${T}
   sleep 30 # give it time to come up
else
   if ! ssh $H1test "ssh $H2test -o ConnectTimeout=15 true 2>/dev/null" ; then
      $LOG "Killing dead tunnel $T at  $SPID"
      kill -9 $SPID
      sleep 10 # give it time to die
   else
     # All fine, rest 10 minutes
     sleep 600
   fi
fi
#done
  • To have the script running all the time, there are a few different solutions. My preferred one at the moment is to add the script to /etc/inittab on host2:
t1:345:respawn:/usr/local/sbin/host1_tunnel.sh

and have init reload it:

/sbin/telinit q

Notes:

  • No password is assigned to either tunnel@host1 or tunnel@host2. This means that these users can only connect using the SSH keys - which is good for security.

Known bugs:

The tunnel can fail for a while if the connection is temporarily dropped, and host1 still keeps the local sshd instance running and the tunnel port open, so that a new tunnel cannot use it, until the TCP socket dies of natural death. For a faster recovery, assuming the connection is up again, try the following:

host1$ sudo netstat -l -p --tcp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 localhost.localdomain:10001 *:*                         LISTEN      4915/sshd: tunnel

host1$ sudo kill 4915