From 79ea4090f9b5591e1e93c0bc5a5250ce4b390981 Mon Sep 17 00:00:00 2001 From: Timo Wilken Date: Thu, 20 Apr 2023 23:12:01 +0200 Subject: Add initial restic-server code --- tw/packages/restic.scm | 80 +++++++++++++++++++++++++++++++++++++++++++++++ tw/services/restic.scm | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 tw/packages/restic.scm create mode 100644 tw/services/restic.scm diff --git a/tw/packages/restic.scm b/tw/packages/restic.scm new file mode 100644 index 00000000..9c1a5865 --- /dev/null +++ b/tw/packages/restic.scm @@ -0,0 +1,80 @@ +(define-module (tw packages restic) + #:use-module (gnu packages golang) + #:use-module (gnu packages syncthing) ;some Go libraries + #:use-module (guix build utils) + #:use-module (guix build-system copy) + #:use-module (guix build-system go) + #:use-module (guix git-download) + #:use-module (guix packages) + #:use-module ((guix licenses) + #:prefix license:)) + +(define-public go-github-com-coreos-go-systemd-activation + (package + (name "go-github-com-coreos-go-systemd-activation") + (version "0.0.0-20191104093116-d3cd4ed1dbcf") + (source (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/coreos/go-systemd") + (commit (go-version->git-ref version)))) + (file-name (git-file-name name version)) + (sha256 + (base32 + "193mgqn7n4gbb8jb5kyn6ml4lbvh4xs55qpjnisaz7j945ik3kd8")))) + (build-system go-build-system) + (arguments + '(#:import-path "github.com/coreos/go-systemd/activation" + #:unpack-path "github.com/coreos/go-systemd")) + (home-page "https://github.com/coreos/go-systemd") + (synopsis "Go bindings to systemd socket activation") + (description "Go bindings to systemd socket activation; for writing and +using socket activation from Go.") + (license license:asl2.0))) + +(define-public restic-rest-server + (package + (name "restic-rest-server") + (version "0.11.0") + (source (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/restic/rest-server") + (commit (string-append "v" version)))) + (file-name (git-file-name name version)) + (sha256 + (base32 + "1nvmxc9x0mlks6yfn66fmwn50k5q83ip4g9vvb0kndzd7hwcyacy")))) + (build-system go-build-system) + (arguments + '(#:import-path "github.com/restic/rest-server/cmd/rest-server" + #:unpack-path "github.com/restic/rest-server" + #:install-source? #f ;all we need is the binary + #:phases (modify-phases %standard-phases + (replace 'check + (lambda* (#:key tests? #:allow-other-keys . args) + (when tests? + ;; Unit tests seems to break with Guix' non-standard TMPDIR. + (setenv "TMPDIR" "/tmp") + (apply (assoc-ref %standard-phases + 'check) args)))) + (add-after 'install 'rename-binary + (lambda* (#:key outputs #:allow-other-keys) + (with-directory-excursion (assoc-ref outputs "out") + ;; "rest-server" is a bit too generic. + (rename-file "bin/rest-server" + "bin/restic-rest-server"))))))) + (propagated-inputs (list go-golang-org-x-crypto + go-github-com-spf13-cobra + go-github-com-prometheus-client-golang + go-github-com-miolini-datacounter + go-github-com-minio-sha256-simd + go-github-com-gorilla-handlers + go-github-com-coreos-go-systemd-activation)) + (home-page "https://github.com/restic/rest-server") + (synopsis "Restic REST server") + (description + "The Restic REST server is a high performance HTTP server that implements +restic's REST backend API. It provides a secure and efficient way to backup +data remotely, using the restic backup client and a @code{rest:} URL.") + (license license:bsd-2))) diff --git a/tw/services/restic.scm b/tw/services/restic.scm new file mode 100644 index 00000000..637c7104 --- /dev/null +++ b/tw/services/restic.scm @@ -0,0 +1,84 @@ +(define-module (tw services restic) + #:use-module (gnu) + #:use-module ((gnu packages admin) + #:select (shadow)) + #:use-module (gnu services) + #:use-module (gnu services configuration) + #:use-module (gnu services shepherd) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module (tw packages restic) + #:export (restic-server-service-type + restic-server-configuration)) + +(define-maybe/no-serialization integer) +(define-maybe/no-serialization string) + +;; TODO: implement --tls, --tls-cert and --tls-key, maybe using certbot-service-type? +(define-configuration/no-serialization restic-server-configuration + (repository-path (string "/var/lib/restic") "The directory containing +restic's repositories and @code{.htpasswd} file, unless otherwise configured +using @code{htpasswd-file}.") + (restic-server (package restic-rest-server) "The restic REST server package to use.") + (bind-address (string ":8000") "The listen address (including port) to bind to.") + (htpasswd-file (maybe-string #f) "Location of @code{.htpasswd} file +(default: @code{REPOSITORY-PATH/.htpasswd}).") + (auth? (boolean #t) "Whether to authenticate users at all (using .htpasswd).") + (verify-upload? (boolean #t) "Whether to verify the integrity of uploaded +data. @emph{Do not disable} unless the restic server is to be run on a very +low-power device.") + (append-only? (boolean #f) "Whether to run the restic server in append-only mode.") + (max-repository-size (maybe-integer %unset-value) "Maximum repository size +in bytes, if any.") + (private-repos-only? (boolean #f) "Whether to let users only access their +private restic repos.") + (prometheus? (boolean #f) "Whether to serve Prometheus metrics.") + (prometheus-auth? (boolean #t) "Whether to require authentication as the +@code{metrics} user to access the Prometheus /metrics endpoint.")) + +(define (restic-server-arguments config) + "Turn CONFIG into a list of arguments to the restic-rest-server executable." + `("--path" ,(restic-server-configuration-repository-path config) + "--log" "/var/log/restic-server.log" + "--listen" ,(restic-server-configuration-bind-address config) + ,@(if (restic-server-configuration-append-only? config) '("--append-only") '()) + ,@(let ((max-size (restic-server-configuration-max-repository-size config))) + (if (integer? max-size) `("--max-size" ,max-size) '())) + ,@(if (restic-server-configuration-private-repos-only? config) '("--private-repos") '()) + ,@(if (restic-server-configuration-prometheus? config) '("--prometheus") '()) + ,@(if (restic-server-configuration-prometheus-auth? config) '() '("--prometheus-no-auth")))) + +(define (restic-server-service config) + "Create a `shepherd-service' for the restic REST server from CONFIG." + (list (shepherd-service + (provision '(restic-server)) + (requirement '(networking)) + (documentation "Run the Restic REST server to serve backup repositories via HTTP.") + (start #~(make-forkexec-constructor + (list #$(file-append (restic-server-configuration-restic-server config) + "/bin/restic-rest-server") + #$@(restic-server-arguments config)) + #:user "restic" #:group "restic")) + (stop #~(make-kill-destructor))))) + +(define (restic-server-accounts config) + "Create user accounts and groups for the restic REST server defined in CONFIG." + (list (user-account + (name "restic") + (group "restic") + (comment "Restic server user") + (system? #t) + (home-directory (restic-server-configuration-repository-path config)) + (shell (file-append shadow "/sbin/nologin"))) + (user-group + (name "restic") + (system? #t)))) + +(define restic-server-service-type + (service-type + (name 'restic-server) + (extensions + (list (service-extension shepherd-root-service-type restic-server-service) + (service-extension account-service-type restic-server-accounts))) + (description + "Restic REST server, running as a service user instead of root."))) -- cgit v1.2.3