backup.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2021, 2022, 2023, 2024 Oleg Pykhalov <go.wigust@gmail.com>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (services backup)
  19. #:use-module (gnu packages base)
  20. #:use-module (gnu packages backup)
  21. #:use-module (gnu packages curl)
  22. #:use-module (gnu packages linux)
  23. #:use-module (gnu packages virtualization)
  24. #:use-module (gnu services)
  25. #:use-module (gnu services base)
  26. #:use-module (gnu services shepherd)
  27. #:use-module (gnu system shadow)
  28. #:use-module (guix gexp)
  29. #:use-module (guix records)
  30. #:use-module (srfi srfi-1)
  31. #:export (restic-rest-configuration
  32. restic-rest-service-type
  33. restic-system-backup
  34. restic-guix-backup
  35. restic-win10-backup
  36. restic-win2022-backup
  37. restic-ntfsgames-backup
  38. restic-openwrt-init
  39. restic-openwrt-backup
  40. restic-whonix-gateway-direct-init
  41. restic-whonix-gateway-direct-backup
  42. restic-command))
  43. ;;; Commentary:
  44. ;;;
  45. ;;; This module provides a service definition for the backup service.
  46. ;;;
  47. ;;; Code:
  48. (define-record-type* <restic-rest-configuration>
  49. restic-rest-configuration make-restic-rest-configuration
  50. restic-rest-configuration?
  51. (restic-rest restic-rest-configuration-restic-rest ;string
  52. (default #f))
  53. (listen-address restic-rest-configuration-listen-address ;string
  54. (default "0.0.0.0:8080"))
  55. (data-path restic-rest-configuration-data-path ;string
  56. (default "/var/lib/restic"))
  57. (append-only? restic-rest-configuration-append-only? ;boolean
  58. (default #f))
  59. (prometheus? restic-rest-configuration-prometheus? ;boolean
  60. (default #f))
  61. (private-repos? restic-rest-configuration-private-repos? ;boolean
  62. (default #f))
  63. (arguments restic-rest-configuration-arguments ;list of strings
  64. (default '()))
  65. (user restic-rest-configuration-user ;string
  66. (default "restic"))
  67. (group restic-rest-configuration-group ;string
  68. (default "restic"))
  69. (authentication? restic-rest-configuration-authentication? ; boolean
  70. (default #t)))
  71. (define (restic-account config)
  72. (list (user-account
  73. (name "restic")
  74. (group "restic")
  75. (system? #t)
  76. (comment "Restic REST privilege separation user")
  77. (home-directory "/var/lib/restic"))
  78. (user-group
  79. (name "restic")
  80. (system? #t))))
  81. (define (restic-rest-activation config)
  82. #~(begin
  83. (let* ((user (getpw "restic"))
  84. (home (passwd:dir user))
  85. (uid (passwd:uid user))
  86. (group (getgrnam "restic"))
  87. (gid (group:gid group))
  88. (data-path #$(restic-rest-configuration-data-path config)))
  89. (mkdir-p data-path)
  90. (chown data-path uid gid))))
  91. (define (restic-rest-shepherd-service config)
  92. (list
  93. (shepherd-service
  94. (provision '(restic-rest))
  95. (documentation "Run Restic REST.")
  96. (requirement '(user-processes loopback))
  97. (start #~(make-forkexec-constructor
  98. (list #$(restic-rest-configuration-restic-rest config)
  99. "--listen" #$(restic-rest-configuration-listen-address config)
  100. "--path" #$(restic-rest-configuration-data-path config)
  101. #$@(if (restic-rest-configuration-append-only? config)
  102. '("--append-only")
  103. '())
  104. #$@(if (restic-rest-configuration-private-repos? config)
  105. '("--private-repos")
  106. '())
  107. #$@(if (restic-rest-configuration-prometheus? config)
  108. '("--prometheus")
  109. '())
  110. #$@(if (restic-rest-configuration-authentication? config)
  111. '()
  112. '("--no-auth"))
  113. #$@(restic-rest-configuration-arguments config))
  114. #:log-file "/var/log/restic-rest.log"
  115. #:user #$(restic-rest-configuration-user config)
  116. #:group #$(restic-rest-configuration-group config)))
  117. (respawn? #f)
  118. (stop #~(make-kill-destructor)))))
  119. (define restic-rest-service-type
  120. (service-type (name 'restic-rest)
  121. (extensions (list (service-extension account-service-type
  122. restic-account)
  123. (service-extension shepherd-root-service-type
  124. restic-rest-shepherd-service)
  125. (service-extension activation-service-type
  126. restic-rest-activation)))
  127. (description "Run Restic REST.")))
  128. ;;;
  129. ;;; Restic
  130. ;;;
  131. (define %root-directories
  132. '("/root/.cache"
  133. "/root/.guix-profile"
  134. "/root/.nix-profile"))
  135. (define %user-directories
  136. '(".cache"
  137. ".guix-profile"
  138. ".nix-profile"
  139. "Downloads"
  140. "GNS3"
  141. "Videos"
  142. "vm"))
  143. (define restic-binary
  144. (file-append restic "/bin/restic"))
  145. (define virsh-binary
  146. (file-append libvirt "/bin/virsh"))
  147. (define dd-binary
  148. (file-append coreutils "/bin/dd"))
  149. (define curl-binary
  150. (file-append curl "/bin/curl"))
  151. (define lvcreate-binary
  152. (file-append lvm2 "/sbin/lvcreate"))
  153. (define lvremove-binary
  154. (file-append lvm2 "/sbin/lvremove"))
  155. (define %user-home
  156. (passwd:dir (getpw "oleg")))
  157. (define restic-system-backup
  158. (program-file
  159. "restic-backup-system"
  160. #~(begin
  161. (use-modules (ice-9 rdelim)
  162. (srfi srfi-1))
  163. (let ((%backup-directories (list #$%user-home
  164. "/etc"
  165. "/root"
  166. "/var/lib/grafana"
  167. "/var/lib/crowdsec"
  168. "/var/lib/opensearch"
  169. "/var/lib/docker"
  170. "/var/lib/peertube"
  171. "/var/lib/peertube_assets"))
  172. (%exclude-directories
  173. (append '#$%root-directories
  174. (map (lambda (directory)
  175. (string-append #$%user-home "/" directory))
  176. '#$%user-directories))))
  177. (setenv "RESTIC_PASSWORD_FILE"
  178. (string-trim-right
  179. (with-input-from-file "/etc/guix/secrets/restic"
  180. read-string)))
  181. (display "Creating new Restic system snapshot\n")
  182. (zero?
  183. (apply system*
  184. (append (list #$restic-binary "--no-cache"
  185. "--repo" "/srv/backup/guixsd")
  186. (fold (lambda (directory directories)
  187. (append (list "--exclude" directory) directories))
  188. '() %exclude-directories)
  189. (list "backup")
  190. %backup-directories)))))))
  191. (define* (restic-lv-backup vg lv
  192. #:key (predicate #~(begin #t))
  193. restic-repository
  194. restic-password-file
  195. (lvm2-snapshot-size "32G"))
  196. "Return a GEXP which defines a logical volume backup steps."
  197. (let* ((device (string-append "/dev/" vg "/" lv))
  198. (lvm2-snapshot-name (string-append lv "-snap"))
  199. (lvm2-snapshot-device (string-append device "-snap")))
  200. (program-file
  201. (string-append "restic-backup-" vg "-" lv)
  202. #~(begin
  203. (use-modules (ice-9 rdelim)
  204. (srfi srfi-1))
  205. (if #$predicate
  206. (begin
  207. (format #t "Creating new Restic ~a snapshot~%" #$device)
  208. (setenv "RESTIC_PASSWORD_FILE" #$restic-password-file)
  209. (zero?
  210. (system
  211. (string-join
  212. (append (list #$dd-binary
  213. (string-append "if=" #$device)
  214. "bs=4M" "status=none")
  215. (list "|")
  216. (list #$restic-binary "--no-cache"
  217. "--repo" #$restic-repository
  218. "backup" "--stdin" "--stdin-filename"
  219. #$(string-append lv ".img")))))))
  220. (every identity
  221. (list
  222. (begin
  223. (format #t "Creating new LVM ~a snapshot~%" #$device)
  224. (system* #$lvcreate-binary
  225. "--size" #$lvm2-snapshot-size
  226. "--name" #$lvm2-snapshot-name
  227. "--snapshot" #$device))
  228. (begin
  229. (format #t "Creating new Restic ~a snapshot~%" #$device)
  230. (setenv "RESTIC_PASSWORD_FILE" #$restic-password-file)
  231. (zero?
  232. (system
  233. (string-join
  234. (append (list #$dd-binary
  235. (string-append "if=" #$lvm2-snapshot-device)
  236. "bs=4M" "status=none")
  237. (list "|")
  238. (list #$restic-binary "--no-cache"
  239. "--repo" #$restic-repository
  240. "backup" "--tag" "snapshot"
  241. "--stdin" "--stdin-filename"
  242. #$(string-append lv ".img")))))))
  243. (begin
  244. (format #t "Delete LVM snapshot and save changes ~a snapshot~%"
  245. #$lvm2-snapshot-device)
  246. (zero?
  247. (system* #$lvremove-binary "--yes"
  248. #$lvm2-snapshot-device))))))))))
  249. (define (virtual-machine-shut-off? virtual-machine-name)
  250. #~(begin
  251. (use-modules (ice-9 popen)
  252. (ice-9 rdelim))
  253. (let* ((port (open-pipe* OPEN_READ #$virsh-binary
  254. "domstate" #$virtual-machine-name))
  255. (output (read-string port)))
  256. (close-port port)
  257. (string= (string-trim-right output #\newline)
  258. "shut off"))))
  259. (define restic-win10-backup
  260. (restic-lv-backup "lvm1" "win10"
  261. #:restic-repository "/srv/backup/win10"
  262. #:restic-password-file "/etc/guix/secrets/windows"
  263. #:predicate (virtual-machine-shut-off? "win10")))
  264. (define restic-win2022-backup
  265. (restic-lv-backup "lvm2" "win2022"
  266. #:restic-repository "/srv/backup/win2022"
  267. #:restic-password-file "/etc/guix/secrets/windows"
  268. #:predicate (virtual-machine-shut-off? "win2022")))
  269. (define restic-ntfsgames-backup
  270. (restic-lv-backup "lvm2" "ntfsgames"
  271. #:restic-repository "/srv/backup/ntfsgames"
  272. #:restic-password-file "/etc/guix/secrets/guix"
  273. #:predicate (virtual-machine-shut-off? "win10")))
  274. (define restic-guix-backup
  275. (restic-lv-backup "lvm2" "guix"
  276. #:restic-repository "/srv/backup/guix"
  277. #:restic-password-file "/etc/guix/secrets/guix"
  278. #:predicate (virtual-machine-shut-off? "guix")))
  279. (define restic-openwrt-backup
  280. (restic-lv-backup "lvm1" "openwrt"
  281. #:restic-repository "/srv/backup/openwrt"
  282. #:restic-password-file "/etc/guix/secrets/restic-openwrt"
  283. #:predicate (virtual-machine-shut-off? "openwrt")))
  284. (define restic-whonix-gateway-direct-backup
  285. (restic-lv-backup "lvm1" "whonix-gateway-direct"
  286. #:restic-repository "/srv/backup/whonix-gateway-direct"
  287. #:restic-password-file "/etc/guix/secrets/restic-whonix-gateway-direct"
  288. #:predicate (virtual-machine-shut-off? "whonix-gateway-direct")))
  289. (define (restic-repository-init restic-repository-name
  290. restic-repository-directory
  291. restic-password-file)
  292. (program-file
  293. (string-append "restic-repository-init-" restic-repository-name)
  294. #~(unless (file-exists? #$restic-repository-directory)
  295. (use-modules (ice-9 rdelim))
  296. (setenv "RESTIC_PASSWORD_FILE" #$restic-password-file)
  297. (format #t "Creating new Restic ~a repository~%"
  298. #$restic-repository-directory)
  299. (zero?
  300. (apply system*
  301. (append (list #$restic-binary "--no-cache"
  302. "--repo" #$restic-repository-directory)
  303. (list "init")))))))
  304. (define restic-openwrt-init
  305. (restic-repository-init "openwrt"
  306. "/srv/backup/openwrt"
  307. "/etc/guix/secrets/restic-openwrt"))
  308. (define restic-whonix-gateway-direct-init
  309. (restic-repository-init "whonix-gateway-direct"
  310. "/srv/backup/whonix-gateway-direct"
  311. "/etc/guix/secrets/restic-whonix-gateway-direct"))
  312. (define (restic-command)
  313. (program-file
  314. "restic-commands"
  315. #~(map (lambda (program)
  316. (zero? (system* program)))
  317. (list #$restic-system-backup
  318. #$restic-guix-backup
  319. #$restic-win10-backup
  320. #$restic-win2022-backup
  321. #$restic-ntfsgames-backup
  322. #$restic-openwrt-init
  323. #$restic-openwrt-backup
  324. #$restic-whonix-gateway-direct-init
  325. #$restic-whonix-gateway-direct-backup))))
  326. ;;; backup.scm ends here