ulfben

backup.sh (rotating archives, encrypted)

Jan 9th, 2018
129
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2. # backup.sh - a basic backup script with grandfather-father-son rotation:
  3. #  - The rotation will do a daily backup Sunday through Friday.
  4. #  - On Saturday a weekly backup is done giving you four weekly backups a month.
  5. #  - The monthly backup is done on the first of the month, rotating two monthly backups (odd/even)
  6. #
  7. # The script was inspired by the Ubuntu Server Guide https://help.ubuntu.com/lts/serverguide/backups.html
  8. #
  9. # backup.sh will archive using tar + gzip (multithreaded) and then encrypt using 7z (AES256, no compression)
  10. # This is MUCH faster than it is to let 7z compress-&-encrypt in a single step - even at x=0.
  11. # Plus 7z is unfit for backup duty on Linux (see the man-page) so the tarball stays. :)
  12. #
  13. # Add a cron job to run at 03:00 every day:
  14. #   $ crontab -e
  15. #   0 3 * * * bash /home/myuser/scripts/backup.sh > /var/log/backup.log 2>&1
  16. #
  17. # //Ulf Benjaminsson, 2018-01
  18. if [ $(id -u) != 0 ] ; then
  19.     echo "please run backup.sh as root" >&2
  20.     exit 1
  21. fi
  22.  
  23. HOSTNAME=$(hostname -s)
  24. TIMESTAMP_FORMAT='%Y-%m-%d %H:%M:%S'
  25. ARCHIVE_PWD='secret' #used for 7z encryption. Will show up in process lists.
  26. BACKUP_OWNER='myuser'
  27. BACKUP_GROUP='mygroup'
  28.  
  29. MYSQL_DUMP_USER='BACKUPUSER'
  30. MYSQL_DUMP_PWD="secret" #will NOT be printed in logs or process lists
  31. DB_DESTINATION='/home/myuser/mysqldump'
  32.  
  33. FOLDERS_TO_BACKUP='/home /etc /var/www /var/log /usr/local /usr/share/phpmyadmin /root /boot /opt'
  34. ARCHIVE_DESTINATION='/media/backup/rotatingbackup'
  35.  
  36. # log(): timestamped output
  37. # Bash version in numbers like XYYYZZZ, where X is major version, YYY is minor, ZZZ is subminor.
  38. printf -v BV '%d%03d%03d' ${BASH_VERSINFO[0]} ${BASH_VERSINFO[1]} ${BASH_VERSINFO[2]}
  39. if [[ ${BV} -gt 4002000 ]]; then
  40.     log() { # Fast (builtin) but sec is highest precision for most implementations    
  41.         printf "[%(${TIMESTAMP_FORMAT})T] %s\\n" '-1' "$*"
  42.     }
  43. else
  44.     log() { # Slower, but support nanoseconds and legacy bash versions    
  45.         echo "[$(date +"${TIMESTAMP_FORMAT}")] $*"
  46.     }
  47. fi
  48.  
  49. # which week of the month is it (1-4)?
  50. day_num=$(date +%-d)
  51. if (( day_num <= 7 )); then
  52.     week_file="${HOSTNAME}-week1"
  53. elif (( day_num <= 14 )); then
  54.     week_file="${HOSTNAME}-week2"
  55. elif (( day_num <= 21 )); then
  56.     week_file="${HOSTNAME}-week3"
  57. else # day_num < 32
  58.     week_file="${HOSTNAME}-week4"
  59. fi
  60.  
  61. # is the month odd or even?
  62. is_even_month=$(expr $(date +%m) % 2)
  63. if [ "$is_even_month" -eq 0 ]; then
  64.     month_file="${HOSTNAME}-month2"
  65. else
  66.     month_file="${HOSTNAME}-month1"
  67. fi
  68.  
  69. # create archive filename.
  70. day=$(date +%A)
  71. if [ "$day_num" == 1 ]; then
  72.     archive_name=$month_file
  73. elif [ "$day" != "Saturday" ]; then
  74.         archive_name="${HOSTNAME}-$day"
  75. else
  76.     archive_name=$week_file
  77. fi
  78.  
  79. #build the full paths before starting the real work
  80. database_file="${DB_DESTINATION}/${archive_name}.sql.7z"
  81. archive_file="${ARCHIVE_DESTINATION}/${archive_name}.tgz"
  82. encrypted_archive_file="${archive_file}.7z"
  83.  
  84. log "Starting backup.sh"
  85.  
  86. if [ ! -d "${DB_DESTINATION}" ]
  87. then
  88.     log "ERROR: ${DB_DESTINATION} does not exist. Skipping mysqldump."
  89. else
  90.     log "Dumping MySQL to ${database_file}"
  91.     rm -f "${database_file}" # delete any pre-existing archive manually, because 7za is a PITA.
  92.     export MYSQL_PWD=$MYSQL_DUMP_PWD #read https://unix.stackexchange.com/a/369568 for the security implications   
  93.     mysqldump -u ${MYSQL_DUMP_USER} --all-databases | 7za a -mhe=on -p"${ARCHIVE_PWD}" -mx=9 -si "${database_file}"
  94.     unset MYSQL_PWD
  95.     chown ${BACKUP_OWNER}:${BACKUP_GROUP} "${database_file}"   
  96. fi # mysqldump
  97.  
  98. if [ ! -d "${ARCHIVE_DESTINATION}" ]
  99. then
  100.     log "ERROR: ${ARCHIVE_DESTINATION} does not exist. Skipping backup ${archive_name}"
  101.     log "backup.sh finished with errors!"
  102. else
  103.     log "Backing up ${FOLDERS_TO_BACKUP} to ${archive_file}"
  104.     tar --use-compress-program=pigz -Pcf "${archive_file}" $FOLDERS_TO_BACKUP
  105.        
  106.     # delete any pre-existing encrypted archive manually, because 7za is a PITA.
  107.     rm -f "${encrypted_archive_file}"
  108.    
  109.     log "Encrypting ${archive_file} to ${encrypted_archive_file}"
  110.     7za a -mhe=on -p"${ARCHIVE_PWD}" -mx=0 "${encrypted_archive_file}" "${archive_file}"
  111.  
  112.     #make sure the 7z was created before deleting the tarball
  113.     if [ -e "${encrypted_archive_file}" ]
  114.     then   
  115.         log "Removing unencrypted ${archive_file}"
  116.         rm -f "${archive_file}"
  117.         chown ${BACKUP_OWNER}:${BACKUP_GROUP} "${encrypted_archive_file}"
  118.     else
  119.         chown ${BACKUP_OWNER}:${BACKUP_GROUP} "${archive_file}"
  120.         log "ERROR: 7z failed! Leaving unencrypted ${archive_file} on drive!"
  121.     fi
  122.    
  123.     log "backup.sh finished!"
  124.     #list files ${ARCHIVE_DESTINATION} to check file sizes.
  125.     ls -lh "${ARCHIVE_DESTINATION}/"
  126. fi # archive creation
RAW Paste Data