#!/bin/bash
# backup.sh - a basic backup script with grandfather-father-son rotation:
# - The rotation will do a daily backup Sunday through Friday.
# - On Saturday a weekly backup is done giving you four weekly backups a month.
# - The monthly backup is done on the first of the month, rotating two monthly backups (odd/even)
#
# The script was inspired by the Ubuntu Server Guide https://help.ubuntu.com/lts/serverguide/backups.html
#
# backup.sh will archive using tar + gzip (multithreaded) and then encrypt using 7z (AES256, no compression)
# This is MUCH faster than it is to let 7z compress-&-encrypt in a single step - even at x=0.
# Plus 7z is unfit for backup duty on Linux (see the man-page) so the tarball stays. :)
#
# Add a cron job to run at 03:00 every day:
# $ crontab -e
# 0 3 * * * bash /home/myuser/scripts/backup.sh > /var/log/backup.log 2>&1
#
# //Ulf Benjaminsson, 2018-01
if [ $(id -u) != 0 ] ; then
echo "please run backup.sh as root" >&2
exit 1
fi
HOSTNAME=$(hostname -s)
TIMESTAMP_FORMAT='%Y-%m-%d %H:%M:%S'
ARCHIVE_PWD='secret' #used for 7z encryption. Will show up in process lists.
BACKUP_OWNER='myuser'
BACKUP_GROUP='mygroup'
MYSQL_DUMP_USER='BACKUPUSER'
MYSQL_DUMP_PWD="secret" #will NOT be printed in logs or process lists
DB_DESTINATION='/home/myuser/mysqldump'
FOLDERS_TO_BACKUP='/home /etc /var/www /var/log /usr/local /usr/share/phpmyadmin /root /boot /opt'
ARCHIVE_DESTINATION='/media/backup/rotatingbackup'
# log(): timestamped output
# Bash version in numbers like XYYYZZZ, where X is major version, YYY is minor, ZZZ is subminor.
printf -v BV '%d%03d%03d' ${BASH_VERSINFO[0]} ${BASH_VERSINFO[1]} ${BASH_VERSINFO[2]}
if [[ ${BV} -gt 4002000 ]]; then
log() { # Fast (builtin) but sec is highest precision for most implementations
printf "[%(${TIMESTAMP_FORMAT})T] %s\\n" '-1' "$*"
}
else
log() { # Slower, but support nanoseconds and legacy bash versions
echo "[$(date +"${TIMESTAMP_FORMAT}")] $*"
}
fi
# which week of the month is it (1-4)?
day_num=$(date +%-d)
if (( day_num <= 7 )); then
week_file="${HOSTNAME}-week1"
elif (( day_num <= 14 )); then
week_file="${HOSTNAME}-week2"
elif (( day_num <= 21 )); then
week_file="${HOSTNAME}-week3"
else # day_num < 32
week_file="${HOSTNAME}-week4"
fi
# is the month odd or even?
is_even_month=$(expr $(date +%m) % 2)
if [ "$is_even_month" -eq 0 ]; then
month_file="${HOSTNAME}-month2"
else
month_file="${HOSTNAME}-month1"
fi
# create archive filename.
day=$(date +%A)
if [ "$day_num" == 1 ]; then
archive_name=$month_file
elif [ "$day" != "Saturday" ]; then
archive_name="${HOSTNAME}-$day"
else
archive_name=$week_file
fi
#build the full paths before starting the real work
database_file="${DB_DESTINATION}/${archive_name}.sql.7z"
archive_file="${ARCHIVE_DESTINATION}/${archive_name}.tgz"
encrypted_archive_file="${archive_file}.7z"
log "Starting backup.sh"
if [ ! -d "${DB_DESTINATION}" ]
then
log "ERROR: ${DB_DESTINATION} does not exist. Skipping mysqldump."
else
log "Dumping MySQL to ${database_file}"
rm -f "${database_file}" # delete any pre-existing archive manually, because 7za is a PITA.
export MYSQL_PWD=$MYSQL_DUMP_PWD #read https://unix.stackexchange.com/a/369568 for the security implications
mysqldump -u ${MYSQL_DUMP_USER} --all-databases | 7za a -mhe=on -p"${ARCHIVE_PWD}" -mx=9 -si "${database_file}"
unset MYSQL_PWD
chown ${BACKUP_OWNER}:${BACKUP_GROUP} "${database_file}"
fi # mysqldump
if [ ! -d "${ARCHIVE_DESTINATION}" ]
then
log "ERROR: ${ARCHIVE_DESTINATION} does not exist. Skipping backup ${archive_name}"
log "backup.sh finished with errors!"
else
log "Backing up ${FOLDERS_TO_BACKUP} to ${archive_file}"
tar --use-compress-program=pigz -Pcf "${archive_file}" $FOLDERS_TO_BACKUP
# delete any pre-existing encrypted archive manually, because 7za is a PITA.
rm -f "${encrypted_archive_file}"
log "Encrypting ${archive_file} to ${encrypted_archive_file}"
7za a -mhe=on -p"${ARCHIVE_PWD}" -mx=0 "${encrypted_archive_file}" "${archive_file}"
#make sure the 7z was created before deleting the tarball
if [ -e "${encrypted_archive_file}" ]
then
log "Removing unencrypted ${archive_file}"
rm -f "${archive_file}"
chown ${BACKUP_OWNER}:${BACKUP_GROUP} "${encrypted_archive_file}"
else
chown ${BACKUP_OWNER}:${BACKUP_GROUP} "${archive_file}"
log "ERROR: 7z failed! Leaving unencrypted ${archive_file} on drive!"
fi
log "backup.sh finished!"
#list files ${ARCHIVE_DESTINATION} to check file sizes.
ls -lh "${ARCHIVE_DESTINATION}/"
fi # archive creation
Comments
0 B
|0 👍
/0 👎
0 B
|0 👍
/0 👎