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>
41 lines
1.9 KiB
Django/Jinja
41 lines
1.9 KiB
Django/Jinja
# {{ ansible_managed }}
|
|
#
|
|
# Redis for skudak-cloud: Nextcloud distributed cache + transactional file
|
|
# locking. Reachable only by container name on the `shared` podman network --
|
|
# no host port is published.
|
|
#
|
|
# The password lives HERE rather than on the command line as
|
|
# `redis-server --requirepass <pass>`. That is the existing house idiom (see
|
|
# the deleted container-nosql.yml in git history), but it leaks the secret into
|
|
# `podman inspect`, into the generated systemd unit under
|
|
# ~/.config/systemd/user/, and into `ps` for every user on the host. A 0640
|
|
# config file mounted read-only keeps it out of all three.
|
|
requirepass {{ cloud_skudak_redis_pass }}
|
|
|
|
# Bind to all interfaces WITHIN the container's network namespace. The
|
|
# container publishes no port, so this is reachable only from the `shared`
|
|
# podman network -- not from the host and not from the LAN.
|
|
bind 0.0.0.0
|
|
port 6379
|
|
protected-mode yes
|
|
|
|
# NO maxmemory / eviction policy, deliberately.
|
|
#
|
|
# Nextcloud puts BOTH the distributed cache and the transactional file locks in
|
|
# this instance. Cache entries are safely evictable; LOCKS ARE NOT. An
|
|
# `allkeys-lru` policy under memory pressure can evict a lock that a live
|
|
# request still believes it holds, which permits concurrent writers to the same
|
|
# file -- silent corruption rather than a visible error. With no maxmemory,
|
|
# Redis never evicts. The host has ~14 GiB free of 31 GiB and this instance
|
|
# holds a few hundred keys, so a cap buys nothing.
|
|
#
|
|
# If a cap is ever genuinely needed, use `maxmemory-policy noeviction` so Redis
|
|
# returns an error instead of silently discarding a lock.
|
|
|
|
# No persistence. Locks are ephemeral and TTL-bounded, and the cache is
|
|
# rebuildable -- there is nothing here worth surviving a restart. Persisting
|
|
# would be actively worse: a restored RDB could reinstate locks whose owning
|
|
# request died, blocking files until the TTL expired.
|
|
save ""
|
|
appendonly no
|