From 47f8e82a3c33fadce4524b750915d92e8e5b9c4b Mon Sep 17 00:00:00 2001 From: Timo Wilken Date: Tue, 19 Sep 2023 23:32:26 +0200 Subject: Formalise password keys to avoid PATH issues Mcron seems to set a restricted PATH now, so make sure it knows where to find "pass". Also use less shell substitution and implement repository URL building in Guile. --- tw/services/restic.scm | 68 +++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 25 deletions(-) (limited to 'tw/services/restic.scm') diff --git a/tw/services/restic.scm b/tw/services/restic.scm index eda02d3e..ec2c9f87 100644 --- a/tw/services/restic.scm +++ b/tw/services/restic.scm @@ -3,19 +3,25 @@ #:use-module (gnu home services mcron) #:use-module ((gnu packages admin) #:select (shadow)) - #:use-module (gnu packages backup) + #:use-module ((gnu packages backup) + #:select (restic)) + #:use-module ((gnu packages password-utils) + #:select (password-store)) #:use-module (gnu services) #:use-module (gnu services configuration) #:use-module (gnu services mcron) #:use-module (gnu services shepherd) #:use-module (guix gexp) #:use-module (guix packages) + #:use-module ((guix records) + #:select (match-record)) #:use-module (srfi srfi-1) #:export (restic-server-service-type restic-server-configuration restic-cleanup-service-type restic-cleanup-repository home-restic-backup-service-type + restic-rest-repository restic-backup-repository)) (define %restic-user "restic") @@ -190,6 +196,13 @@ true." (define (nonempty-list-of-strings? thing) (and (pair? thing) (list-of-strings? thing))) +(define-configuration/no-serialization restic-rest-repository + (username string "The HTTP username for the repository.") + (password-key string "The password-store key of the repo's HTTP password.") + (hostname string "The hostname serving the repository.") + (port (integer 80) "The port number to connect to.") + (path (string "/") "The HTTP path at which the repository is found.")) + (define-configuration/no-serialization restic-backup-repository (schedule gexp "An mcron schedule, specified as a gexp (@pxref{G-Expressions}), to use for the backup job. String, list or lambda @@ -197,35 +210,40 @@ syntax is fine (@pxref{Syntax, mcron job specifications,, mcron, GNU@tie{}mcron}).") (paths nonempty-list-of-strings "List of paths to back up. At least one must be given. Leading @code{~/} are replaced with @code{$HOME}.") - (url-command string "Run this command (inside @code{sh -c}) to obtain the -directory or URL of the repo to back up to. This allows you to substitute -passwords in @code{rest:} URLs.") - (password-command string "Run this command to obtain the repository password.") + (url restic-rest-repository "Back up to the given @code{rest:} repository.") + (password-key string "Obtain the repository password from password-store at +the given key.") (tags (list-of-strings '()) "Optional tags to add to the snapshot.") (restic (package restic) "The restic package to use.")) (define (restic-backup-cronjobs repositories) (define (cronjob repo) - #~(job #$(restic-backup-repository-schedule repo) - #$(program-file "restic-backup-command" - #~(begin - (use-modules (ice-9 popen) - (ice-9 textual-ports) - (srfi srfi-1)) - (define (replace-home path) - (if (string-prefix? "~/" path) - (string-replace path (getenv "HOME") 0 1) - path)) - (setenv "RESTIC_PASSWORD_COMMAND" - '#$(restic-backup-repository-password-command repo)) - (let ((pipe (open-pipe '#$(restic-backup-repository-url-command repo) OPEN_READ))) - (setenv "RESTIC_REPOSITORY" (string-trim-right (get-string-all pipe) #\newline)) - (close-pipe pipe)) - (apply execl #$(file-append (restic-backup-repository-restic repo) "/bin/restic") - "restic" "backup" "--cleanup-cache" - #$@(append-map (lambda (tag) (list "--tag" tag)) - (restic-backup-repository-tags repo)) - (map replace-home '#$(restic-backup-repository-paths repo))))))) + (match-record repo + (schedule paths url password-key tags restic) + (match-record url + (username hostname port path) + #~(job #$schedule + #$(program-file "restic-backup-command" + #~(begin + (use-modules (ice-9 popen) + (ice-9 textual-ports) + (srfi srfi-1)) + (define pass #$(file-append password-store "/bin/pass")) + (define (replace-home path) + (if (string-prefix? "~/" path) + (string-replace path (getenv "HOME") 0 1) + path)) + (let ((pipe (open-pipe* OPEN_READ pass #$(restic-rest-repository-password-key url)))) + (setenv "RESTIC_REPOSITORY" + (format #f "rest:http://~a:~a@~a:~a/~a" + #$username (get-line pipe) #$hostname #$port + (string-trim #$path #\/))) + (close-pipe pipe)) + (setenv "RESTIC_PASSWORD_COMMAND" (string-append pass " " #$password-key)) + (apply execl #$(file-append restic "/bin/restic") + "restic" "backup" "--cleanup-cache" + #$@(append-map (lambda (tag) (list "--tag" tag)) tags) + (map replace-home '#$paths)))))))) (map cronjob repositories)) (define home-restic-backup-service-type -- cgit v1.2.3