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:
15
README.md
15
README.md
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user