borg_wrap.sh 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. #!/bin/sh
  2. ##### This is my (demuredemeanor) borgbackup wrapper script.
  3. # Blog Post: https://demu.red/blog/2017/02/backups-revisited/
  4. # Source Code: https://notabug.org/demure/scripts/src/master/borg_wrap.sh
  5. # Uses tabstop=4; shiftwidth=4 tabs; foldmarker={{{,}}};
  6. #
  7. # This is a script to run my borg backups.
  8. # It has basic config error checking, and verifies I am on my home network.
  9. # Usage: borg_wrap.sh [-q|--quiet]
  10. #
  11. # Note: I run this as root, and have a /etc/sudoers.d/ file granting permissions.
  12. #
  13. # Note: I run this via crontab
  14. # 0 */1 * * * sudo /usr/local/sbin/borg_wrap.sh -q
  15. #
  16. # Note: pruning:
  17. # Keep last last 24h, one per day of last week, one per week of last month, one per month.
  18. # borg prune -v --list --dry-run -d=7 -w=4 -m=-1 --keep-within=1d /path/to/repo
  19. ### Conf ### {{{
  20. ## Set Repo location
  21. export BORG_REPO='ssh://demure@10.0.0.10:500/mnt/borg/doom-x270'
  22. ## Set the right ssh key
  23. ## Note, use with the following in the destination ssh authorized_keys
  24. ## command="borg serve --restrict-to-path /path/to/repo",no-pty,no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-user-rc ssh-rsa AAAAB3[...]
  25. export BORG_RSH="ssh -i $HOME/.ssh/borg_id_ed25519"
  26. ## Pull Repo passwd
  27. export BORG_PASSPHRASE="$(pass cli/borg 2>/dev/null)"
  28. ## Path to log file
  29. Log_Path="$HOME/.config/borg/log"
  30. ## Temp file for log pruning
  31. Log_tmp="/tmp/borg_log.tmp"
  32. ## Define remote MAC addr, for security check.
  33. MAC_addr="24:5e:be:0d:b4:04"
  34. ## Define remote address, for testing.
  35. Host="10.0.0.10"
  36. ## Define User for chmod fix (due to running with sudo)
  37. User="demure"
  38. ### End Conf ### }}}
  39. ### Conf Check ### {{{
  40. ## Exit if BORG_REPO not specified.
  41. if [ "${BORG_REPO}" = "" ]; then
  42. echo 'No repo specified.'
  43. exit 78
  44. fi
  45. ## Exit if no log file specified.
  46. if [ "${Log_Path}" = "" ]; then
  47. echo 'No log file specified.'
  48. exit 78
  49. fi
  50. ## Exit if no router MAC specified.
  51. if [ "${MAC_addr}" = "" ]; then
  52. echo 'No router MAC specified.'
  53. exit 78
  54. fi
  55. ## Exit if storage address specified.
  56. if [ "${Host}" = "" ]; then
  57. echo 'No storage address specified.'
  58. exit 78
  59. fi
  60. ## Exit if no chown user specified.
  61. if [ "${User}" = "" ]; then
  62. echo 'User for chown not set.'
  63. exit 78
  64. fi
  65. ### End Conf Check ### }}}
  66. ### Startup Check ### {{{
  67. ### Augment Check ### {{{
  68. if [ $# -gt 1 ]; then
  69. echo 'Too many augments.'
  70. exit 1
  71. fi
  72. ## Initialize Variables
  73. Quiet=0 ## Default, not quite
  74. VERBOSE='-v --stats -p' ## Default, verbose output
  75. Prune=0 ## Default, not prune mode
  76. DRY='' ## Default, not dry
  77. if [ ! -z "${1}" ]; then
  78. if [ "${1}" = '-q' ] || [ "${1}" = '--Quiet' ]; then
  79. VERBOSE=""
  80. Quiet=1
  81. elif [ "${1}" = '--prune' ]; then
  82. Prune=1
  83. elif [ "${1}" = '--dry-prune' ]; then
  84. Prune=1
  85. DRY='--dry-run'
  86. else
  87. echo 'Bad augments.'
  88. exit 1
  89. fi
  90. fi
  91. ### End Augment Check ### }}}
  92. ### Network Check ### {{{
  93. ## Check for host, then check if MAC addr matches
  94. ping -c 3 ${Host} 1>/dev/null
  95. if [ ! $? = 0 ]; then
  96. if [ ${Quiet} -eq 0 ]; then
  97. echo "Can't reach host."
  98. fi
  99. Error='NoPing'
  100. else
  101. check_mac="$(/bin/ip neigh | awk -v HOST=${Host} '$0~HOST{print $5}')"
  102. if [ ! "${MAC_addr}" = "${check_mac}" ]; then
  103. if [ ${Quiet} -eq 0 ]; then
  104. echo "Not on home network."
  105. fi
  106. Error='BadNet'
  107. fi
  108. fi
  109. ### End Network Check ### }}}
  110. ### Passwd Load Check ### {{{
  111. if [ -z "${BORG_PASSPHRASE}" ]; then
  112. if [ ${Quiet} -eq 0 ]; then
  113. echo "Password failed to load."
  114. fi
  115. Error='NoPass'
  116. fi
  117. ### End Passed Load Check ### }}}
  118. ### End Startup Check ### }}}
  119. ### Prune Mode ### {{{
  120. ## Note: prune mode shouln't need sudo
  121. if [ "${Prune}" -eq 1 ]; then
  122. if [ ! -z ${Error} ]; then
  123. echo "Error: ${Error}"
  124. exit 1
  125. fi
  126. borg prune ${DRY} -v -s --list -d=7 -w=4 -m=-1 --keep-within=1d
  127. exit 0
  128. fi
  129. ### End Prune Mode ### }}}
  130. ## If no errors, run.
  131. ## Note: Will need sudo if backing up system wide files
  132. ## A suggested naming convntion
  133. ## ::'{hostname}_{now:%Y-%m-%d_%H%M}' \
  134. if [ -z $Error ]; then
  135. borg create ${VERBOSE} -C lz4 \
  136. ::'doom-x270_{now:%Y-%m-%d_%H%M}' \
  137. / \
  138. --exclude-caches \
  139. -e /dev \
  140. -e /media/ \
  141. -e /mnt/ \
  142. -e /proc \
  143. -e /run \
  144. -e /sys \
  145. -e /tmp \
  146. -e /var/cache \
  147. -e '/var/tmp/*' \
  148. -e '/home/*/Downloads/' \
  149. -e '/home/*/temp/' \
  150. -e '/home/*/vault' \
  151. -e '/home/*/.config/chromium' \
  152. -e '/home/*/.local/share/mana/updates' \
  153. -e '*.cache' \
  154. -e '*.mail' \
  155. -e '*.thumbnails'
  156. ## Catch exit code
  157. Error=$?
  158. fi
  159. ## Log pass/fail state
  160. if [ ! $Error = "0" ]; then
  161. echo "$(date '+%F %T')\tfailed\t${Error}" | cat >> ${Log_Path}
  162. else
  163. echo "$(date '+%F %T')\tpassed" | cat >> ${Log_Path}
  164. fi
  165. ## This prunes the log, leaving last 30 days.
  166. ## If no passed backups in 30 days, also leaves lasted passed entry.
  167. ## You can comment out to disable
  168. awk -v cutoff="$(date -d"-30 days" +%F)" 'BEGIN{pchk=0} {a[i++]=$0} END{for(j=i-1;j>=0;j--) if(a[j] >= cutoff || pchk==0 && a[j] ~ /passed/){if(a[j] ~ /passed/){pchk=1}; b[k++]=a[j]};{for(l=k-1;l>=0;l--) print b[l]}}' ${Log_Path} > ${Log_tmp} && mv ${Log_tmp} ${Log_Path}
  169. ## Fix borg permissions so that things like `borg list` work without sudo
  170. chown -R ${User}:${User} ${HOME}/.config/borg ${HOME}/.cache/borg