====== Implementing a secure remote backup using unix tools ======
I wanted to store the backup of my server onto another machines filesystem.
===== Features =====
Features that I wanted the solution to provide:
* different backup sets (to have different backups)
* full backup
* differential backup
* incremental backup
* verify
* restore ;-)
* fault tolerant backup (one broken bit should not make a backup useless)
* encrypted backups
* secure network transmission
* bandwith limiting
===== Tools =====
All this can be done by making it the common unixway: tightening together some unixtools
Tools that I came across:
* [[http://tinyplanet.ca/projects/tob/|tob]] - "Tape Oriented Backup" is a simple but powerful backup script that uses afio (fault tolerant!) to do the actual backup. It is highly configurable and understands full, diff and inc backup and can verify and restore backups
* [[http://loop-aes.sourceforge.net/aespipe/|aespipe]] - a small and very cool tool to encrypt a data stream using the fast and secure AES encryption algorythm
* [[http://www.openssh.org|openssh]] - The secureshell client can be used to transmit data
* [[http://www.cons.org/cracauer/cstream.html|cstream]] - A tool which can be used to limit a stream to a specified Bandwith.
===== Tob patch =====
I needed to extend tob a bit so that it supports remote backups (patch is against tob 0.23):
--- tob.bak Tue Mar 29 19:33:37 2005
+++ tob Thu Mar 31 13:08:02 2005
@@ -191,7 +191,7 @@
error "BACKUPDIR is deprecated: check your config file, and rename it to BACKUPDEV"
fi
- if [[secureremotebackup|-d "$BACKUPDEV" ]]; then
+ if $RSH_CMD [[secureremotebackup|-d "$BACKUPDEV" ]]; then
BACKUPDIR=$BACKUPDEV;
BACKUPDEV="${BACKUPDIR}/${VOLUMENAME}_${DATE}_${TYPE}.gz"
@@ -466,8 +466,8 @@
runverify ()
{
VOLUMENAME=$2 # store name for usage in .rc file
- if [[secureremotebackup|-d "$BACKUPDIR" ]]; then
- BACKUPDEV=`$LS ${BACKUPDIR}/${VOLUMENAME}_*full.gz`
+ if $RSH_CMD [[secureremotebackup|-d "$BACKUPDIR" ]]; then
+ BACKUPDEV=`$RSH_CMD $LS ${BACKUPDIR}/${VOLUMENAME}_*full.gz`
fi
message "Now starting verify program for $BACKUPDEV."
( cd /;
@@ -554,20 +554,32 @@
$RM -f $TOBLISTS/$2.Full* $TOBLISTS/$2.incremental* $TOBLISTS/$2.differential.*
gzip -c $TMPLIST > $TOBLISTS/$2.Full.gz
- if [[secureremotebackup|-d "$BACKUPDIR" ]]; then
+ if $RSH_CMD [[secureremotebackup|-d "$BACKUPDIR" ]]; then
UPTO=`basename $BACKUPDEV`
SEEN='0'
- cd $BACKUPDIR
- for i in `find ${VOLUMENAME}_* -mtime +$MAXBACKUPAGE -exec echo {} \;`; do
- if [[secureremotebackup|$i == $UPTO ]]; then
- SEEN=1
- fi
- if [[secureremotebackup|$SEEN == '0' ]]; then
- message "Deleted old backup $i"
- $RM -f $i
- fi
- done
+ if [[secureremotebackup|"$RSH_CMD" ]]; then
+ for i in `$RSH_CMD "cd $BACKUPDIR; find ${VOLUMENAME}_* -mtime +$MAXBACKUPAGE -exec echo {} \;"`; do
+ if [[secureremotebackup|$i == $UPTO ]]; then
+ SEEN=1
+ fi
+ if [[secureremotebackup|$SEEN == '0' ]]; then
+ message "Deleted old backup $i"
+ $RSH_CMD "cd $BACKUPDIR; $RM -f $i"
+ fi
+ done
+ else
+ cd $BACKUPDIR
+ for i in `find ${VOLUMENAME}_* -mtime +$MAXBACKUPAGE -exec echo {} \;`; do
+ if [[secureremotebackup|$i == $UPTO ]]; then
+ SEEN=1
+ fi
+ if [[secureremotebackup|$SEEN == '0' ]]; then
+ message "Deleted old backup $i"
+ $RM -f $i
+ fi
+ done
+ fi
fi
}
@@ -622,21 +634,34 @@
$RM -f $TOBLISTS/$2.incremental*
gzip -c "$TMPLIST1" > "$TOBLISTS/$2.differential.gz"
- if [[secureremotebackup|-d "$BACKUPDIR" ]]; then
+ if $RSH_CMD [[secureremotebackup|-d "$BACKUPDIR" ]]; then
UPTO=`basename $BACKUPDEV`
SEEN='0'
- cd $BACKUPDIR
- $RM -f ${VOLUMENAME}_*inc*
- for i in `find ${VOLUMENAME}_*diff* -mtime +$MAXBACKUPAGE -exec echo {} \;`; do
- if [[secureremotebackup|$i == $UPTO ]]; then
- SEEN=1
- fi
- if [[secureremotebackup|$SEEN == '0' ]]; then
- message "Deleting old backup $i"
- $RM -f $i
- fi
- done
+ if [[secureremotebackup|"$RSH_CMD" ]]; then
+ $RSH_CMD "cd $BACKUPDIR; $RM -f ${VOLUMENAME}_*inc*"
+ for i in `$RSH_CMD "cd $BACKUPDIR; find ${VOLUMENAME}_*diff* -mtime +$MAXBACKUPAGE -exec echo {} \;"`; do
+ if [[secureremotebackup|$i == $UPTO ]]; then
+ SEEN=1
+ fi
+ if [[secureremotebackup|$SEEN == '0' ]]; then
+ message "Deleting old backup $i"
+ $RSH_CMD "cd $BACKUPDIR; $RM -f $i"
+ fi
+ done
+ else
+ cd $BACKUPDIR
+ $RM -f ${VOLUMENAME}_*inc*
+ for i in `find ${VOLUMENAME}_*diff* -mtime +$MAXBACKUPAGE -exec echo {} \;`; do
+ if [[secureremotebackup|$i == $UPTO ]]; then
+ SEEN=1
+ fi
+ if [[secureremotebackup|$SEEN == '0' ]]; then
+ message "Deleting old backup $i"
+ $RM -f $i
+ fi
+ done
+ fi
fi
}
@@ -681,9 +706,9 @@
verbose ()
{
- if [[secureremotebackup|-d "$BACKUPDIR" ]]; then
+ if $RSH_CMD [[secureremotebackup|-d "$BACKUPDIR" ]]; then
message "Generating report of $BACKUPDIR."
- for i in `$LS ${BACKUPDIR}/*`; do
+ for i in `$RSH_CMD $LS ${BACKUPDIR}/*`; do
BACKUPDEV=$i
eval "$LISTCMD"
done
@@ -707,8 +732,8 @@
FILESPEC=$3
message "Restoring volume $VOLUMENAME files matching $FILESPEC into $startdir."
- if [[secureremotebackup|-d "$BACKUPDIR" ]]; then
- for i in `$LS ${BACKUPDIR}/${VOLUMENAME}_*gz`; do
+ if $RSH_CMD [[secureremotebackup|-d "$BACKUPDIR" ]]; then
+ for i in `$RSH_CMD $LS ${BACKUPDIR}/${VOLUMENAME}_*gz`; do
BACKUPDEV=$i
eval "$RESTORECMD" && message "restored data from $i"
done
===== tob.rc =====
This options have to be changed in the tob configuration file (tob.rc)
- set some variables to make customising the
- backup commands a bit easier
THROTTLE_CMD="cstream -t 1000000 -v2"
AESPIPE_PWFILE='/etc/tob/aespipe.pwd'
AESPIPE_OPTIONS="-p3 -eAES256 -C128"
- RSH_CMD: if set tob will use this command
- to manage backup files on a remote host
- (see tob patch)
RSH_CMD="ssh user@backup-server.org"
- DELETIONSCOMMAND needs RSH_CMD, too so set it after RSH_CMD is set...
DELETIONSCOMMAND='$RSH_CMD "cat >$BACKUPDIR/${VOLUMENAME}_${DATE}_${TYPE}_deletions"'
- special backup commands for remote, encrypted, throttled backup
BACKUPCMD='afio $BLOCKSIZE $BUFFERBLK $GZIPFACTOR -o - < $FILELIST \
| $THROTTLE_CMD \
| aespipe $AESPIPE_OPTIONS 3< $AESPIPE_PWFILE \
| su backup -c "$RSH_CMD \"cat > $BACKUPDEV\""'
BACKUPCMDTOSTDOUT='afio $BLOCKSIZE $BUFFERBLK $GZIPFACTOR -o - < $FILELIST'
LISTCMD='su backup -c "$RSH_CMD \"cat $BACKUPDEV\"" \
| $THROTTLE_CMD \
| aespipe -d $AESPIPE_OPTIONS 3< $AESPIPE_PWFILE \
| afio $BLOCKSIZE $BUFFERBLK -t -'
RESTORECMD='su backup -c "$RSH_CMD \"cat $BACKUPDEV\"" \
| $THROTTLE_CMD \
| aespipe -d $AESPIPE_OPTIONS 3< $AESPIPE_PWFILE \
| afio $BLOCKSIZE $BUFFERBLK $GZIPFACTOR -viny"$FILESPEC" - '
VERIFYCMD='su backup -c "$RSH_CMD \"cat $BACKUPDEV\"" \
| $THROTTLE_CMD \
| aespipe -d $AESPIPE_OPTIONS 3< $AESPIPE_PWFILE \
| afio $BLOCKSIZE $BUFFERBLK $GZIPFACTOR -r -'
===== aespipe.pwd =====
Creating a secure password for aespipe:
head -c 1024 /dev/urandom | uuencode -m - | head -n 24 | tail -n 23 > /etc/tob/aespipe.pwd
chmod 0600 /etc/tob/aespipe.pwd
This generates a password from 1024 random bytes and writes it into the file /etc/tob/aespipe.pwd
--[[user:mschiff|mschiff]] 13:26, 31 Mar 2005 (CEST)