cryptshotr 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. #!/bin/sh
  2. ## CryptShotR
  3. ## Open and mount a LUKs volume, locally or remotely, before performing a
  4. # backup with rsnapshot.
  5. #
  6. # Author: demure demeanor
  7. # Repository: https://notabug.org/demure/scripts
  8. # Website: http://demu.red/
  9. # Blog Post: http://demu.red/
  10. ## This is based on cryptshot by:
  11. # Author: Pig Monkey (pm@pig-monkey.com)
  12. # Website: https://github.com/pigmonkey/backups
  13. ## Modified to support a GPG encrypted LUKs key file.
  14. # This can backup to the LUKs container either locally, or over the network
  15. # to server where the LUKs container is connected. Supports a flag to
  16. # suppress non-configuration related errors. The script can optionally
  17. # support ssh-agent. GPG decryption relies on the gpg-agnet caching, which
  18. # I frequently refresh for pulling emails in mutt.
  19. #
  20. # It will check if the volume with configured UUID exists locally, and if
  21. # not will check remotely for the volume. If the volume is found it will be
  22. # treated as a LUKs volume and decrypted with the given GPG encrypted LUKs
  23. # key file, and then mounted. The script then runs rsnapshot. After the
  24. # backup is complete the volume is unmounted and closed. Can optionally
  25. # delete the mount point for cleanup.
  26. #
  27. # This script requires a LUKs+dmcrypt container, as well as a GPG encrypted
  28. # LUKs key file. The container allows for secure storage of backups.
  29. # The encrypted key file protects the key from compromise.
  30. #
  31. # The script should be called with your configured rsnapshot intervals.
  32. # Optionally, '-q' or '-quiet' can be used as the first augment, to silence
  33. # non-configuration errors.
  34. ### Conf ### {{{
  35. ## Define the UUID of the backup volume.
  36. UUID="9c6a7bec-d6ff-484c-87e0-25b9f038aed1"
  37. ## Define the location of the LUKS key file.
  38. KEYFILE="$HOME/backups.gpg"
  39. ## Define gpg key, used to decrypt LUKS key file.
  40. GPGPRINT="FA88A5CE"
  41. ## Define the mount point for the backup volume.
  42. ## This will be created if it does not already exist.
  43. MOUNTPOINT="/mnt/$UUID"
  44. ## Any non-zero value here will caused the mount point to be deleted after the
  45. ## volume is unmounted.
  46. REMOVEMOUNT=1
  47. ## Define the location of rsnapshot.
  48. RSNAPSHOT="/usr/bin/rsnapshot"
  49. ### End Conf ### }}}
  50. ### Remote Conf ### {{{
  51. ## Enable Remote backups
  52. RE_ON=1
  53. ## Enable ssh agent
  54. # This is assuming that your key is already sourced like:
  55. # https://notabug.org/demure/dotfiles/src/master/subbash/sshagent
  56. RE_AGENT=1
  57. ## Define home routers MAC addr.
  58. RE_HOME="10:FE:ED:E9:63:04"
  59. ## Define remote address, for testing.
  60. RE_HOST="tiny-server-of-doom.local"
  61. ## Define remote user
  62. RE_USER="backups"
  63. ### End Remote Conf ### }}}
  64. ### Startup Tests ### {{{
  65. ## Exit if no volume is specified.
  66. if [ "$UUID" = "" ]; then
  67. echo 'No volume specified.'
  68. exit 78
  69. fi
  70. ## Exit if no key file is specified.
  71. if [ "$KEYFILE" = "" ]; then
  72. echo 'No key file specified.'
  73. exit 78
  74. fi
  75. ## Exit if no gpg finger print is specified.
  76. if [ "$GPGPRINT" = "" ]; then
  77. echo 'No GPG finger print specified.'
  78. exit 78
  79. fi
  80. ## Exit if no mount point is specified.
  81. if [ "$MOUNTPOINT" = "" ]; then
  82. echo 'No mount point specified.'
  83. exit 78
  84. fi
  85. ## Exit if no interval was specified.
  86. ## Tests about this line should not be effected by Quiet flag
  87. ## Why? Well, it's enacted here, and missing configuration is BAD.
  88. if [ "$1" = "" ]; then
  89. echo "No interval specified."
  90. exit 64
  91. elif [ "$1" = "-q" ] || [ "$1" = "--quiet" ]; then
  92. ## set quiet mode code here.
  93. ## NOTE: Make quiet mode not effect missing settings or interval.
  94. QUIET=1
  95. if [ "$2" = "" ]; then
  96. echo "No interval specified."
  97. exit 64
  98. else
  99. interval=$2
  100. fi
  101. else
  102. interval=$1
  103. QUIET=0
  104. fi
  105. ### End Startup Tests ### }}}
  106. ### Remote Startup Tests ### {{{
  107. if [ "$RE_ON" = "1" ]; then
  108. if [ "$RE_HOME" = "" ]; then
  109. echo "No local network router MAC specified."
  110. exit 73
  111. fi
  112. if [ "$RE_HOST" = "" ]; then
  113. echo "No remote host specified."
  114. exit 73
  115. fi
  116. if [ "$RE_USER" = "" ]; then
  117. echo "No remote user specified."
  118. exit 73
  119. fi
  120. fi
  121. ### End Remote Startup Tests ### }}}
  122. ### Prep Work ### {{{
  123. ## Build the reference to the volume.
  124. volume="/dev/disk/by-uuid/$UUID"
  125. ## Create a unique name for the LUKS mapping.
  126. name="crypt-$UUID"
  127. ## If the mount point does not exist, create it.
  128. if [ ! -d "$MOUNTPOINT" ]; then
  129. mkdir $MOUNTPOINT
  130. # Exit if the mount point was not created.
  131. if [ $? -ne 0 ]; then
  132. echo "Failed to create mount point."
  133. exit 73
  134. fi
  135. fi
  136. ### GPG Test ### {{{
  137. ## Check that GPG key is cached in gpg-agent
  138. ## First see if SUDO_UID exists.
  139. # This is needed as cryptshotr-cron -> cryptshotr results in using SUDO_UID
  140. # but cryptshot run manually has UID
  141. # http://demu.red/blog/2016/08/how-to-check-if-your-gpg-key-is-in-cache-part-2/
  142. if [ -z "${SUDO_UID}" ]; then
  143. gpg_socket="/run/user/${UID}/gnupg/S.gpg-agent"
  144. else
  145. gpg_socket="/run/user/${SUDO_UID}/gnupg/S.gpg-agent"
  146. fi
  147. ## Test if cached
  148. if [ -e ${gpg_socket} ]; then
  149. gpg_test=$(gpg-connect-agent -S ${gpg_socket} 'keyinfo --list' /bye 2>/dev/null | awk 'BEGIN{CACHED=0} /^S/ {if($7==1){CACHED=1}} END{print CACHED}')
  150. else
  151. gpg_test=$(gpg-connect-agent 'keyinfo --list' /bye 2>/dev/null | awk 'BEGIN{CACHED=0} /^S/ {if($7==1){CACHED=1}} END{print CACHED}')
  152. fi
  153. ## Finally, the graceful failure.
  154. if [ $gpg_test = "0" ]; then
  155. if [ "$QUIET" = 0 ]; then
  156. echo 'GPG key is not cached.'
  157. fi
  158. exit 78
  159. fi
  160. ### End GPG Test ### }}}
  161. ## Load ssh-agent, if enabled.
  162. if [ "$RE_AGENT" = 1 ]; then
  163. ssh_env="$HOME/.ssh/environment"
  164. if [ -f ${ssh_env} ]; then
  165. . "${ssh_env}" >/dev/null
  166. ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ >/dev/null || {
  167. echo "PID bad?";
  168. }
  169. else
  170. echo "ENV file bad?"
  171. fi
  172. fi
  173. ## Set the default exit code to 0.
  174. exitcode=0
  175. ### End Prep Work ### }}}
  176. ## Continue if the volume exists.
  177. if [ -e $volume ]; then
  178. ### Local Backup ### {{{
  179. ## Attempt to open the LUKS volume.
  180. gpg2 --no-permission-warning --no-tty -qd $KEYFILE | cryptsetup luksOpen --key-file=- $volume $name
  181. ## If the volume was decrypted, mount it.
  182. if [ $? -eq 0 ]; then
  183. mount /dev/mapper/$name $MOUNTPOINT
  184. ## If the volume was mounted, run the backup.
  185. if [ $? -eq 0 ]; then
  186. ## Run rsnapshot
  187. $RSNAPSHOT $interval
  188. ## Unmount the volume
  189. umount $MOUNTPOINT
  190. ## If the volume was unmounted and the user has requested that the
  191. ## mount point be removed, remove it.
  192. if [ $? -eq 0 -a $REMOVEMOUNT -ne 0 ]; then
  193. rmdir $MOUNTPOINT
  194. fi
  195. else
  196. exitcode=$?
  197. if [ $"$QUIET" = 0 ]; then
  198. echo "Failed to mount $volume at $MOUNTPOINT."
  199. fi
  200. fi
  201. ## Close the LUKS volume.
  202. cryptsetup luksClose $name
  203. else
  204. exitcode=$?
  205. if [ $"$QUIET" = 0 ]; then
  206. echo "Failed to open $volume with key $KEYFILE."
  207. fi
  208. fi
  209. ### End Local Backup ### }}}
  210. else
  211. ### Remote Backup ### {{{
  212. if [ $RE_ON -ne 0 ]; then
  213. re_addr="${RE_USER}@${RE_HOST}"
  214. ## Get MAC of router
  215. check_mac="$(iwgetid --ap -r)"
  216. if [ "$RE_HOME" = "$check_mac" ]; then
  217. ## Check that host is up
  218. ping -c 3 $RE_HOST 1>/dev/null
  219. if [ $? = 0 ]; then
  220. ## Check if drive connected
  221. check_volume=$(ssh $re_addr "test -e $volume && echo 1 || echo 0" 2>/dev/null)
  222. if [ $check_volume = 1 ]; then
  223. ## Make MOUNTPOINT if not present
  224. ssh $re_addr "test -e $MOUNTPOINT || sudo mkdir $MOUNTPOINT"
  225. if [ $? = 0 ]; then
  226. ## Open LUKs container
  227. gpg2 --no-permission-warning --no-tty -qd $KEYFILE | ssh $re_addr "sudo cryptsetup luksOpen --key-file=- $volume $name"
  228. if [ $? = 0 ]; then
  229. ## Mount LUKs container
  230. ssh $re_addr "sudo mount /dev/mapper/$name $MOUNTPOINT"
  231. if [ $? = 0 ]; then
  232. ## Run rsnapshot
  233. ssh $re_addr "sudo $RSNAPSHOT $interval"
  234. ## Unmount LUKs container
  235. ssh $re_addr "sudo umount $MOUNTPOINT"
  236. ## If the volume was unmounted and the user has requested that the
  237. ## mount point be removed, remove it.
  238. if [ $? -eq 0 -a $REMOVEMOUNT -ne 0 ]; then
  239. ssh $re_addr "sudo rmdir $MOUNTPOINT"
  240. rmdir $MOUNTPOINT
  241. fi
  242. else
  243. exitcode=$?
  244. if [ $"$QUIET" = 0 ]; then
  245. echo "Failed to mount remote $volume at $MOUNTPOINT."
  246. fi
  247. fi
  248. else
  249. exitcode=$?
  250. if [ $"$QUIET" = 0 ]; then
  251. echo "Failed to open remote $volume with key $KEYFILE."
  252. fi
  253. fi
  254. ## Close LUKs Container
  255. ssh $re_addr "sudo cryptsetup luksClose $name"
  256. else
  257. exitcode=66
  258. if [ $"$QUIET" = 0 ]; then
  259. echo "Could not make remote $MOUNTPOINT."
  260. fi
  261. fi
  262. else
  263. exitcode=66
  264. if [ $"$QUIET" = 0 ]; then
  265. echo "Remote volume $UUID not found."
  266. fi
  267. fi
  268. else
  269. exitcode=66
  270. if [ $"$QUIET" = 0 ]; then
  271. echo "Host not found."
  272. fi
  273. fi
  274. else
  275. exitcode=66
  276. if [ $"$QUIET" = 0 ]; then
  277. echo "Not on home network."
  278. fi
  279. fi
  280. else
  281. exitcode=33
  282. if [ $"$QUIET" = 0 ]; then
  283. echo "Volume $UUID not found."
  284. fi
  285. fi
  286. ### End Remote Backup ### }}}
  287. fi
  288. exit $exitcode