From 0c6999726c122ad9c3b89b1ed4e674017b1aeec4 Mon Sep 17 00:00:00 2001 From: Timo Wilken Date: Sun, 25 Feb 2024 21:29:43 +0100 Subject: Add Paperless-ngx service --- tw/services/docker.scm | 10 ++- tw/services/paperless.scm | 117 +++++++++++++++++++++++++++++++ tw/system/files/paperless-secret-key.enc | 8 +++ tw/system/files/restic/lud-paperless.enc | 7 ++ tw/system/lud.scm | 22 +++++- 5 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 tw/services/paperless.scm create mode 100644 tw/system/files/paperless-secret-key.enc create mode 100644 tw/system/files/restic/lud-paperless.enc (limited to 'tw') diff --git a/tw/services/docker.scm b/tw/services/docker.scm index 8361991f..46c7a933 100644 --- a/tw/services/docker.scm +++ b/tw/services/docker.scm @@ -61,7 +61,9 @@ following run.") (docker-args (list-of-strings '()) "Extra command-line arguments to pass to @code{docker run}.") (docker-cli (package docker-cli) "The package containing the Docker -executable to use.")) +executable to use.") + (extra-requirements (list-of-symbols '()) "Any Shepherd services on the host +system that this container relies on.")) (define (docker-container-shepherd-service config) (match-record config @@ -75,7 +77,8 @@ executable to use.")) read-only-root? remove-after-stop? docker-args - docker-cli) + docker-cli + extra-requirements) (let ((docker-run-args `(,@(if read-only-root? '("--read-only") '()) @@ -100,7 +103,8 @@ executable to use.")) (shepherd-service (provision (list (string->symbol (string-append "docker-container-" (maybe-value name image))))) - (requirement `(dockerd ,@(if (string=? network-type "none") '() '(networking)))) + (requirement `(dockerd ,@(if (string=? network-type "none") '() '(networking)) + ,@extra-requirements)) (documentation (format #f "Run a Docker container called ~s from the image ~s." (maybe-value name) image)) (start #~(lambda () diff --git a/tw/services/paperless.scm b/tw/services/paperless.scm new file mode 100644 index 00000000..6db99f65 --- /dev/null +++ b/tw/services/paperless.scm @@ -0,0 +1,117 @@ +(define-module (tw services paperless) + #:use-module (gnu) + #:use-module ((gnu packages admin) #:select (shadow)) + #:use-module (gnu services) + #:use-module (gnu services configuration) + #:use-module (gnu services databases) + #:use-module (guix records) + #:use-module (tw services docker) + #:use-module (tw services restic) + #:use-module (tw services web) + #:export (paperless-service-type + paperless-configuration)) + +(define %paperless-user "paperless") +(define %paperless-uid 481) ; randomly chosen to avoid collisions + +(define-configuration/no-serialization paperless-configuration + (container (string "ghcr.io/paperless-ngx/paperless-ngx:2.5") "Container image to run.") + (domain (string "localhost") "The external domain which will resolve to this +Grafana instance.") + (bind-address (string "127.0.0.1") "The host IP to bind to.") + (data-path (string "/var/lib/paperless") "The path to store data in, on the host.") + (secret-key-file string "A file name containing a @code{PAPERLESS_SECRET_KEY=...} + assignment. This is used to create session tokens, and must be changed from +the default. Make sure this file is readable only by the root user.")) + +(define (paperless-accounts config) + (list (user-account + (name %paperless-user) + (uid %paperless-uid) + (group %paperless-user) + (comment "Paperless server user") + (system? #t) + (home-directory (paperless-configuration-data-path config)) + (shell (file-append shadow "/sbin/nologin"))) + (user-group + (name %paperless-user) + (id %paperless-uid) + (system? #t)))) + +(define (paperless-environment config) + (match-record config (domain bind-address) + (plain-file "paperless.env" (string-append "\ +PAPERLESS_BIND_ADDR=" bind-address " +PAPERLESS_URL=https://" domain " +PAPERLESS_OCR_LANGUAGES=eng deu fra por +PAPERLESS_TIME_ZONE=Europe/Berlin +PAPERLESS_UID=" (number->string %paperless-uid) " +PAPERLESS_GID=" (number->string %paperless-uid) " +")))) + +(define (paperless-docker-service config) + (match-record config (container data-path secret-key-file) + (list (docker-container-configuration + (name "paperless") + (image container) + (read-only-root? #f) ; wrapper script runs "apt" in container + (volumes + ;; We need to mount each subdir separately because the container + ;; image specifies volumes to be mounted there otherwise. + (map (lambda (subdir) + (list (string-append data-path "/" subdir) + (string-append "/usr/src/paperless/" subdir) + #t)) + '("consume" "data" "export" "media"))) + (environment-files + (list (paperless-environment config) secret-key-file)) + (network-type "host") + ;; Paperless connects to redis://localhost:6379 (default port) by default. + (extra-requirements '(redis)))))) + +(define (paperless-reverse-proxy config) + (match-record config (domain bind-address) + (if (string=? domain "localhost") (list) + (list (https-reverse-proxy-configuration + (domains (list domain)) + ;; The container runs on port 8000 and has a health check with + ;; that port hardcoded, so just use that. + (destination-port 8000) + (destination-ip + (if (string=? bind-address "0.0.0.0") + "127.0.0.1" + bind-address))))))) + +(define %paperless-backup-repo + (restic-local-repository (path "/var/backups/paperless"))) +(define %paperless-backup-password + (restic-password-source + (type 'file) + (name "/etc/restic/lud-paperless"))) + +(define (paperless-backups config) + (match-record config (data-path) + (list (restic-scheduled-backup + (schedule #~"0 5 * * *") + (paths (list data-path)) + (repo %paperless-backup-repo) + (password %paperless-backup-password))))) + +(define (paperless-backup-cleanup config) + (list (restic-scheduled-cleanup + (schedule #~"0 12 * * *") + (repo %paperless-backup-repo) + (password %paperless-backup-password) + (keep-daily 30) + (keep-monthly -1)))) + +(define paperless-service-type + (service-type + (name 'paperless) + (extensions + (list (service-extension docker-container-service-type paperless-docker-service) + (service-extension account-service-type paperless-accounts) + (service-extension https-reverse-proxy-service-type paperless-reverse-proxy) + (service-extension restic-backup-service-type paperless-backups) + (service-extension restic-cleanup-service-type paperless-backup-cleanup))) + (description "Paperless server, running under Docker."))) diff --git a/tw/system/files/paperless-secret-key.enc b/tw/system/files/paperless-secret-key.enc new file mode 100644 index 00000000..ab11cf0c --- /dev/null +++ b/tw/system/files/paperless-secret-key.enc @@ -0,0 +1,8 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHBESlBiZyBUdkVy +OXBIRHVKZ01McXVkU1IzbDRCeDRMcU95TnRqSEJ0VHNWOEJGbTEwCkZnME1wR2Vn +TVVNSldva0dFdDFJcFNxVG9rSHdQYi9aUWQ5U1hsbnRVdmMKLS0tIEV5d21GcGVI +aHROU0IxdGFwRXlEVlNQV1NNdzRHQnlTcWF1YnNsM0ZuUlkKZiNsYpphMWqTzUFO +8zfIk3cmPuuoSUh8D+xlNzhzX/7gu0rM1iFabIqj7ucmwf1wSoNr/29jcsSP9RC2 +/zF3JiSKqTM/5A== +-----END AGE ENCRYPTED FILE----- diff --git a/tw/system/files/restic/lud-paperless.enc b/tw/system/files/restic/lud-paperless.enc new file mode 100644 index 00000000..135e0cef --- /dev/null +++ b/tw/system/files/restic/lud-paperless.enc @@ -0,0 +1,7 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHBESlBiZyA5Zk11 +aVNZQ28zaldNZXYrOXVsd290aGRUcDFXQmdoaU8xV3dNa2Ivb0c4CnM3c2VUdDRK +MlBqMndKQ24vcUE4TFNubExJWGE1MU5FWDdjSnp5bm5sMWsKLS0tIGZaWWp0NjNj +cm5zMGVnNkpOL3pmTVhnU1Z2di9tOXd2SWhuUTZucHVQVmcK7sCThALV4gOc08rT +oFB2deLCs1tcp2bOEhWSGtYwTqm+KGIVuS0MeJ4b9aV9OtyLWw== +-----END AGE ENCRYPTED FILE----- diff --git a/tw/system/lud.scm b/tw/system/lud.scm index dd0d39a9..d6d60be6 100644 --- a/tw/system/lud.scm +++ b/tw/system/lud.scm @@ -9,6 +9,7 @@ #:use-module (tw services nextcloud) #:use-module (tw services matrix) #:use-module (tw services media) + #:use-module (tw services paperless) #:use-module (tw services restic) #:use-module (tw services secrets) #:use-module (tw services web) @@ -17,8 +18,9 @@ (use-package-modules acl admin bash certs databases guile-xyz linux man php python python-xyz rsync shells tls tor version-control video) -(use-service-modules certbot cgit databases file-sharing mcron monitoring - networking pm ssh syncthing version-control vpn web) +(use-service-modules certbot cgit databases dbus desktop docker file-sharing + mcron monitoring networking pm ssh syncthing + version-control vpn web) (define efi-system-partition ; /dev/sda1 (uuid "51F3-FB71" 'fat32)) @@ -82,6 +84,16 @@ (host-name host-name) (ipv6? #f))) ; currently broken + (service dbus-root-service-type) ; for Docker + (service elogind-service-type) ; for Docker + (service docker-service-type) ; for Paperless + (service redis-service-type) ; for Paperless + (service paperless-service-type + (paperless-configuration + (domain "paper.wilkenfamily.de") + (data-path "/var/data/paperless") + (secret-key-file "/etc/paperless/secret-key"))) + ;; Allow anonymous git access via Wireguard, e.g. to this channel's git repo. ;; Repos are only published if they contain a `git-daemon-export-ok' file. (service git-daemon-service-type @@ -277,6 +289,12 @@ innodb_io_capacity = 4000 (secrets-configuration (secrets (list + (secret + (encrypted-file (local-file "files/paperless-secret-key.enc")) + (destination "/etc/paperless/secret-key")) + (secret + (encrypted-file (local-file "files/restic/lud-paperless.enc")) + (destination "/etc/restic/lud-paperless")) (secret (encrypted-file (local-file "files/mythic-dns.scm.enc")) (destination "/etc/mythic-dns.scm")) -- cgit v1.2.3