#!/bin/bash # needs to be bash for the redirection to work. # # geode # http://www.operationaldynamics.com/reference/software/scripts/#geode # # Copyright (c) 2004 Andrew Cowie, Operational Dynamics Consulting Pty Ltd # Redistributable under the terms of the GNU GPL, version 2. # # # A tool to encrypt and archive critical files to a USB key, remote # server, or other offsite / offline destination. # # If you want to use it, you probably need to change the following: # # - the user name in the tmpfiles # - the recpient in the gpg encrpytion commands # - the exclude settings # - the server hostname and username if using for remote backups, # and the mount commands used, if using to transfer to usbkey. # - the prefix used to name the archive directory # - the actual directories shlurped up in the various targets # # Obviously this is a hack that went way overboard. It is fairly robust, but # still, so much should be refactored (ie, all the configuration above) and it # should really be made into an integratted application. I'm working on a # better version using the java-gnome bindings, but this will stand as a good # example of how to play with tar output and how to get good progress bars # from zenity. # # AfC # 29 Sep 04 # umask 077 DATE=`date +%Y%m%d` CRITICAL_TARBALL=/tmp/andrew-critical-$DATE.tar.bz2 CRYPTO_TARBALL=/tmp/andrew-crypto-$DATE.tar.bz2 WORKSPACE_TARBALL=/tmp/andrew-workspace-$DATE.tar.bz2 cleanup () { echo "Cleaning up." rm -f $TMP_FILES rm -f $COPY_FILES } usage () { echo "USAGE:" echo " geode ... " echo echo "targets are one or more of:" echo " critical" echo " crypto" echo " workspace" echo echo "where is one of:" echo " server" echo " usbkey" echo " taronly" echo exit 1 } tarball () { TITLE=$1 TARBALL=$2 shift 2 export EXCLUDES="--exclude=.svn --exclude=pubring.gpg~" export NUM=`tar cvf /dev/null $EXCLUDES $* | wc -l` tar cjvf $TARBALL $EXCLUDES $* \ | perl -e ' $| = 1; $total = $ENV{"NUM"}; $i = 0; # there must be an easier way to do that $LENGTH = 60; $SPACER = ""; for ($k = 0; $k < $LENGTH ; $k++) { $SPACER .= " "; } print "#$SPACER.\n"; while (<>) { $i++; $p = int($i/$total*100); print "$p\n"; print "#"; $BLAH=substr($_,-$LENGTH); if (length $BLAH < length $_) { print "..."; } print "$BLAH\n"; if ($p >= 100) { break; } } print "#done\n"; print "100\n"; exit; ' \ | zenity --progress --auto-close --width=500 --title="Geode $TITLE" 2>/dev/null if [ $? -ne 0 ] ; then echo "Cancelled" exit fi } read_passphrase () { passphrase="" while [ "$passphrase" == "" ] ; do # supposedly this goes to stderr, but backticks seem to catch it passphrase=`zenity --entry --title="Geode" --text="Enter GPG passphrase for signing tarballs" --hide-text` if [ $? -ne 0 ] ; then echo "Cancelled" exit 1 fi echo hellp | gpg -s --passphrase-fd 3 3< <(echo "$passphrase") >/dev/null if [ $? -ne 0 ] ; then passphrase="" fi done } read_keyphrase () { symmetric_keyphrase="" key1="one" key2="something not one" while [ "$symmetric_keyphrase" == "" ] ; do key1=`zenity --entry --title="Geode" --text="Enter key for symmetric cipher" --hide-text` if [ $? -ne 0 ] ; then echo "Cancelled" exit 1 fi key2=`zenity --entry --title="Geode" --text="Please re-enter key to confirm" --hide-text` if [ $? -ne 0 ] ; then echo "Cancelled" exit 1 fi if [ "$key1" == "$key2" ] ; then symmetric_keyphrase="$key1" else zenity --warning --text="Passphrase didn't match. Try again." --title="Geode" fi done } # # main program # trap "echo '[signal caught]' ; cleanup ; exit" 0 1 2 3 11 15 cd CMD="" TARGETS="" for i in "$@" do case "$i" in # # potential destinations # server|\ usbkey|\ taronly) DESTINATION="$i" # and stop processing break ;; # # potential targets # cri*) #critcal TARGETS="$TARGETS critical" ;; cry*) #crypto TARGETS="$TARGETS crypto" ;; wor*) #workspace TARGETS="$TARGETS workspace" ;; *) echo "ERROR: Don't know what $i is. Abort." cleanup exit 2 ;; esac done # # check and make sure we got out of the arg loop in good form. # if [ -z "$TARGETS" ] ; then echo "ERROR: Need to specify at least one target to archive" usage fi if [ -z "$DESTINATION" ] ; then echo "ERROR: Need to specify where to copy to" usage fi # # check physical preconditions (network, key attached) # case "$DESTINATION" in server) ping -c 1 -w 5 server.example.com if [ $? -ne 0 ] ; then echo "PROBLEM: can't reach destination server. Is networking enabled?" exit 1 fi ;; usbkey) if [ -z "`mount | grep /mnt/usbkey`" ] ; then # shrug, try mounting the damn thing mount /mnt/usbkey if [ -z "`mount | grep /mnt/usbkey`" ] ; then echo "PROBLEM: usb key not mounted (!)" exit 1 fi fi ;; taronly) ;; *) echo "INTERNAL ERROR: unknown destination $DESTINATION, so can't check it's preconditions." ;; esac # # get secret key passphrase # read_passphrase for i in $TARGETS do if [ "$i" == "crypto" ] ; then read_keyphrase fi done # # process the targets, doing the tarballs and encryption # COPY_FILES="" TMP_FILES="" for i in $TARGETS do case "$i" in critical) tarball "critical files" $CRITICAL_TARBALL Documents Sites gpg -se -r andrew@example.com \ --passphrase-fd 3 $CRITICAL_TARBALL \ 3< <(echo "$passphrase") if [ $? -ne 0 ] ; then echo "ERROR: Encryption step failed." exit 1 fi TMP_FILES="$TMP_FILES $CRITICAL_TARBALL" COPY_FILES="$COPY_FILES $CRITICAL_TARBALL.gpg" ;; workspace) tarball "eclipse workspace" $WORKSPACE_TARBALL workspace gpg -se -r andrew@example.com \ --passphrase-fd 3 $WORKSPACE_TARBALL \ 3< <(echo "$passphrase") if [ $? -ne 0 ] ; then echo "ERROR: Encryption step failed." exit 1 fi TMP_FILES="$TMP_FILES $WORKSPACE_TARBALL" COPY_FILES="$COPY_FILES $WORKSPACE_TARBALL.gpg" ;; crypto) tarball "cryptographic data" $CRYPTO_TARBALL .ssh2 .gnupg gpg -c --force-mdc --yes --passphrase-fd 3 $CRYPTO_TARBALL \ 3< <(echo $symmetric_keyphrase) if [ $? -ne 0 ] ; then echo "ERROR: Encryption step failed." exit 1 fi gpg --detach-sign --armour --yes --passphrase-fd 3 \ $CRYPTO_TARBALL.gpg \ 3< <(echo "$passphrase") if [ $? -ne 0 ] ; then echo "ERROR: Signing step failed." exit 1 fi TMP_FILES="$TMP_FILES $CRYPTO_TARBALL" COPY_FILES="$COPY_FILES $CRYPTO_TARBALL.gpg $CRYPTO_TARBALL.gpg.asc" ;; *) echo "INTERNAL ERROR: Don't know what target $i is. Abort." cleanup exit 2 ;; esac done # # since there's no output we can key off of in the maner we do for the tar # balls, we just guesstimate the progress through the final stages. this is # just a silly utility function to increment the counter used for the progress # bar. # INDEX=0 step () { INDEX=`expr $INDEX + 10` echo $INDEX } case "$DESTINATION" in server) scp -p $COPY_FILES afcowie@server.example.com:/usr/home/afcowie/archive/operationaldynamics ;; usbkey) ( PREFIX=AndrewCowie_Geode_mylaptophostname cd /mnt/usbkey step echo "#Temporary backup original data" for i in ${PREFIX}_* do step mv ${i} /tmp/${i}-$$ done mkdir ${PREFIX}_${DATE} step echo "#Copying files to USB key" for i in $COPY_FILES do step cp -pv $i ${PREFIX}_${DATE} done echo "90" echo "#Cleaning up" if [ $? -eq 0 ] ; then rm -r /tmp/${PREFIX}_* fi cd - echo "99" sleep 1 echo "100" ) | zenity --progress --title="Geode" --auto-close ;; taronly) ls -l $COPY_FILES $TMP_FILES ;; *) echo "INTERNAL ERROR: unknown destination $DESTINATION, Abort." ;; esac # will be trapped, causing cleanup exit 0