scripts/shell/restic_backup.sh

156 lines
4.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# shellcheck disable=SC2317,SC2086,SC2181,SC2129,SC2002,SC2088
#
# restic backup using rclone as the transport
# - designed to be cron/timer driven, uses repo key on disk (security warning)
#
# (a) set up and test the rclone remote
# (b) run the restic init first-time repo prep
# (c) prepare the encryption key and excludes files, chmod 0600
# (d) uses ~/.mailrc to send backup log/report via `mail` cmd
#
# SPDX-License-Identifier: MIT
##
## TEMPLATE - NEEDS ALL SYSTEM SPECIFIC VARIABLES CONFIGURED
##
# restic exit codes:
# Exit status is 0 if the command was successful
# Exit status is 1 if there was a fatal error
# (no snapshot created).
# Exit status is 3 if some source data could not be read
# (incomplete snapshot created).
# restic init:
# restic -r rclone:remote: init
# restic excludes format examples:
# Downloads/
# VirtualBox**
# .config/**Cache*
# .config/*/sessions
# .config/restic/xyzzy.txt
# .thunderbird/**/*.msf
# .xsession-errors*
# cloud provider expired SSL cert hack
RCLONE_NO_CHECK_CERTIFICATE=true
export RCLONE_NO_CHECK_CERTIFICATE
# email and log settings
MAILTO="user@example.com"
LOGDIR="/home/user/.logs"
TIMESTAMP=$(date +%Y-%m-%d_%H%M)
MAILSUB="restic backup: ${TIMESTAMP}"
LOGFILE="${LOGDIR}/restic_${TIMESTAMP}.log"
# have `mail` add timestamps to dead.letter
DEAD="~/dead.letter-${TIMESTAMP}"
export DEAD
# restic settings
REPO="rclone:remote:"
RSRC="/home/user"
# encryption key, plaintext, chmod 0600
RKEY="/home/user/.config/restic/xyzzy.txt"
# excludes, chmod 0600
EXCL="/home/user/.config/restic/excludes.txt"
# snapshots to keep
KEEP=10
# trap our signals
function error_exit {
echo "Trapped a kill signal, exiting."
exit 99
}
trap error_exit SIGHUP SIGINT SIGTERM
# this may be needed for non-packaged binaries
# (e.g. using newer restic on older LTS distro)
function check_restic() {
echo "Checking restic..."
# get installed version
_LOCAL=$(restic version 2>/dev/null | awk '{print $2}'; exit ${PIPESTATUS[0]})
if [[ $? -ne 0 ]]; then
echo "Unable to get installed restic version" >> "${LOGFILE}"
exit 1
fi
# get latest version, strip leading "v" (v1.55.1 -> 1.55.1)
_REMOTE=$(curl -s "https://api.github.com/repos/restic/restic/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')
_REMOTE=${_REMOTE#v}
# bash doesn't see versions as numbers, but as strings
if [[ "${_LOCAL}" != "${_REMOTE}" ]]; then
echo "Upgrade restic - installed ${_LOCAL}, latest ${_REMOTE}" >> "${LOGFILE}"
else
echo "Installed restic is the latest - ${_LOCAL}" >> "${LOGFILE}"
fi
}
# uncomment as needed
#check_restic
# begin timestamp
echo "== ${TIMESTAMP} ==" >> "${LOGFILE}"
# remove stale locks
restic -r "${REPO}" \
--password-file="${RKEY}" \
unlock >> "${LOGFILE}"
_EC=$?
# create a new snapshot
if [[ $_EC -eq 0 ]]; then
restic --quiet -r "${REPO}" \
backup --no-scan \
--password-file="${RKEY}" \
--exclude-file="${EXCL}" \
"${RSRC}" >> "${LOGFILE}"
_EC=$?
else
echo "Non-zero exit from restic unlock: $_EC" >> "${LOGFILE}"
fi
# prune older snapshots
if [[ $_EC -eq 0 ]]; then
restic --quiet -r "${REPO}" \
forget --prune --keep-last ${KEEP} \
--password-file="${RKEY}" >> "${LOGFILE}"
_EC=$?
if [[ $_EC -ne 0 ]]; then
echo "Non-zero exit from restic forget: $_EC" >> "${LOGFILE}"
fi
else
echo "Non-zero exit from restic backup: $_EC" >> "${LOGFILE}"
fi
# check reposotory health
restic --cleanup-cache -r "${REPO}" \
check --password-file="${RKEY}" >> "${LOGFILE}"
_EC=$?
if [[ $_EC -ne 0 ]]; then
echo "Non-zero exit from restic check: $_EC" >> "${LOGFILE}"
else
# list available snapshots
restic -r "${REPO}" \
snapshots -c --password-file="${RKEY}" >> "${LOGFILE}"
# generate stats about repo
restic -r "${REPO}" \
stats --mode=files-by-contents --password-file="${RKEY}" >> "${LOGFILE}"
restic -r "${REPO}" \
stats --mode=raw-data --password-file="${RKEY}" >> "${LOGFILE}"
fi
# end timestamp
_NOW=$(date +%Y-%m-%d_%H%M)
echo "== ${_NOW} ==" >> "${LOGFILE}"
# ~/.mailrc needs to be configured
cat "${LOGFILE}" | mail -A mailprovider -s "${MAILSUB}" "${MAILTO}"
# remove older logfiles
find "${LOGDIR}" -type f -name restic\* -mtime +30 -delete
exit 0