123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- #!/bin/sh
- ## CryptShotR
- ## Open and mount a LUKs volume, locally or remotely, before performing a
- # backup with rsnapshot.
- #
- # Author: demure demeanor
- # Repository: https://notabug.org/demure/scripts
- # Website: http://demu.red/
- # Blog Post: http://demu.red/
- ## This is based on cryptshot by:
- # Author: Pig Monkey (pm@pig-monkey.com)
- # Website: https://github.com/pigmonkey/backups
- ## Modified to support a GPG encrypted LUKs key file.
- # This can backup to the LUKs container either locally, or over the network
- # to server where the LUKs container is connected. Supports a flag to
- # suppress non-configuration related errors. The script can optionally
- # support ssh-agent. GPG decryption relies on the gpg-agnet caching, which
- # I frequently refresh for pulling emails in mutt.
- #
- # It will check if the volume with configured UUID exists locally, and if
- # not will check remotely for the volume. If the volume is found it will be
- # treated as a LUKs volume and decrypted with the given GPG encrypted LUKs
- # key file, and then mounted. The script then runs rsnapshot. After the
- # backup is complete the volume is unmounted and closed. Can optionally
- # delete the mount point for cleanup.
- #
- # This script requires a LUKs+dmcrypt container, as well as a GPG encrypted
- # LUKs key file. The container allows for secure storage of backups.
- # The encrypted key file protects the key from compromise.
- #
- # The script should be called with your configured rsnapshot intervals.
- # Optionally, '-q' or '-quiet' can be used as the first augment, to silence
- # non-configuration errors.
- ### Conf ### {{{
- ## Define the UUID of the backup volume.
- UUID="9c6a7bec-d6ff-484c-87e0-25b9f038aed1"
- ## Define the location of the LUKS key file.
- KEYFILE="$HOME/backups.gpg"
- ## Define gpg key, used to decrypt LUKS key file.
- GPGPRINT="FA88A5CE"
- ## Define the mount point for the backup volume.
- ## This will be created if it does not already exist.
- MOUNTPOINT="/mnt/$UUID"
- ## Any non-zero value here will caused the mount point to be deleted after the
- ## volume is unmounted.
- REMOVEMOUNT=1
- ## Define the location of rsnapshot.
- RSNAPSHOT="/usr/bin/rsnapshot"
- ### End Conf ### }}}
- ### Remote Conf ### {{{
- ## Enable Remote backups
- RE_ON=1
- ## Enable ssh agent
- # This is assuming that your key is already sourced like:
- # https://notabug.org/demure/dotfiles/src/master/subbash/sshagent
- RE_AGENT=1
- ## Define home routers MAC addr.
- RE_HOME="10:FE:ED:E9:63:04"
- ## Define remote address, for testing.
- RE_HOST="tiny-server-of-doom.local"
- ## Define remote user
- RE_USER="backups"
- ### End Remote Conf ### }}}
- ### Startup Tests ### {{{
- ## Exit if no volume is specified.
- if [ "$UUID" = "" ]; then
- echo 'No volume specified.'
- exit 78
- fi
- ## Exit if no key file is specified.
- if [ "$KEYFILE" = "" ]; then
- echo 'No key file specified.'
- exit 78
- fi
- ## Exit if no gpg finger print is specified.
- if [ "$GPGPRINT" = "" ]; then
- echo 'No GPG finger print specified.'
- exit 78
- fi
- ## Exit if no mount point is specified.
- if [ "$MOUNTPOINT" = "" ]; then
- echo 'No mount point specified.'
- exit 78
- fi
- ## Exit if no interval was specified.
- ## Tests about this line should not be effected by Quiet flag
- ## Why? Well, it's enacted here, and missing configuration is BAD.
- if [ "$1" = "" ]; then
- echo "No interval specified."
- exit 64
- elif [ "$1" = "-q" ] || [ "$1" = "--quiet" ]; then
- ## set quiet mode code here.
- ## NOTE: Make quiet mode not effect missing settings or interval.
- QUIET=1
- if [ "$2" = "" ]; then
- echo "No interval specified."
- exit 64
- else
- interval=$2
- fi
- else
- interval=$1
- QUIET=0
- fi
- ### End Startup Tests ### }}}
- ### Remote Startup Tests ### {{{
- if [ "$RE_ON" = "1" ]; then
- if [ "$RE_HOME" = "" ]; then
- echo "No local network router MAC specified."
- exit 73
- fi
- if [ "$RE_HOST" = "" ]; then
- echo "No remote host specified."
- exit 73
- fi
- if [ "$RE_USER" = "" ]; then
- echo "No remote user specified."
- exit 73
- fi
- fi
- ### End Remote Startup Tests ### }}}
- ### Prep Work ### {{{
- ## Build the reference to the volume.
- volume="/dev/disk/by-uuid/$UUID"
- ## Create a unique name for the LUKS mapping.
- name="crypt-$UUID"
- ## If the mount point does not exist, create it.
- if [ ! -d "$MOUNTPOINT" ]; then
- mkdir $MOUNTPOINT
- # Exit if the mount point was not created.
- if [ $? -ne 0 ]; then
- echo "Failed to create mount point."
- exit 73
- fi
- fi
- ### GPG Test ### {{{
- ## Check that GPG key is cached in gpg-agent
- ## First see if SUDO_UID exists.
- # This is needed as cryptshotr-cron -> cryptshotr results in using SUDO_UID
- # but cryptshot run manually has UID
- # http://demu.red/blog/2016/08/how-to-check-if-your-gpg-key-is-in-cache-part-2/
- if [ -z "${SUDO_UID}" ]; then
- gpg_socket="/run/user/${UID}/gnupg/S.gpg-agent"
- else
- gpg_socket="/run/user/${SUDO_UID}/gnupg/S.gpg-agent"
- fi
- ## Test if cached
- if [ -e ${gpg_socket} ]; then
- gpg_test=$(gpg-connect-agent -S ${gpg_socket} 'keyinfo --list' /bye 2>/dev/null | awk 'BEGIN{CACHED=0} /^S/ {if($7==1){CACHED=1}} END{print CACHED}')
- else
- gpg_test=$(gpg-connect-agent 'keyinfo --list' /bye 2>/dev/null | awk 'BEGIN{CACHED=0} /^S/ {if($7==1){CACHED=1}} END{print CACHED}')
- fi
- ## Finally, the graceful failure.
- if [ $gpg_test = "0" ]; then
- if [ "$QUIET" = 0 ]; then
- echo 'GPG key is not cached.'
- fi
- exit 78
- fi
- ### End GPG Test ### }}}
- ## Load ssh-agent, if enabled.
- if [ "$RE_AGENT" = 1 ]; then
- ssh_env="$HOME/.ssh/environment"
- if [ -f ${ssh_env} ]; then
- . "${ssh_env}" >/dev/null
- ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ >/dev/null || {
- echo "PID bad?";
- }
- else
- echo "ENV file bad?"
- fi
- fi
- ## Set the default exit code to 0.
- exitcode=0
- ### End Prep Work ### }}}
- ## Continue if the volume exists.
- if [ -e $volume ]; then
- ### Local Backup ### {{{
- ## Attempt to open the LUKS volume.
- gpg2 --no-permission-warning --no-tty -qd $KEYFILE | cryptsetup luksOpen --key-file=- $volume $name
- ## If the volume was decrypted, mount it.
- if [ $? -eq 0 ]; then
- mount /dev/mapper/$name $MOUNTPOINT
- ## If the volume was mounted, run the backup.
- if [ $? -eq 0 ]; then
- ## Run rsnapshot
- $RSNAPSHOT $interval
- ## Unmount the volume
- umount $MOUNTPOINT
- ## If the volume was unmounted and the user has requested that the
- ## mount point be removed, remove it.
- if [ $? -eq 0 -a $REMOVEMOUNT -ne 0 ]; then
- rmdir $MOUNTPOINT
- fi
- else
- exitcode=$?
- if [ $"$QUIET" = 0 ]; then
- echo "Failed to mount $volume at $MOUNTPOINT."
- fi
- fi
- ## Close the LUKS volume.
- cryptsetup luksClose $name
- else
- exitcode=$?
- if [ $"$QUIET" = 0 ]; then
- echo "Failed to open $volume with key $KEYFILE."
- fi
- fi
- ### End Local Backup ### }}}
- else
- ### Remote Backup ### {{{
- if [ $RE_ON -ne 0 ]; then
- re_addr="${RE_USER}@${RE_HOST}"
- ## Get MAC of router
- check_mac="$(iwgetid --ap -r)"
- if [ "$RE_HOME" = "$check_mac" ]; then
- ## Check that host is up
- ping -c 3 $RE_HOST 1>/dev/null
- if [ $? = 0 ]; then
- ## Check if drive connected
- check_volume=$(ssh $re_addr "test -e $volume && echo 1 || echo 0" 2>/dev/null)
- if [ $check_volume = 1 ]; then
- ## Make MOUNTPOINT if not present
- ssh $re_addr "test -e $MOUNTPOINT || sudo mkdir $MOUNTPOINT"
- if [ $? = 0 ]; then
- ## Open LUKs container
- gpg2 --no-permission-warning --no-tty -qd $KEYFILE | ssh $re_addr "sudo cryptsetup luksOpen --key-file=- $volume $name"
- if [ $? = 0 ]; then
- ## Mount LUKs container
- ssh $re_addr "sudo mount /dev/mapper/$name $MOUNTPOINT"
- if [ $? = 0 ]; then
- ## Run rsnapshot
- ssh $re_addr "sudo $RSNAPSHOT $interval"
- ## Unmount LUKs container
- ssh $re_addr "sudo umount $MOUNTPOINT"
- ## If the volume was unmounted and the user has requested that the
- ## mount point be removed, remove it.
- if [ $? -eq 0 -a $REMOVEMOUNT -ne 0 ]; then
- ssh $re_addr "sudo rmdir $MOUNTPOINT"
- rmdir $MOUNTPOINT
- fi
- else
- exitcode=$?
- if [ $"$QUIET" = 0 ]; then
- echo "Failed to mount remote $volume at $MOUNTPOINT."
- fi
- fi
- else
- exitcode=$?
- if [ $"$QUIET" = 0 ]; then
- echo "Failed to open remote $volume with key $KEYFILE."
- fi
- fi
- ## Close LUKs Container
- ssh $re_addr "sudo cryptsetup luksClose $name"
- else
- exitcode=66
- if [ $"$QUIET" = 0 ]; then
- echo "Could not make remote $MOUNTPOINT."
- fi
- fi
- else
- exitcode=66
- if [ $"$QUIET" = 0 ]; then
- echo "Remote volume $UUID not found."
- fi
- fi
- else
- exitcode=66
- if [ $"$QUIET" = 0 ]; then
- echo "Host not found."
- fi
- fi
- else
- exitcode=66
- if [ $"$QUIET" = 0 ]; then
- echo "Not on home network."
- fi
- fi
- else
- exitcode=33
- if [ $"$QUIET" = 0 ]; then
- echo "Volume $UUID not found."
- fi
- fi
- ### End Remote Backup ### }}}
- fi
- exit $exitcode
|