Run two-stages backups (#1)

1. Download to a local directory, archive/encrypt there
1. Push encrypted archive to remote folder

This helps when the backup destination is e.g. a NFS drive.

Also, introduce harder checks and fix some flaws.

Reviewed-on: #1
Co-authored-by: Max Mehl <mail@mehl.mx>
Co-committed-by: Max Mehl <mail@mehl.mx>
This commit was merged in pull request #1.
This commit is contained in:
2023-12-13 12:22:54 +01:00
committed by mxmehl
parent 6da9f1fabc
commit 8faea3fef1
4 changed files with 58 additions and 24 deletions

View File

@@ -63,6 +63,21 @@ shortcuts:
You can give multiple locations that shall be backed up. Just separate them by You can give multiple locations that shall be backed up. Just separate them by
`|` characters. See the example file for more. `|` characters. See the example file for more.
## Process
The script runs the following most important steps:
1. For each host in `hosts.csv`
1. Check SSH connection
1. Compose SSH host settings
1. For each backup source
1. Resolve special backup sources
1. Create backup destination
1. rsync source to destination
1. tar the destination
1. gpg-encrypt the destination
1. Delete older backups
1. Output completion info
## Manual run ## Manual run
You can run `ssh-checker.sh` and `uberspace-backup.sh` manually. Without any arguments given, both will check/backup all hosts. You can run `ssh-checker.sh` and `uberspace-backup.sh` manually. Without any arguments given, both will check/backup all hosts.

View File

@@ -4,10 +4,13 @@
# File with hosts and their backup source paths # File with hosts and their backup source paths
HOSTS="$CURDIR"/hosts.csv HOSTS="$CURDIR"/hosts.csv
# root dir where backups shall be saved to # Temporary download destination for backups
BACKUPDIR=/var/backups/uberspace TEMPDIR=/tmp/uberspace-backup
# GPG fingerprint of key used for encryption # root dir where backups shall be saved to
BACKUPDIR=/mnt/remotesrv/uberspace
# GPG fingerprint of key used for encryption
GPG=6775E8DDD8CEABCC83E38CEHE6334BCA29DF8192 GPG=6775E8DDD8CEABCC83E38CEHE6334BCA29DF8192
# Maximum number of backups that shall be retained (0 to disable automatic deletion) # Maximum number of backups that shall be retained (0 to disable automatic deletion)

View File

@@ -68,6 +68,4 @@ while read -r line; do
echo "[SUCCESS] SSH login possible for ${RHOST}." echo "[SUCCESS] SSH login possible for ${RHOST}."
fi fi
echo
done < "$HOSTS" done < "$HOSTS"

View File

@@ -9,6 +9,9 @@
# #
######################################################################## ########################################################################
# Fail fast on errors
set -Eeuo pipefail
# Set correct UTF-8 encoding (for FreeBSD jail) # Set correct UTF-8 encoding (for FreeBSD jail)
export LC_ALL=en_US.UTF-8 export LC_ALL=en_US.UTF-8
@@ -26,8 +29,6 @@ else
SSH_KEY=~/.ssh/id_rsa SSH_KEY=~/.ssh/id_rsa
fi fi
ARG1="$1"
# Get current date # Get current date
DATE=$(date +"%Y-%m-%d_%H-%M") DATE=$(date +"%Y-%m-%d_%H-%M")
LOG="$CURDIR"/backup.log LOG="$CURDIR"/backup.log
@@ -41,8 +42,8 @@ function pdate {
} }
function logecho { function logecho {
# Echo string and copy it to log while attaching the current date # Echo string and copy it to log while attaching the current date
echo "$(pdate) $@" echo "$(pdate) $*"
echo "$(pdate) $@" >> "$LOG" echo "$(pdate) $*" >> "$LOG"
} }
while read -r line; do while read -r line; do
@@ -51,8 +52,8 @@ while read -r line; do
RHOST=$(echo "$line" | cut -d";" -f1 | trim) RHOST=$(echo "$line" | cut -d";" -f1 | trim)
# Jump to next line if this line's host does not match host of ARG1 (if given) # Jump to next line if this line's host does not match host of first argument (if given)
if [[ "${ARG1}" != "" ]] && [[ "${ARG1}" != "${RHOST}" ]]; then if [[ "${1-}" != "" ]] && [[ "${1-}" != "${RHOST}" ]]; then
continue continue
fi fi
# Task ssh-checker.sh to check this host # Task ssh-checker.sh to check this host
@@ -79,28 +80,37 @@ while read -r line; do
logecho "${RHOST}: Starting backups" logecho "${RHOST}: Starting backups"
NORDIR=$(echo "$ALLRDIR" | grep -o "|" | wc -l) NORDIR=$(echo "$ALLRDIR" | grep -o "|" | wc -l || true)
NORDIR=$(($NORDIR + 1)) NORDIR=$(($NORDIR + 1))
# Loop through all backup sources
for ((i = 1; i <= $NORDIR; i++)); do for ((i = 1; i <= $NORDIR; i++)); do
RDIR=$(echo "$ALLRDIR" | cut -d"|" -f${i} | trim) RDIR=$(echo "$ALLRDIR" | cut -d"|" -f${i} | trim)
# Set a relative destination directory
if [ "${RDIR}" == "%virtual" ]; then if [ "${RDIR}" == "%virtual" ]; then
RDIR=/var/www/virtual/${RUSER} RDIR=/var/www/virtual/${RUSER}
DEST="$BACKUPDIR/$RHOST/$DATE/virtual" DEST_REL="$RHOST/$DATE/virtual"
elif [ "${RDIR}" == "%mysql" ]; then elif [ "${RDIR}" == "%mysql" ]; then
RDIR=mysql RDIR=mysql
DEST="$BACKUPDIR/$RHOST/$DATE/$(basename "${RDIR}")" DEST_REL="$RHOST/$DATE/$(basename "${RDIR}")"
elif [ "${RDIR}" == "%mails" ]; then elif [ "${RDIR}" == "%mails" ]; then
RDIR=/home/${RUSER}/users RDIR=/home/${RUSER}/users
DEST="$BACKUPDIR/$RHOST/$DATE/mails" DEST_REL="$RHOST/$DATE/mails"
elif [ "${RDIR}" == "%home" ]; then elif [ "${RDIR}" == "%home" ]; then
RDIR=/home/${RUSER} RDIR=/home/${RUSER}
DEST="$BACKUPDIR/$RHOST/$DATE/home" DEST_REL="$RHOST/$DATE/home"
else else
DEST="$BACKUPDIR/$RHOST/$DATE/$(basename "${RDIR}")" DEST_REL="$RHOST/$DATE/$(basename "${RDIR}")"
fi fi
# Define absolute temporary and final backup destination paths
# Example:
# DEST=/tmp/uberspace-backup/user@example.com/2019-01-01/virtual
# DEST_FINAL=/media/Uberspace/user@example.com/2019-01-01/
DEST="${TEMPDIR}/${DEST_REL}"
DEST_FINAL="$(dirname "${BACKUPDIR}/${DEST_REL}")"
# Set Source directory, and make exception for %mysql # Set Source directory, and make exception for %mysql
SOURCE="${RDIR}" SOURCE="${RDIR}"
if [ "${RDIR}" == "mysql" ]; then if [ "${RDIR}" == "mysql" ]; then
@@ -111,8 +121,9 @@ while read -r line; do
fi fi
fi fi
# Create backup destination if necessary # Create temporary and final backup destination if necessary
if [ ! -e "${DEST}" ]; then mkdir -p "${DEST}"; fi if [ ! -e "${DEST}" ]; then mkdir -p "${DEST}"; fi
if [ ! -e "${DEST_FINAL}" ]; then mkdir -p "${DEST_FINAL}"; fi
# RSYNC # RSYNC
logecho "${RHOST}: Downloading ${SOURCE} to ${DEST}" logecho "${RHOST}: Downloading ${SOURCE} to ${DEST}"
@@ -128,12 +139,19 @@ while read -r line; do
gpg --output "${DEST}".tar.gpg --encrypt --recipient ${GPG} "${DEST}".tar gpg --output "${DEST}".tar.gpg --encrypt --recipient ${GPG} "${DEST}".tar
rm "${DEST}".tar rm "${DEST}".tar
# Delete all old directories except the $MAXBAK most recent # Push encrypted backup to final backup destination
if [ $(ls -tp "${BACKUPDIR}"/"${RHOST}"/ | grep '/$' | wc -l | tr -d ' ') -gt $MAXBAK ]; then logecho "${RHOST}: Moving $(basename "${DEST}") to ${DEST_FINAL}"
logecho "${RHOST}: Removing older backups of $(basename "${DEST}")" cp "${DEST}".tar.gpg "${DEST_FINAL}/"
ls -tpd "${BACKUPDIR}"/"${RHOST}"/* | grep '/$' | tail -n +$(($MAXBAK + 1)) | xargs -0 | xargs rm -r -- rm "${DEST}".tar.gpg
fi
done done # End of loop through all backup sources
# Delete all old directories except the $MAXBAK most recent
if [ $(ls -tp "${BACKUPDIR}"/"${RHOST}"/ | grep '/$' | wc -l | tr -d ' ') -gt $MAXBAK ]; then
oldbackups=$(ls -tp "${BACKUPDIR}"/"${RHOST}"/ | grep '/$' | tail -n +$(($MAXBAK + 1)))
logecho "${RHOST}: Removing older backup directories: ${oldbackups}"
ls -tpd "${BACKUPDIR}"/"${RHOST}"/* | grep '/$' | tail -n +$(($MAXBAK + 1)) | xargs -0 | xargs rm -r --
fi
done < "$HOSTS" done < "$HOSTS"