Part 6: how to reverse SSH from your office

Here I'll explain how to connect through an office firewall. A firewall prevents outside connections from coming in.
Basically, you need a server at the office that will initiate an SSH connection to your home server. Your home server can then follow this connection back through the firewalls to the office network. The connection that does this is a reverse or remote tunnel.

You need to
(a) Overview,
(b) Windows XP = no; Linux = yes.
(c) Scripts needed to make this work
(d) Future Improvements


(a) Overview:

I assume that your office is behind a firewall (as well as NAT) and that you can't access it directly from the internet.

What needs to be accomplished:

  1. The remote computer (at the office) should automatically reconnect if the connection is dropped.
  2. The remote computer needs to check if the ssh connection is frozen. If it is, then kill the connection and restart it.
  3. The remote computer must be able to handle my home server (Fishbuntu) having a dynamic IP address.
  4. The automatic connection must enter passphrases by itself (we have disabled password access on Fishbutu for security reasons).
  5. The remote computer should be able to do all of this even when it has a power failure and has to reboot.

The only way I can do this is by quickly setting up a Linux server at the office using a spare computer (called Work-LinSrv in the diagram below). This computer is just left running all the time. In the BIOS, it is set to turn itself on again whenever there is a power failure. (Obviously, you have to make sure that you can do this without getting caught and fired!) Read on to see why I couldn't do this in WinXP.

  • What you have to do is have a computer at the office make an SSH -R connection to your home Linux server, fishbuntu. (This is the green connection in the diagram below.)
  • You can then connect back from your home server at any time through this tunnel to your work computer. (This is the red connection in the diagram below.) The green connection is set to reconnect automatically if it gets disconnected.
  • The red connection is made any time you need to access your office computer remotely. Note that you can also do this from anywhere in the internet as long as you connect to fishbuntu first (see part 3).

I am also assuming that your home IP address is dynamic. This is why there is a WGET (or CURL) step in the script.


Problems with Windows XP automatically initiating a SSH -R connection         (You can skip this box)

  • Pageant (with Putty) will store your passphrases, but there is no way to have this done automatically if the computer is rebooted. You have to type them in manualy.
    • I wrote a script to automatically enter the passphrase into Pageant and compiled this into an exe.
    • However, pageant requires that there be a window - for the key to be typed in. It will not work in scheduled tasks, nor in the startup folder, nor in Autoexec.bat. Pageant starts running, but there is never a window for the key to be typed in and no icon in the system tray.
  • The next thing to do is to hardcode the key into the source code and recompile the pageant executable. This is not a huge security risk as (i) the passphrase is ONLY used for the account which has "limited shell" running, (ii) you still need the key file.
  • Putty pops up a window on the screen when it is running. This is distracting and not desirable.
  • Try to run Putty automatically:
    • put it into Scheduled tasks -- this may work, except that it always leaves a Putty window on the desktop.
    • use "srvany" to make Putty a service, but it doesn't have a restart option (i.e. to automatically restart the service if the program dies)
    • use "launchsrv" to make Putty a service, but it doesn't take commandline parameters. (I was using a script to get my new IP address first and then start Putty, bu this script needed command line parameters. I later changed from the Kixtart scripting language to AutoIt which allows you to make a script into an EXE.
  • I tried to run Putty as a service, but it wouldn't get the passphrase from Pageant.
  • A solution is to have an account on Fishbuntu that has not password, and to allow logins with no password. This is terrible security.
  • I tried using Cygwin, but had real trouble getting ssh-agent and then ssh to work automatically.
  • When the IP address changes, Putty asks if I want to save the new HOST fingerprint or not. This can be handled using ssh in Linux because it includes an option: -o StrictHostKeyChecking=no
  • I was able to fix this by writing a script that adds the host fingerprint to the IP address in the registry before Putty is run.


(c) Setup using scripts

1. On Work-LinSrv install all of the basic stuff that you installed for your home server (e.g. OpenSSH). Make a public/private key pair. Transfer the public key to Fishbuntu (see part 3). Try and log on to make sure that the passphrase works.

2. Install the following packages:
screen: "sudo apt-get install screen"
expect: "sudo apt-get install expect"
timeout: "sudo apt-get install timeout"

3. Add the following scripts. I put mine in a directory called "$HOME/scripts". Make the appropriate adjustments for other locations.

