Multiple concurrent rsyncs over a single SSH tunnel

Posted on June 14th, 2008 by dandavis

This might not have much of an audience, but I needed to write it so I decided to share it…

I had a need to download log files from a number of security appliances out on a network. The problem was that I needed to access these appliances from a specific server (let's call it the Management Server [192.168.1.1]). Well, I guess that wasn't the real problem… the real problem was that the management server did not have enough hard drive space and the server that did have the space did not have direct access to the appliances.

What's a lazy scripter to do? I mean, sure, I could've put in requests to the network guys to get my storage machine routed to the appliances… and then there are the firewall requests to go with that… oh, screw it.

My management server and security appliances all accept require SSH with public key authentication. How can I leverage that into something that means less work for me?

Well, first I make sure that my public key is on the server and appliances so my scripts can login.

Then I setup my ~/.ssh/config file so that my local machine can access each appliance via a LocalForward:

Host 192.168.1.1
   LocalForward 2220 APPLIANCE_01:22
   LocalForward 2221 APPLIANCE_02:22
   LocalForward 2222 APPLIANCE_03:22
   LocalForward 2223 APPLIANCE_04:22
   LocalForward 2224 APPLIANCE_05:22
   LocalForward 2225 APPLIANCE_06:22
   LocalForward 2226 APPLIANCE_07:22
   LocalForward 2227 APPLIANCE_08:22
   LocalForward 2228 APPLIANCE_09:22
   LocalForward 2229 APPLIANCE_10:22
   LocalForward 2230 APPLIANCE_11:22
   LocalForward 2231 APPLIANCE_12:22
   LocalForward 2232 APPLIANCE_13:22

Here is a link to a file containing the same information: SSH .config file.

OK, so now I have my public key out and about, I have the SSH config file setup to tunnel some ports through my management server out to my appliances. Now it's time to do the work:

#!/bin/bash
 
function GetLogs {
   ApplianceName="$1"
   AppliancePort="$2"
   echo "["`date +'%Y-%m-%d %H:%M:%S'`"] ${ApplianceName}: RSYNC Initiating over port ${AppliancePort}."
   /usr/bin/rsync -aqz \
      -e "ssh -p ${AppliancePort}" \
      support@localhost:/logs/ /home/dandavis/appliancelogs/${ApplianceName}/ 2> /dev/null
   echo "["`date +'%Y-%m-%d %H:%M:%S'`"] ${ApplianceName}: RSYNC Completed."
}
 
# Create tunnel connection
echo -n "["`date +'%Y-%m-%d %H:%M:%S'`"] Building SSH Tunnel to Management Server... "
ssh -S /home/dandavis/.tunnel.socket -M -N -f 192.168.1.1
echo "Done."
 
# kick off the rsyncs into background
echo "["`date +'%Y-%m-%d %H:%M:%S'`"] Initiating RSYNCs."
GetLogs APPLIANCE_01 2220 &
GetLogs APPLIANCE_02 2221 &
GetLogs APPLIANCE_03 2222 &
GetLogs APPLIANCE_04 2223 &
GetLogs APPLIANCE_05 2224 &
GetLogs APPLIANCE_06 2225 &
GetLogs APPLIANCE_07 2226 &
GetLogs APPLIANCE_08 2227 &
GetLogs APPLIANCE_09 2228 &
GetLogs APPLIANCE_10 2229 &
GetLogs APPLIANCE_11 2230 &
GetLogs APPLIANCE_12 2231 &
GetLogs APPLIANCE_13 2232 &
 
# wait for rsyncs to finish
while [[ `ps -deaf | grep -v grep | grep rsync | grep APPLIANCE` ]]; do sleep 5; done
 
# Close tunnel connection
echo -n "["`date +'%Y-%m-%d %H:%M:%S'`"] Closing SSH Tunnel to Management Server... "
ssh -q -S /home/dandavis/.tunnel.socket -O exit 192.168.1.1 2> /dev/null
echo "Done."

And, just in case Wordpress… um… improved upon my formatting, here's a direct link to the script: mainsshtunnel.sh.

WTf?!?

Starting at the top and working down…

The function GetLogs has the steps we follow for each appliance. It prints a message to the console indicating that the rsync is about to begin. Then we do the actual rsync utilizing ssh over a specific port (${AppliancePort}). When it's done, it prints another message to the console.

The first real commands in the script print out a message to the console (I love messages to the console… they're so… informative). Then it creates the SSH tunnel to the management server. The options are:

  • -M Puts the client into "master" mode… makes it easier to kill off later on.
  • -S Related to the -M option, this sets the control path that we'll use to communicate with the master client later on.
  • -N Tells the client not to execute a command on the remote host. Essentially to just connect and then chill out.
  • -f Throws the client into the background so we can get on with our business.

Now that the tunnel is up, we can start our rsyncs running. With my network setup, it's no problem running a dozen or two simultaneous rsyncs. In a future post, I'll rewrite this script a bit to allow the user to control the number of concurrent rsyncs running. To start an rsync, we just call the GetLogs function with the appliance name (for directory structure only cause the rsync-ssh actually connects to localhost), the local port on which the ssh client is listening for that particular appliance, and & to throw the whole thing into the background.

After all the rsyncs are started up, we get into a while loop waiting for them to finish up (or die off, whatev). You might need to tweak the ps and grep commands a bit to get them just right for your setup.

Once all the rsyncs are done and the while loop exits, we send a control command to the master ssh client telling it to exit.

That's it. We've got our logs downloaded.

One Response to 'Multiple concurrent rsyncs over a single SSH tunnel'

Subscribe to comments with RSS or TrackBack to 'Multiple concurrent rsyncs over a single SSH tunnel'.


  1. on February 20th, 2009 at 6:26 am

    [...] to the server. While this is usually negligible, it has still irked me. Some googling turned up this post. Nice. One SSH tunnel for rsyncing back and [...]

Post a comment