fec7d62acb
LibreSign had been silently broken since it was first deployed in January. Every step of the old before-starting hook ended in `|| echo`, so six months of failures logged nothing. LibreSign repair - Root cause was a stale config_path: a valid OpenSSL root CA existed at generation 1, a failed CFSSL attempt left an empty generation 2, and config_path was left pointing at the empty one. Regenerated as "Skudak LLP" (was the pre-rename "Skudak Rennsport LLP"). - Deleted the hook. Java/PDFtk/jSignPdf live under data/appdata_*, a persisted volume, so they only ever needed installing once. Install and verification are now explicit tasks that actually fail. - PHP_MEMORY_LIMIT 1024M -- the 512M image default fails opaquely mid-signature. LC_ALL/LANG so the JVM is not ANSI_X3.4-1968. - signature_render_mode=GRAPHIC_ONLY. Any other mode halves the stamp width and overlays a name/date block that collides with the drawn mark and duplicates what our documents already typeset. The value must be exactly GRAPHIC_ONLY; a bare "GRAPHIC" is accepted by occ, matches no radio in the UI, and silently reverts to default. - write_qrcode_on_footer=false, written with --type=boolean because FooterHandler reads it via getValueBool and the typed appconfig API does not coerce a string "0". The validation URL text is kept. - identification_documents=0 -- the default gates signing behind an ID upload plus admin approval, so signers saw no way to sign. - shareapi_restrict_user_enumeration_full_match=no, so an email owned by an existing account can be added as a signer. Root cause is in core (MailPlugin.php:163), not LibreSign. Do NOT set full_match_email=no -- that disables email signer search entirely. Mail branding (skudakmail app) - Two supported extension points, no core patch and no LibreSign fork: mail_template_class for layout, subjects, button labels and the footer LibreSign never adds; and a BeforeMessageSent listener to embed the wordmark as a cid: part so it survives remote-image blocking. - A third listener adds scoped CSS fixing the signing page being clipped on iOS Safari (100vh -> 100dvh). Patched upstream too. - skudakmail-verify.php.j2 asserts all of the above through the real useTemplate() path and fails the play on drift. Every assertion was proven to fail when deliberately regressed. Redis - memcache.locking was unset, so Nextcloud used DBLockingProvider and every file lock became a MariaDB write -- the contention behind the intermittent multi-second stalls. Verified after: db locks static, redis keys growing. - requirepass lives in a mounted 0640 conf, not --requirepass, which would leak it into podman inspect, the systemd unit and ps. The file is chowned to uid 999 because redis-server does not run as root and the :ro mount stops the image fixing it itself. - No maxmemory: cache is evictable, locks are NOT, and evicting a held lock permits concurrent writers to one file. No persistence either -- a restored RDB could reinstate locks whose owner is long dead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
241 lines
8.7 KiB
YAML
241 lines
8.7 KiB
YAML
---
|
|
- import_tasks: firewall.yml
|
|
- import_tasks: podman/podman.yml
|
|
|
|
- import_tasks: podman/podman-prune.yml
|
|
tags: podman-prune
|
|
|
|
# WEB SERVER: Caddy is the default and only web server
|
|
# nginx has been completely replaced and removed
|
|
|
|
# ===== WEB SERVER CONFIGURATION =====
|
|
# Caddy is the default web server
|
|
- import_tasks: containers/base/conf-caddy.yml
|
|
tags:
|
|
- caddy
|
|
- web
|
|
|
|
- import_tasks: containers/base/caddy.yml
|
|
vars:
|
|
image: docker.io/library/caddy:2.11.2
|
|
tags:
|
|
- caddy
|
|
- web
|
|
|
|
# nginx cleanup completed - infrastructure removed
|
|
|
|
|
|
- import_tasks: containers/base/awsddns.yml
|
|
vars:
|
|
image: docker.io/bdebyl/awsddns:1.0.34
|
|
tags: ddns
|
|
|
|
# Drone CI infrastructure completely removed
|
|
|
|
- import_tasks: containers/home/hass.yml
|
|
vars:
|
|
image: ghcr.io/home-assistant/home-assistant:2026.5.1
|
|
tags: hass
|
|
|
|
- import_tasks: containers/home/partsy.yml
|
|
vars:
|
|
image: "git.debyl.io/debyltech/partsy:latest"
|
|
tags: partsy
|
|
|
|
- import_tasks: containers/skudak/partsy.yml
|
|
vars:
|
|
image: "git.debyl.io/debyltech/partsy:latest"
|
|
tags: skudak, partsy-skudak
|
|
|
|
- import_tasks: containers/skudak/wiki.yml
|
|
vars:
|
|
db_image: docker.io/library/mysql:5.7.21
|
|
image: docker.io/solidnerd/bookstack:26.3.4
|
|
tags: skudak, skudak-wiki
|
|
|
|
- import_tasks: containers/home/photos.yml
|
|
vars:
|
|
db_image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
|
|
ml_image: ghcr.io/immich-app/immich-machine-learning:v3.0.0
|
|
redis_image: docker.io/redis:6.2-alpine@sha256:eaba718fecd1196d88533de7ba49bf903ad33664a92debb24660a922ecd9cac8
|
|
image: ghcr.io/immich-app/immich-server:v3.0.0
|
|
tags: photos
|
|
|
|
- import_tasks: containers/home/cloud.yml
|
|
vars:
|
|
db_image: docker.io/library/mariadb:10.6
|
|
image: docker.io/library/nextcloud:34.0.2-apache
|
|
tags: cloud
|
|
|
|
- import_tasks: containers/skudak/cloud.yml
|
|
vars:
|
|
db_image: docker.io/library/mariadb:10.6
|
|
redis_image: docker.io/redis:8.2-alpine
|
|
image: docker.io/library/nextcloud:34.0.2-apache
|
|
tags: skudak, skudak-cloud
|
|
|
|
- import_tasks: containers/debyltech/fulfillr.yml
|
|
vars:
|
|
image: git.debyl.io/debyltech/fulfillr:20260728.2155
|
|
tags: debyltech, fulfillr
|
|
|
|
# Staging back-office (fulfillr-dev.debyltech.com) — same image, staging Turso config.
|
|
- import_tasks: containers/debyltech/fulfillr-dev.yml
|
|
vars:
|
|
image: git.debyl.io/debyltech/fulfillr:20260728.2155
|
|
tags: debyltech, fulfillr-dev
|
|
|
|
- import_tasks: containers/debyltech/uptime-kuma.yml
|
|
vars:
|
|
image: docker.io/louislam/uptime-kuma:2.3.2
|
|
tags: debyltech, uptime-debyltech
|
|
|
|
- import_tasks: containers/home/uptime-kuma.yml
|
|
vars:
|
|
image: docker.io/louislam/uptime-kuma:2.3.2
|
|
tags: home, uptime
|
|
|
|
- import_tasks: data/geoip.yml
|
|
tags: graylog, geoip
|
|
|
|
- import_tasks: containers/debyltech/graylog.yml
|
|
vars:
|
|
mongo_image: docker.io/mongo:7.0
|
|
opensearch_image: docker.io/opensearchproject/opensearch:2
|
|
image: docker.io/graylog/graylog:7.0.1
|
|
tags: debyltech, graylog
|
|
|
|
- import_tasks: containers/home/gregtime.yml
|
|
vars:
|
|
image: localhost/greg-time-bot:3.10.0
|
|
tags: gregtime
|
|
|
|
- import_tasks: containers/home/zomboid.yml
|
|
vars:
|
|
image: docker.io/cm2network/steamcmd:root
|
|
tags: zomboid
|
|
|
|
# ---------------------------------------------------------- Gitea backups
|
|
# The Gitea pods themselves are owned by roles/git, but the backup machinery
|
|
# (containers/cloud-backup.yml plus templates/nextcloud/*) lives here, and an
|
|
# include_tasks reaching across roles would need a path outside the role. So
|
|
# the two Gitea backup instances are wired here alongside the container ones.
|
|
#
|
|
# Both run as ROOTLESS podman under "{{ git_user }}", not "{{ podman_user }}"
|
|
# -- hence backup_podman_user -- and use PostgreSQL rather than MariaDB.
|
|
#
|
|
# Scheduled ahead of the 04:00/04:30 Nextcloud runs so everything lands before
|
|
# the 05:00 TrueNAS snapshot.
|
|
# `apply` is required: tags on a dynamic include_tasks select whether the
|
|
# include runs, but do NOT propagate to the tasks inside it, so without this
|
|
# `make deploy TAGS=gitea-backup` includes the file and then filters out every
|
|
# task in it. The Nextcloud instances avoid this only because they are reached
|
|
# through a static import_tasks chain that tags their children at parse time.
|
|
- include_tasks:
|
|
file: containers/cloud-backup.yml
|
|
apply:
|
|
tags: gitea-backup
|
|
vars:
|
|
backup_name: gitea-debyl
|
|
backup_product: Gitea
|
|
backup_podman_user: "{{ git_user }}"
|
|
data_path: "{{ git_home }}/volumes/gitea/data"
|
|
db_container: gitea-debyl-postgres
|
|
backup_db_type: postgres
|
|
ssh_key_path: /etc/ssh/backup_keys/gitea
|
|
ssh_key_content: "{{ gitea_backup_ssh_key }}"
|
|
ssh_user: gitea
|
|
remote_path: /mnt/glacier/gitea
|
|
script_path: /usr/local/bin/gitea-backup.sh
|
|
# actions_log/artifacts are CI churn (510 MB and growing) and rebuildable;
|
|
# indexers/queues/tmp are derived state Gitea recreates on start. Note the
|
|
# default excludes are Nextcloud-specific, so this must be set explicitly.
|
|
backup_rsync_excludes: >-
|
|
--exclude '/gitea/actions_log' --exclude '/gitea/actions_artifacts'
|
|
--exclude '/gitea/tmp' --exclude '/gitea/indexers' --exclude '/gitea/queues'
|
|
backup_oncalendar: "*-*-* 03:30:00"
|
|
tags: gitea-backup
|
|
|
|
# ------------------------------------------------- Skudak app-data backups
|
|
# BookStack (wiki.skudak.com) and partsy-skudak are BUSINESS data. Both share
|
|
# one TrueNAS dataset (/mnt/glacier/skudakapps) so they need only one backup
|
|
# user, key and cloud-sync task between them; the personal "iDrive E2 Backup"
|
|
# task excludes /skudakapps/** and Skudak's own task pushes it to backup-all.
|
|
#
|
|
# Scheduled ahead of the 03:30+ Gitea/Nextcloud jobs and the 05:00 snapshot.
|
|
- include_tasks:
|
|
file: containers/cloud-backup.yml
|
|
apply:
|
|
tags: [skudak, skudak-apps-backup]
|
|
vars:
|
|
backup_name: bookstack
|
|
backup_product: BookStack
|
|
data_path: "{{ bookstack_path }}"
|
|
db_container: bookstack-db
|
|
# mysql:5.7 predates the mariadb-dump alias -- see cloud-backup.sh.j2.
|
|
backup_db_type: mysql
|
|
ssh_key_path: /etc/ssh/backup_keys/skudakapps
|
|
ssh_key_content: "{{ skudakapps_backup_ssh_key }}"
|
|
ssh_user: skudakapps
|
|
remote_path: /mnt/glacier/skudakapps/bookstack
|
|
script_path: /usr/local/bin/bookstack-backup.sh
|
|
# The wiki content is the DATABASE; /mysql is its raw datadir, which must
|
|
# not be rsynced live -- the dump above is the consistent copy. public/
|
|
# and storage/ hold the uploads and are the only file trees worth shipping.
|
|
backup_rsync_excludes: "--exclude '/mysql'"
|
|
backup_oncalendar: "*-*-* 03:00:00"
|
|
tags: skudak, skudak-apps-backup
|
|
|
|
- include_tasks:
|
|
file: containers/cloud-backup.yml
|
|
apply:
|
|
tags: [skudak, skudak-apps-backup]
|
|
vars:
|
|
backup_name: partsy-skudak
|
|
backup_product: Partsy
|
|
data_path: "{{ partsy_skudak_path }}"
|
|
# Live WAL-mode SQLite: snapshotted via `sqlite3 .backup` rather than
|
|
# rsynced, so the -wal/-shm sidecars are deliberately excluded from the
|
|
# file tree -- shipping them alongside a separately-taken snapshot would
|
|
# only invite a confusing restore.
|
|
backup_sqlite_dbs:
|
|
- "{{ partsy_skudak_path }}/data/partsy.db"
|
|
backup_rsync_excludes: "--exclude '*-wal' --exclude '*-shm'"
|
|
ssh_key_path: /etc/ssh/backup_keys/skudakapps
|
|
ssh_key_content: "{{ skudakapps_backup_ssh_key }}"
|
|
ssh_user: skudakapps
|
|
remote_path: /mnt/glacier/skudakapps/partsy-skudak
|
|
script_path: /usr/local/bin/partsy-skudak-backup.sh
|
|
backup_oncalendar: "*-*-* 03:10:00"
|
|
tags: skudak, skudak-apps-backup
|
|
|
|
# BUSINESS data. Rsynced to TrueNAS here, then pushed offsite to SKUDAK'S OWN
|
|
# iDrive e2 account (bucket `backup-all`) by TrueNAS cloud-sync task "Skudak
|
|
# iDrive - Gitea" (id 9, /mnt/glacier/skudakgit -> /skudakgit, daily 06:30).
|
|
#
|
|
# The personal "iDrive E2 Backup" task's /skudakgit/** exclude is PERMANENT:
|
|
# it is what keeps business data out of personal storage. Do not remove it --
|
|
# Skudak has its own task and bucket instead.
|
|
- include_tasks:
|
|
file: containers/cloud-backup.yml
|
|
apply:
|
|
tags: [skudak, gitea-backup-skudak]
|
|
vars:
|
|
backup_name: skudak-gitea
|
|
backup_product: Gitea
|
|
backup_podman_user: "{{ git_user }}"
|
|
data_path: "{{ git_home }}/volumes/gitea-skudak/data"
|
|
db_container: gitea-skudak-postgres
|
|
backup_db_type: postgres
|
|
ssh_key_path: /etc/ssh/backup_keys/skudak-gitea
|
|
ssh_key_content: "{{ skudakgit_backup_ssh_key }}"
|
|
ssh_user: skudakgit
|
|
remote_path: /mnt/glacier/skudakgit
|
|
script_path: /usr/local/bin/skudak-gitea-backup.sh
|
|
backup_rsync_excludes: >-
|
|
--exclude '/gitea/actions_log' --exclude '/gitea/actions_artifacts'
|
|
--exclude '/gitea/tmp' --exclude '/gitea/indexers' --exclude '/gitea/queues'
|
|
backup_oncalendar: "*-*-* 03:45:00"
|
|
tags: skudak, gitea-backup-skudak
|
|
|