Put the following in your crontab
# every 15 minutes check for SSH connection */15 * * * * $HOME/scripts/sshstarter # midnight once a week (stop log file from growing too large): 0 0 * */2 * mv $HOME/scripts/sshconnect.log $HOME/scripts/sshconnect.log.old
Copy the following and paste it into a file called sshstarter:
Make sure that you change the line just about step 3.b. to your correct ISP (basically the correct results of "uname -n")
#!/bin/sh # # Note: This is version 'b'. So now all of the scripts called have a 'b' at the end. #================================================================== # GENERAL INFO: # # run this script from crontab. # Output is written to a log file "sshconnect.log" mostly for debugging. # ---------- # 1. see if ssh is running # 2. if ssh is NOT runing, then start SSH # (don't have the SSH script do the automatic restarting every 15 minutes, # just start it once, crontab will check every 15 min) # exit # 3. if ssh does exist then # a. do a uname -a to check the connection # b. if this works, exit -- wait another 15 minutes ... # c. if this does not work, the connection is frozen. # i. kill the ssh connection # ii. start a new one # #================================================================== # FUNCTIONING SCRIPT # DT=`date` echo "\n\n$DT" >> $HOME/scripts/sshconnect.log # Step 1. see if ssh is running ANS1=`ps -ef | grep ssh | grep StrictHostKey` if [ $? -eq 0 ]; then # the command worked ($? is the return value) echo "Step1. SSH connection exists" >> $HOME/scripts/sshconnect.log else #Step 2. start SSH, then exit echo "Step2. starting SSH" >> $HOME/scripts/sshconnect.log $HOME/scripts/part1b exit 0 fi #Step 3.a. ssh exists, check if it is frozen. #start ssh-agent and set environment variables using eval ANS2=`/usr/bin/ssh-agent` echo "Step 3a. Start ssh-agent \n$ANS2" >> $HOME/scripts/sshconnect.log eval `echo $ANS2` ##find the PID ... ##PID=`ps -ef | grep /usr/bin/ssh-agent | awk '{ print $2 }'` ##echo $PID ##SSH_AGENT_PID=$PID ##export SSH_AGENT_PID #now the variables are set and I can run SSH-ADD in the following script expect $HOME/scripts/script.exp #see if the ssh connection is alive. # for some reason this works $(...) #ANS3=$(ssh -p 5999 gia321@localhost uname -n) ANS3=`timeout 30 ssh -p 5999 gia321@localhost uname -n` #kill the ssh-agent we just created. It's no longer needed. ssh-agent -k if [ $ANS3 = "fishbuntu.yourISP.com" ]; then #Step 3.b. everything works; exit. echo "Step 3b. uname -a worked" >> $HOME/scripts/sshconnect.log exit 0 fi #Step 3.c. the connection is frozen #Step 3.c.i. kill the ssh-connection PID=`echo $ANS1 | awk '{ print $2 }'` echo "Step 3c.i. killing SSH PID=$PID" >> $HOME/scripts/sshconnect.log kill $PID #Step 3.c.ii. restart the connection #echo sleeping 20 sleep 20 echo "Step 3c.ii. Starting SSH" >> $HOME/scripts/sshconnect.log $HOME/scripts/part1b #DONE!!! exit 0
Copy and paste the following into script.exp
Expect is a program that allows passwords to be entered automatically (so you don't need a person logged on to do it).
#!/usr/bin/expect -f # # This Expect script was generated by autoexpect on Sat Sep 6 7:08:09 2010 # Expect and autoexpect were both written by Don Libes, NIST. # # Note that autoexpect does not guarantee a working script. It # necessarily has to guess about certain things. Two reasons a script # might fail are: # # 1) timing - A surprising number of programs (rn, ksh, zsh, telnet, # etc.) and devices discard or ignore keystrokes that arrive "too # quickly" after prompts. If you find your new script hanging up at # one spot, try adding a short sleep just before the previous send. # Setting "force_conservative" to 1 (see below) makes Expect do this # automatically - pausing briefly before sending each character. This # pacifies every program I know of. The -c flag makes the script do # this in the first place. The -C flag allows you to define a # character to toggle this mode off and on. set force_conservative 0 ;# set to 1 to force conservative mode even if ;# script wasn't run conservatively originally if {$force_conservative} { set send_slow {1 .1} proc send {ignore arg} { sleep .1 exp_send -s -- $arg } } # # 2) differing output - Some programs produce different output each time # they run. The "date" command is an obvious example. Another is # ftp, if it produces throughput statistics at the end of a file # transfer. If this causes a problem, delete these patterns or replace # them with wildcards. An alternative is to use the -p flag (for # "prompt") which makes Expect only look for the last line of output # (i.e., the prompt). The -P flag allows you to define a character to # toggle this mode off and on. # # Read the man page for more info. # # -Don set timeout -1 spawn ssh-add match_max 100000 expect -exact "Enter passphrase for /home/osmium/.ssh/id_rsa: " #Type your correct passphrase to Fishbuntu in the next line send -- "oh be a fine girl kiss me\r" #why do I need the next line? Expect EOF ?? expect eof
Copy and paste the following into part1b.
It starts the next scripts running in "screen" which means that the script can run without a terminal attached to it.
#!/bin/sh screen -S fishy -t fish -dm $HOME/scripts/part2b
Copy and paste the following into part2b
This starts the ssh-agent. I could probably do this a different way, but this is a hold over from my version (a) of my scripts.
#!/bin/sh ssh-agent $HOME/scripts/part3b
Copy and paste the following into part3b
This runs ssh-add which actually askes for the passphrase and stores it (ssh-agent does this). It then runs the script that actually does the reverse SSH connection with all of the tunnels.
Modify the tunnels as needed in this script.
#!/bin/sh expect $HOME/scripts/script.exp $HOME/scripts/revSSH4b
Copy and paste the following into revSSH4b
This script will also get the IP of your homeserver even if it has changed. Add in any reverse tunnels that you need to in here.
#!/bin/sh # version b. This version does not include an infinite loop, checking the connection every 15 seconds. # This is done using Crontab instead. createTunnel() { # -n does not work. I think it kills the connection # Add in all of the tunnels needed here ssh -o StrictHostKeyChecking=no -R 5522:localhost:22 -R 9191:10.10.200.54:9100 -R 5511:10.10.0.101:5900 -p 2203 gia321@`cat $HOME/scripts/myip.num` if [ $? -eq 0 ]; then echo Tunnel to host created successfully # > /dev/null else echo Tunnel terminated or never created. Return value was $? fi } wget http://mysite.com/myip.num -O $HOME/scripts/myip.num ## Run ps -ef to check process. If it returns non-zero, then create a new connection ps -ef | grep ssh | grep gia321 if [ $? -ne 0 ]; then echo Creating new tunnel connection createTunnel fi

You're done! Now test it.

Now: do the following steps to connect to Work1 via VNC.

  1. Connect to Fishbuntu using Putty. Make sure that you have the following tunnels: L5522 to localhost 5522 (this allows a tunnel to Work-Linsrv) and L5511 to locahost 5511 (which makes part of the tunnel for VNC). * You'll have to type in your passphrase for Fishbuntu
  2. Run VNC with a connection to 127.0.0.1::5511
    * You'll have to type in the password that you set when you installed the VNC server on Work1 (which is IP 10.10.0.101, port 5900)
    You are now connected from home or anywhere in the world, to your computer "Work1" in the office.
  3. If you want to SSH to Work-LinSrv: start a new Putty session with a connection to Localhost:5522. No tunnels needed to be defined. This will put you at the Linux command prompt on Work-LinSrv.

    You can also type this in from the command prompt on Fishuntu: ssh -p 5522 osmium@localhost to SSH to WorkLinSrv

The next things to do are: (i) Set up your home computer so that it can print to the office printer (see part 8) and, (ii) connect Work-LinSrv to your office "Active Directory" network so that you can transfer files stored on your office network (see part 11).


(d) Future Improvements

It would not be too hard to modify these scripts (i.e. sshstarter) so that the server at work does not maintain a constant connection to your home server. It could check a webpage for an ON/OFF value which would tell it to make the connection or not. This would help avoid detection if your IT department is vigilant (there may be other reasons too). You just connect when you need to by setting the flag on the webpage, then the work server checks it every 15 minutes and creates the SSH connection. You could also set up a webpage that lists the tunnels you want the office server to make.

ALL OF THIS IS DONE!!. See part 6B of this website.