How to get an ssh tunnel started every time the Mac boots

big surlaunchdplistssh

I'm trying to run a script at startup to tunnel to a remote host over a random port.
I want to leave a cookie on the remote machine to say which port I chose.

Something like this:

# to make a record of the tunneling port on the remote host
echo $remoteport > $portfile
scp $portfile rcook@linode.richcook.net:  
# to actually do the tunnel and go into the background
ssh -n -i /Users/rcook/.ssh/id_rsa -o ExitOnForwardFailure=yes -R $remoteport:localhost:22 rcook@linode.richcook.net 
# to make a record of the tunneling port on the remote host
# keep checking if ssh is up
while ps -Awwx -o "user pid ppid pcpu pmem comm args" |  egrep -v grep |  egrep "${remoteport}:localhost"; do
       echo found
       sleep 30
done
echo finished at $(date)

I then point to it from a plist to run at startup.
I'm having multiple problems here.

  1. How to run ssh at login?
  2. How to tell ssh to set up a port and then just hold the port open forever?
  3. How to check on the ssh connection to make sure it's still up and reestablish if it goes down?

I know my answer is through launchctl and ssh but I just can't get all the pieces to line up.

FWIW, here is my LaunchDaemon. Have no idea if it works, since I can't solve the ssh problems first. This is complicated.

root@Richards-iMac (~ ): cat /Library/LaunchDaemons/Tunneler.plist 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:/usr/local/sbin</string>
    </dict>
    <key>GroupName</key>
    <string>admin</string>
    <key>KeepAlive</key>
    <dict>
        <key>Crashed</key>
        <true/>
        <key>SuccessfulExit</key>
        <true/>
    </dict>
    <key>Label</key>
    <string>Tunneler</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/rcook/bin/linode_tunneler.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>UserName</key>
    <string>rcook</string>
</dict>
</plist>

Best Answer

OK, no sooner asked than I figured out the last few pieces.
To get ssh to do what I wanted, here is the shell script to do what I want:

#!/usr/bin/env bash

echo $0 run on $(date)
# Just chose an arbitrary 401 value range of random numbers.  
randport=$(/usr/bin/python -S -c "import random; print random.randrange(5555,5955)")
remoteport=$randport # this way we always know what port we are using.  Fix other problems later... 

portfile=/Users/rcook/.ssh/linode_remote_port.txt

echo $remoteport > $portfile
scp $portfile rcook@linode.richcook.net:
ssh -N -n -i /Users/rcook/.ssh/id_rsa -o ExitOnForwardFailure=yes -R $remoteport:localhost:22 rcook@linode.richcook.net &

sleep 5
while ps -Awwx -o "user pid ppid pcpu pmem comm args" |  egrep -v grep |  egrep "${remoteport}:localhost"; do
       echo found
       sleep 30
done
echo finished at $(date)

I put the launchctl script into /Library/LaunchDaemons/Tunneler.plist. This way it loads at system launch.