opnform_app_key, opnform_jwt_secret, opnform_front_api_secret and
opnform_db_password shipped as real base64 strings in defaults — they
look like production secrets that just happen to be public. Set all
four to '' and rely on the existing Validate task (and the new
argument_specs marking them required) to fail fast when an inventory
forgets to override them.
Mirror the docstring comment to show how to generate each one with
openssl.
* vars/main.yml: header was 'vars file for homarr' (copy-paste from the
homarr role). Fixed to 'vars file for opnform'. File body is empty.
* meta/main.yml: replace ansible-galaxy init boilerplate with real
metadata — author, description, license (MIT-0), min_ansible_version
set to '2.15' as a string (galaxy schema requires str), galaxy_tags
for discovery, and an empty dependencies list.
The third inline finding (dead roles/opnform/templates/compose.yml.j2)
is resolved by dropping the WIP commit a6f301e during the rebase rather
than removing it in a separate commit — the file no longer exists in
the rebased history.
Rename roles/OpnForm → roles/opnform so the role resolves as
digitalboard.core.opnform (Ansible collection convention is
lowercase). Update tests/test.yml reference accordingly.
Add automated admin user creation via POST /api/register, gated on
opnform_admin_email + opnform_admin_password. Idempotent through a
prior login probe. Without these vars the manual setup page flow is
preserved.
Add automated OIDC IdentityConnection setup via the per-workspace
/api/open/workspaces/{id}/oidc-connections endpoint, gated on
opnform_oidc_enabled. Hard-coupled to the admin bootstrap (the API
requires an authenticated admin token); validation block fails fast
if OIDC is enabled without admin credentials. Supports both an
explicit opnform_oidc_group_role_mappings list and a fallback
opnform_oidc_admin_group convenience var.
Convert opnform_oidc_scopes from space-separated string to YAML list
to match OpnForm's API expectation. Rewrite README "First login" and
"OIDC setup" sections to reflect that self-hosted OpnForm does not
ship a pre-seeded admin and to document the new bootstrap paths.
BREAKING CHANGE: opnform_oidc_scopes changed from space-separated
string to YAML list. Inventories that override it must update from
"openid profile email" to [openid, profile, email].
Each of the five roles touched in this branch now ships:
* meta/argument_specs.yml: typed schema for every variable in
defaults/main.yml plus the optional inputs surfaced via this
branch (traefik_extra_hosts, authentik_host_rewrite_domains,
authentik_proxy_apps.mode / .allowed_groups, drawio_extra_domains,
drawio_authentik_forward_auth*, garage_webui_authentik_forward_auth*).
All five specs load cleanly through ansible-core's
ArgumentSpecValidator.
* README.md: replaces the ansible-galaxy boilerplate (where it was
still in place) with a focused write-up — service vars, required
secrets, ForwardAuth/idempotency notes, dependencies, and a working
example playbook. authentik and garage READMEs are rewritten to cover
the new knobs while preserving their existing content.
Add drawio_extra_domains (list, default empty). The traefik Host rule
on the drawio router now expands to Host(<canonical>) || Host(<extra>)
... so the same container can answer on additional FQDNs — e.g. an
internal *.int.* name so a DMZ reverse-proxy can reach drawio via a
backend hostname covered by the local traefik cert.
Empty by default; behaviour unchanged for existing inventories.
nextcloud/server#59629: under PHP 8.x with OPcache,
UserConfig::getValueBool() passes a non-string from getTypedValue()
straight into strtolower(), throwing a TypeError on every authenticated
request once user_ldap is involved. Fix landed in master (PR #59646)
but no stable33 backport made it into 33.0.4.
Discover all compose-managed nextcloud containers, check whether the
`strtolower((string)` cast is already present, and `sed` it into
`lib/private/Config/UserConfig.php` on the ones that still ship the
broken version. Idempotent via grep guard so re-runs are no-ops.
Remove this block once the deployed image >= 33.0.4 ships the upstream fix.
Every `occ config:app:set` / `ldap:set-config` / `notify_push:setup`
call previously fired on every play, marking changed even when the
stored value already matched. Now we read the current value first and
only invoke the setter when it differs:
* richdocuments (collabora): pre-read wopi_url, public_wopi_url,
disable_certificate_verification, wopi_allowlist into a fact map;
guard each `config:app:set` and tag `richdocuments:activate-config`
with `changed_when: false` since it's a discovery refresh.
* drawio: same pattern for DrawioUrl, DrawioTheme, DrawioOffline,
comparing as strings (occ stores booleans as "1"/"0").
* user_ldap: pre-read `ldap:show-config s01 --output=json`, parse JSON
defensively (occ logs interleave on stderr), and skip per-key
`ldap:set-config` calls when the stored value already equals the
desired one.
* notify_push: skip `notify_push:setup` when the stored base_endpoint
already matches the computed URL.
* plugins: `app:install`/`app:enable` were treating "already installed/
enabled" output as a change. Add the negative match to `changed_when`
so re-runs of a fully-provisioned site report ok rather than changed.
* bootstrap: `garage layout show` truncates node IDs to 16 chars, but
the membership check compared against the full hex. After the first
successful join, subsequent runs no longer found the short ID in
`layout show` and re-issued `layout assign`, marking the task
changed every time. Compare against both the truncated and the full
form so a configured node stays detected. Also tag the read-only
`garage node id` / `layout show` probes with `changed_when: false`.
* provision keys: the old parser sliced `stdout_lines[1:]` to drop the
header but missed that INFO log lines and ANSI escapes can interleave
with table rows. Replace with an explicit `^GK[0-9a-fA-F]+` filter
after stripping ANSI, so probe-output noise no longer corrupts the
existing-keys set and triggers spurious `key new` calls.
* provision buckets: same class of fix — match `^[0-9a-f]{16}\s` data
rows instead of slicing `[2:]`, which broke when the table header
wasn't exactly two lines.
* provision permissions: pre-read `bucket info` for each (key, bucket)
pair and only run `bucket allow` when the current `RWO` flag set for
that key ID doesn't already match the desired permissions. Previously
`bucket allow` ran unconditionally and reported changed every play.
* `changed_when: false` on all read-only probes (`key list`, `key info`,
`bucket list`).
Add `*_authentik_forward_auth` + `*_authentik_forward_auth_url` knobs to
both roles. When enabled:
* drawio: traefik attaches a ForwardAuth middleware pointing at the
authentik embedded outpost; unauthenticated requests get redirected
to log in and downstream sees X-Authentik-* identity headers.
* garage WebUI: same ForwardAuth wiring, and `AUTH_USER_PASS` is dropped
from the container env so authentik is the only gate. Tasks now key
the htpasswd hash workflow off `_garage_webui_htpasswd_active`
(`webui_enabled AND NOT authentik_forward_auth`); when authentik
fronts the UI we skip hashing entirely. htpasswd hash is also now
cached on disk and re-verified via `htpasswd -vbB` so unchanged
passwords stop showing as `changed=true` on every run.
Both knobs default to `false`, preserving existing htpasswd/plain behaviour.
* `authentik_host_rewrite_domains`: extra hostnames that reach the
authentik container but make it generate URLs (OIDC issuer, reset
links) as if requested from the canonical `authentik_domains[0]`.
Each entry gets its own traefik router and a URL-based loadbalancer
service that disables passHostHeader and pins X-Forwarded-Host via
middleware, so server-to-server calls on internal FQDNs keep traffic
in the LAN while the iss claim stays aligned with the public host.
Uses a network alias on the canonical FQDN so traefik (sharing the
network) resolves the URL upstream to this very container.
* proxy-app blueprint:
- `mode` (default `forward_single`) lets callers pick between proxy,
forward_single and forward_domain providers in one template.
- `allowed_groups`: when set, emit one PolicyBinding per group on
the application; authentik OR-evaluates bindings, so users in any
listed group pass and others are denied.
Existing inventories with an empty list see no behavioural change.
Add `traefik_extra_hosts` (list of `host:ip`) that maps straight into
the traefik container's compose `extra_hosts`. Needed when a downstream
middleware (e.g. ForwardAuth to authentik on a sibling LAN) has to
resolve a public FQDN to an internal IP because the DMZ doesn't hairpin
the public address back inside.
Empty by default; behaviour unchanged for existing inventories.
- Drop `recreate: always` from collabora/drawio/homarr/opencloud/traefik
handlers and the authentik_outpost_ldap start task. `up -d` with
`state: present` already recreates exactly the services whose
compose definition changed; the blanket recreate was forcing
restarts even when nothing relevant moved.
- Rewrite the `*_domains` Traefik Host loop to the `Host(\`a\`) ||
Host(\`b\`)` form across authentik/collabora/garage/nextcloud so the
rule still matches when traefik can't normalize the comma-form into
the same canonical shape.
- Traefik: add `traefik_acme_tcp_only` (sets LEGO_EXPERIMENTAL_DNS_TCP_ONLY)
and `traefik_acme_disable_ans_checks` (disables lego's authoritative-NS
propagation check) for environments where the DNS path between the
traefik container and the zone's nameservers is constrained.
- Traefik DMZ collector: two-step merge so a `traefik_dmz_exposed_services`
entry that sets its own `backend_host` wins over the host fallback;
lets a route target an internal FQDN covered by the backend cert's
SANs instead of the raw IP.
- Nextcloud: add `nextcloud_notify_push_domain` override for the
`occ notify_push:setup` call so the setup check can hit an internal
FQDN instead of hairpinning through the DMZ. Push router now matches
every entry in `nextcloud_domains`.
- Nextcloud: also %2F-escape slashes in the postgres user/password
inside the notify_push DATABASE_URL.
- Refactor: collapse `*_domain` + `*_extra_domains` into a single
`*_domains` list across authentik, collabora, garage and nextcloud
roles. First entry is the canonical FQDN (used for OVERWRITEHOST,
BASE_URL, notify_push setup and garage root_domain).
- Authentik blueprint: guard the OAuth sources block so an empty
`authentik_login_sources` no longer renders an invalid YAML key.
- Nextcloud: introduce `nextcloud_collabora_public_domain` and set
Collabora's `public_wopi_url` separately from the server-to-server
`wopi_url` so browsers can reach Collabora via the public name while
Nextcloud still talks to it on the internal one.
- Nextcloud: URL-encode the postgres user/password in DATABASE_URL.