2020-10-16 00:32:37 +08:00
|
|
|
#! /usr/bin/env bash
|
|
|
|
# vim: set filetype=sh ts=4 sw=4 sts=4 et:
|
|
|
|
set -e
|
|
|
|
umask 077
|
|
|
|
|
|
|
|
basedir=$(readlink -f "$(dirname "$0")"/../..)
|
|
|
|
# shellcheck source=lib/shell/functions.inc
|
|
|
|
. "$basedir"/lib/shell/functions.inc
|
|
|
|
|
2020-12-31 20:13:20 +08:00
|
|
|
trap "_err 'Unexpected termination!'" EXIT
|
|
|
|
|
|
|
|
exit_fail() {
|
|
|
|
trap - EXIT
|
|
|
|
exit 1
|
|
|
|
}
|
|
|
|
|
2020-10-16 00:32:37 +08:00
|
|
|
if command -v gpg1 >/dev/null 2>&1; then
|
|
|
|
gpgcmd="gpg1"
|
|
|
|
else
|
|
|
|
gpgcmd="gpg"
|
|
|
|
fi
|
|
|
|
|
|
|
|
config_list=''
|
|
|
|
if [ -f "$BASTION_ETC_DIR/osh-backup-acl-keys.conf" ]; then
|
|
|
|
config_list="$BASTION_ETC_DIR/osh-backup-acl-keys.conf"
|
|
|
|
fi
|
|
|
|
if [ -d "$BASTION_ETC_DIR/osh-backup-acl-keys.conf.d" ]; then
|
|
|
|
config_list="$config_list $(find "$BASTION_ETC_DIR/osh-backup-acl-keys.conf.d" -mindepth 1 -maxdepth 1 -type f -name "*.conf" | sort)"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ -z "$config_list" ]; then
|
|
|
|
_err "No configuration loaded, aborting"
|
2020-12-31 20:13:20 +08:00
|
|
|
exit_fail
|
2020-10-16 00:32:37 +08:00
|
|
|
fi
|
|
|
|
|
|
|
|
# load the config files only if they're owned by root:root and mode is o-rwx
|
|
|
|
for file in $config_list; do
|
|
|
|
if [ "$(find "$file" -uid 0 -gid 0 ! -perm /o+rwx | wc -l)" = 1 ] ; then
|
|
|
|
# shellcheck source=etc/bastion/osh-backup-acl-keys.conf.dist
|
|
|
|
. "$file"
|
|
|
|
else
|
|
|
|
_err "Configuration file not secure ($file), aborting."
|
2020-12-31 20:13:20 +08:00
|
|
|
exit_fail
|
2020-10-16 00:32:37 +08:00
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
|
|
# shellcheck disable=SC2153
|
|
|
|
if [ -n "$LOGFILE" ] ; then
|
|
|
|
exec &>> >(tee -a "$LOGFILE")
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ -z "$DESTDIR" ] ; then
|
|
|
|
_err "$0: Missing DESTDIR in configuration, aborting."
|
2020-12-31 20:13:20 +08:00
|
|
|
exit_fail
|
2020-10-16 00:32:37 +08:00
|
|
|
fi
|
|
|
|
|
|
|
|
if ! echo "$DAYSTOKEEP" | grep -Eq '^[0-9]+$' ; then
|
|
|
|
_err "$0: Invalid specified DAYSTOKEEP value ($DAYSTOKEEP), aborting."
|
2020-12-31 20:13:20 +08:00
|
|
|
exit_fail
|
2020-10-16 00:32:37 +08:00
|
|
|
fi
|
|
|
|
|
|
|
|
_log "Starting backup..."
|
|
|
|
|
|
|
|
[ -d "$DESTDIR" ] || mkdir -p "$DESTDIR"
|
|
|
|
|
|
|
|
tarfile="$DESTDIR/backup-$(date +'%Y-%m-%d').tar.gz"
|
|
|
|
_log "Creating $tarfile..."
|
|
|
|
supp_entries=""
|
2020-11-20 00:33:43 +08:00
|
|
|
for entry in /root/.gnupg /root/.ssh /var/otp
|
2020-10-16 00:32:37 +08:00
|
|
|
do
|
|
|
|
[ -e "$entry" ] && supp_entries="$supp_entries $entry"
|
|
|
|
done
|
2020-12-31 20:13:20 +08:00
|
|
|
|
|
|
|
maxtries=50
|
|
|
|
for try in $(seq 1 $maxtries)
|
|
|
|
do
|
|
|
|
# tar may output unimportant warnings to stderr, so we don't want to get noisy
|
|
|
|
# if it exits with 0: save its stderr in a tmpfile, and cat it to stderr only if it returns != 0
|
|
|
|
tarstderr=$(mktemp)
|
|
|
|
set +e
|
|
|
|
# SC2086: we don't want to quote $supp_entries, we want it expanded
|
|
|
|
# shellcheck disable=SC2086
|
|
|
|
tar czf "$tarfile" -p --xattrs --acls --one-file-system --numeric-owner \
|
|
|
|
--exclude=".encrypt" \
|
|
|
|
--exclude="ttyrec" \
|
|
|
|
--exclude="*.sqlite" \
|
|
|
|
--exclude="*.log" \
|
|
|
|
--exclude="*.ttyrec" \
|
|
|
|
--exclude="*.gpg" \
|
|
|
|
--exclude="*.gz" \
|
|
|
|
--exclude="*.zst" \
|
|
|
|
/home/ /etc/passwd /etc/group /etc/shadow /etc/gshadow /etc/bastion /etc/ssh $supp_entries 2>"$tarstderr"; ret=$?
|
|
|
|
set -e
|
|
|
|
if [ $ret -eq 0 ]; then
|
|
|
|
_log "File created"
|
|
|
|
rm -f "$tarstderr"
|
|
|
|
break
|
|
|
|
else
|
2021-02-23 18:05:50 +08:00
|
|
|
# special case: if a file changed/removed while we were reading it, tar fails, in that case: retry
|
|
|
|
if [ $ret -eq 1 ] && grep -q -e 'changed as we read it' -e 'removed before we read it' "$tarstderr"; then
|
2020-12-31 20:13:20 +08:00
|
|
|
_log "Transient tar failure (try $try):"
|
|
|
|
while read -r line; do
|
|
|
|
_log "tar: $line"
|
|
|
|
done < "$tarstderr"
|
|
|
|
rm -f "$tarstderr"
|
|
|
|
_log "Retrying after $try seconds..."
|
|
|
|
sleep "$try"
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
_err "Error while creating file (sysret=$ret)"
|
|
|
|
while read -r line; do
|
|
|
|
_err "tar: $line"
|
|
|
|
done < "$tarstderr"
|
|
|
|
rm -f "$tarstderr"
|
|
|
|
exit_fail
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
if [ "$try" = "$maxtries" ]; then
|
|
|
|
_err "Failed creating tar archive after $maxtries tries!"
|
|
|
|
exit_fail
|
2020-10-16 00:32:37 +08:00
|
|
|
fi
|
|
|
|
|
|
|
|
encryption_worked=0
|
|
|
|
if [ -n "$GPGKEYS" ] ; then
|
|
|
|
cmdline=""
|
|
|
|
for recipient in $GPGKEYS
|
|
|
|
do
|
|
|
|
cmdline="$cmdline -r $recipient"
|
|
|
|
done
|
|
|
|
# just in case, encrypt all .tar.gz files we find in $DESTDIR
|
|
|
|
while IFS= read -r -d '' file
|
|
|
|
do
|
|
|
|
_log "Encrypting $file..."
|
|
|
|
rm -f "$file.gpg" # if the gpg file already exists, remove it
|
|
|
|
# shellcheck disable=SC2086
|
|
|
|
if $gpgcmd --encrypt $cmdline "$file" ; then
|
|
|
|
encryption_worked=1
|
|
|
|
shred -u "$file" 2>/dev/null || rm -f "$file"
|
|
|
|
else
|
|
|
|
_err "Encryption failed"
|
|
|
|
fi
|
|
|
|
done < <(find "$DESTDIR/" -mindepth 1 -maxdepth 1 -type f -name 'backup-????-??-??.tar.gz' -print0)
|
|
|
|
else
|
|
|
|
_warn "$tarfile will not be encrypted! (no GPGKEYS specified)"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# push to remote if needed
|
|
|
|
if [ -n "$PUSH_REMOTE" ] && [ "$encryption_worked" = 1 ] && [ -r "$tarfile.gpg" ] ; then
|
|
|
|
_log "Pushing backup file ($tarfile.gpg) remotely..."
|
|
|
|
# shellcheck disable=SC2086
|
2020-12-31 20:13:20 +08:00
|
|
|
set +e
|
2020-10-16 00:32:37 +08:00
|
|
|
scp $PUSH_OPTIONS "$tarfile.gpg" "$PUSH_REMOTE"; ret=$?
|
2020-12-31 20:13:20 +08:00
|
|
|
set -e
|
2020-10-16 00:32:37 +08:00
|
|
|
if [ $ret -eq 0 ]; then
|
|
|
|
_log "Push done"
|
|
|
|
else
|
|
|
|
_err "Push failed (sysret=$ret)"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
# cleanup
|
|
|
|
_log "Cleaning up old backups..."
|
|
|
|
find "$DESTDIR/" -mindepth 1 -maxdepth 1 -type f -name 'backup-????-??-??.tar.gz' -mtime +"$DAYSTOKEEP" -delete
|
|
|
|
find "$DESTDIR/" -mindepth 1 -maxdepth 1 -type f -name 'backup-????-??-??.tar.gz.gpg' -mtime +"$DAYSTOKEEP" -delete
|
|
|
|
_log "Done"
|
2020-12-31 20:13:20 +08:00
|
|
|
trap - EXIT
|
2020-10-16 00:32:37 +08:00
|
|
|
exit 0
|