Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

My sync-offsite shell script for my new offsite backup server

As part of the buildout of my new offsite backup server, the main goal is to back up the contents of my network to an offsite location. This shell script, sync-offsite.sh and its related files are the main components.

#!/bin/sh
# File: sync-offsite.sh
# Locations:
#    dns2:/etc/installed/
#    /mnt/public/Support/Systems/dns2/
# Author: bgstack15
# Startdate: 2021-11-19
# Title: Script that Syncs Data to Offsite Server
# Purpose:
# History:
# Usage:
# Reference:
# Improve:
#    the whole redirection spaghetti is very dangerous and probably should be rewritten/removed.
# Dependencies:
#    plecho from bgscripts-core
#    freeipa user, hbac rules, and sudo rules
#    /etc/installed/sync-offsite.excludes
# Documentation:
#    server2:/etc/installed/server2a.md
#set -e -u
RUNUID="$( < /dev/urandom tr -dc 'A-Z0-9' | head -c6 )"
LOGFILE=/var/log/sync-offsite/sync.$( date "+%F" ).log
# FUNCTIONS
mainbup() {
   ___target="${1}" # either "server2" or its vpn IP, hardcoded in the script below.
   sudo rsync -avz -e "ssh -i /home/syncuser/.ssh/id_rsa" --rsync-path='sudo /usr/bin/rsync' --exclude-from="${EXCLUDE_FILE:-/etc/installed/sync-offsite.excludes}" \
      /mnt/serverx/shares syncuser@"${___target}":/var/server2/
}
validate_mounts() {
   thismount="$( mount | awk '/jon/{print $1,$3}' )"
   if test "${thismount}" != "serverx:/volume1/sword /mnt/serverx" ;
   then
      echo "FATAL: Mount point for serverx not found. Aborted." 1>&2
      exit 1
   fi
}
validate_user() {
   if test "${USER}" != "syncuser" ;
   then
      echo "Switching user to syncuser"
      sudo su syncuser "$( readlink -f "${0}" )"
      exit 0
   else
      :
   fi
}
get_target_address() {
   for word in server2.ipa.internal.com server2.remote.internal.com 10.222.0.4 ;
   do
      echo "testing ${word}" 1>&2
      timeout 3 bash -c "echo > /dev/tcp/${word}/22"
      test $? -eq 0 && { echo "${word}" ; break ; }
   done
   echo "using remote identifier ${word}" 1>&2
}
validate_user # make sure this is running as syncuser
# MAIN LOOP
{
   echo "START sync-offsite" | plecho
   {
      {
         validate_mounts # make sure /mnt/serverx exists
         target="$( get_target_address )"
         mainbup "${target}"
      } 2>&1 1>&3 | unbuffer -p sed -r -e "s/^/STDERR: /" 1>&2
   } 3>&1
   echo "STOP sync-offsite" | plecho
} 2>&1 | unbuffer -p sed -r -e "s/^/${RUNUID} /;" | tee -a "${LOGFILE}"
yes | scp -p "${LOGFILE}" server1:/var/server1/shares/public/Support/Systems/dns2/"$( dirname "${LOGFILE}" )"

My shell script has some hardcoded paths, because I didn't find it worth it to move those to a config file. You will notice the --rsync-path trick to use sudo rsync.

I also use a rather simple hostname connectivity check. I just hardcode the DNS names I set up for the remote system. Obviously that's subject to change over time if I change backup systems.

I noticed in my logs that if I prepend every single line with the user and timestamp, it's a waste. So I added a unique run id, without using some crazy long guid. I only need to differentiate between different runs on the same day, because I use a new log file per day. So it's only really necessary for when I was building out this script.

Comments