aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Wilken2024-02-25 21:29:43 +0100
committerTimo Wilken2024-02-25 23:36:55 +0100
commit0c6999726c122ad9c3b89b1ed4e674017b1aeec4 (patch)
tree84e3bd881cf49aa8ca87b9159baaa06cd411bd78
parentb412b0e44c540353481762430b946741872e88f4 (diff)
Add Paperless-ngx service
-rwxr-xr-xregenerate-secrets.sh5
-rw-r--r--tw/services/docker.scm10
-rw-r--r--tw/services/paperless.scm117
-rw-r--r--tw/system/files/paperless-secret-key.enc8
-rw-r--r--tw/system/files/restic/lud-paperless.enc7
-rw-r--r--tw/system/lud.scm22
6 files changed, 164 insertions, 5 deletions
diff --git a/regenerate-secrets.sh b/regenerate-secrets.sh
index 979163f6..210b03ae 100755
--- a/regenerate-secrets.sh
+++ b/regenerate-secrets.sh
@@ -34,6 +34,7 @@ for repo in vin-grafana timo-laptop timo-framework timo-battleship timo-phone ti
store_restic vin $repo
done
+store_restic lud lud-paperless
store_restic lud lud-nextcloud
store_restic lud lud-git
@@ -48,3 +49,7 @@ encto vin tw/system/files/grafana/metrics-credentials.enc << EOF
GF_METRICS_BASIC_AUTH_USERNAME=$(pass show computers/vin/grafana/metrics | sed -rn '/^username: /s///p')
GF_METRICS_BASIC_AUTH_PASSWORD=$(pass show computers/vin/grafana/metrics | head -1)
EOF
+
+encto lud tw/system/files/paperless-secret-key.enc << EOF
+PAPERLESS_SECRET_KEY=$(pass show computers/lud/paperless/secret-key | head -1)
+EOF
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 <docker-container-configuration>
@@ -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 <paperless-configuration> (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 <paperless-configuration> (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 <paperless-configuration> (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 <paperless-configuration> (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
@@ -278,6 +290,12 @@ innodb_io_capacity = 4000
(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"))
(secret