#1259 moved the interim CVE-2026-109618 mitigation (memory_limit = -1)
out of the shared hardening.ini into memory-unlimited.ini and mounted it
only in the roles whose images it had inventoried. Its branch predates
#1263, which gave FusionDirectory the php_hardening mount, so after both
merged FusionDirectory ran with the image default of 128M again.
A conf.d mount would not have fixed it: the nfrastack image writes
php_admin_value[memory_limit] into its FPM pool
(/etc/php7/pools.d/www/www-settings.conf) from PHP_MEMORY_LIMIT, and a
pool php_admin_value outranks every conf.d ini. Measured on main: CLI
128M, FPM 128M. So even before #1259 the hardening ini only ever lifted
the CLI limit.
nfrastack/fusiondirectory:1.5 runs PHP 7.4.33 on Alpine 3.15, end of
life and never patched, so the role carries the mitigation until it
moves to a PHP 8 image.
- Set PHP_MEMORY_LIMIT=-1 in templates/env.j2. The image seds it into
php.ini and the pool, so CLI and FPM both report -1.
- The root TODO lists FusionDirectory with the image and the reason it
uses the variable instead of the mount; the role TODO names which
setting carries which CVE mitigation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New roles/sys-svc-container/templates/env.j2 sets TZ={{ HOST_TIMEZONE }} and LANG=C.UTF-8. Every role env.j2 includes it at the top; it renders via stack_host_template, the same engine that resolves the includes in compose.yml.j2, so it works in both compose and swarm. TZ/LANG are removed from the individual role env.j2 so each value stays single-source.
New lint tests/lint/ansible/test_env_base_include.py verifies every role env.j2 includes the base and never re-sets a base-owned key (keys derived from the base file).
mattermost and gitea meta/volumes.yml drop the /etc/timezone host-path bind: swarm hard-rejects a task whose bind source is missing on the node (Debian-trixie nodes have no /etc/timezone). TZ now comes from the base env. /etc/localtime bind also dropped; the container timezone follows TZ.
Non-directive comments were removed from the touched env.j2 (banners, restatements, reference URLs, narration) so the changed files pass the comment-lint; only tool directives remain.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The nfrastack/fusiondirectory image (1.5-3.0.0) initializes LDAP
profiles in `/container/functions/40-fusiondirectory` via
`create_profile(i)` with `i=$(printf "%02d" $i)`, so profile 1 is
expected as `LDAP01_*`. The env.j2 template was rendering `LDAP1_*`
(single digit), which the init script never consumed:
$ docker logs fusiondirectory
** [ERROR] ** [fusiondirectory] No 'Server Name' Entered\! - Set '$LDAP01_NAME'
** ERROR - Some initialization scripts haven't completed:
40-fusiondirectory
The container then ran without an LDAP binding, so the application
itself returned `FATAL: Error when connecting to LDAP server
"ldap://openldap:"` (with a dangling colon because `LDAP01_PORT`
was also missing).
In variant 0 the failure stayed invisible because oauth2-proxy
fronts the fd vhost and intercepts every unauthenticated request
with a redirect to Keycloak — the reachability probe got `200 OK`
from the proxy even though the upstream was dead. Variant 1
disables oauth2, the probe hits fd directly, and the matrix-deploy
aborts with `502 Bad Gateway`.
Two changes:
- rename `LDAP1_*` to `LDAP01_*` for every LDAP env-var the role
exposes, matching the image's zero-padded convention.
- add the missing `LDAP01_PORT` from `svc-db-openldap`'s
`services.openldap.ports.local.ldap` so the rendered URI carries
a port (`ldap://openldap:389` instead of `ldap://openldap:`).
Verified in-container: after patching the live `.env` with the new
names + port and `docker compose up -d --force-recreate
fusiondirectory`, the container reports `healthy` and
`wget https://fd.infinito.example/` returns `200 OK` with
`<title>FusionDirectory - Sign in</title>`. Before the fix the
same probe was `502` (variant 1) or a Keycloak login page hiding
the broken upstream (variant 0).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The administrator password literally starts with `'` (verified via cat
on the rendered .env/env: `FUSIONDIRECTORY_ADMIN_PASS='$tr0ng€xampl3PW\!`),
which Docker Compose's .env parser interprets as an opening single
quote and fails with `unterminated quoted value`. Wrapping both
credential fields with the existing `| dotenv_quote` filter
(plugins/filter/dotenv.py — double-quote + escape `$`/`\\`/`"`) makes
the value compose-safe. Matches the pattern used in
web-app-{mastodon,listmonk}/templates/env.j2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
40-fusiondirectory init script (sanity_var checks at lines 354-358 of
/etc/cont-init.d/40-fusiondirectory in the image) requires the numbered
LDAP1_* names because the script supports multiple LDAP servers via a
`for (( i = 1; i <= NUM; i++ ))` loop. The previous LDAP_* names caused
`sanity_var LDAP1_ADMIN_PASS "Admin Password"` to abort init, leaving
the container in an unhealthy state.
Also moves the `environment:` block out of compose.yml.j2 into the
canonical `templates/env.j2`; the env_file is auto-wired via
sys-svc-container/templates/base.yml.j2 once `has_env()` is true.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace usages of the get_app_conf filter with the config lookup plugin
across roles and templates. Adjust quoting where required to keep YAML
and Jinja syntax valid. No functional changes intended.
https://chatgpt.com/share/69860678-cef8-800f-8402-7fe6231fdc2d
- Converted group_vars/all/13_ldap.yml from lower-case to ALL-CAPS nested keys.
- Updated all roles, tasks, templates, and filter_plugins to reference LDAP.* instead of ldap.*.
- Fixed Keycloak JSON templates to properly quote Jinja variables.
- Adjusted svc-db-openldap filter plugins and unit tests to handle new LDAP structure.
- Updated integration test to only check uniqueness of TOP-LEVEL ALL-CAPS constants, ignoring nested keys.
See: https://chatgpt.com/share/68b01017-efe0-800f-a508-7d7e2f1c8c8d