Merge remote-tracking branch 'origin/main' into wt/20261104-104444
This commit is contained in:
commit
25114ca622
9 files changed
+892
-100
No files matched your search
@@ -148,36 +148,46 @@ Read from the live database (read-only `SELECT`s against the
|
||||
| Object | Value |
|
||||
|---|---|
|
||||
| LDAP provider | `Jellyfin LDAP` (pk 11), base DN `dc=franksdank,dc=cloud`, `search_mode=direct`, `bind_mode=direct` |
|
||||
| Bind DN | `cn=svc-jellyfin-ldap,ou=users,dc=franksdank,dc=cloud` *(unverified 2026-10-18: base DN confirmed in the DB, the `ou=users` path was not probed — an LDAP bind needs the bind secret)* |
|
||||
| Service account | `svc-jellyfin-ldap` (user pk 8). **It is not passwordless**: its password field is a real hash, not Django's `!`-prefixed unusable marker (`starts_with(password,'!')` → `f`) — an earlier revision's "passwordless/unusable" claim is wrong, which matters because an account with a usable password can sign in through the ordinary password flow *(unverified 2026-10-18: whether anyone holds that password)* |
|
||||
| Bind token | exactly one row for pk 8, **`intent=app_password`**, non-expiring — an earlier revision recorded `intent=verification`; the live value is `app_password` |
|
||||
| Bind DN | `cn=svc-jellyfin-ldap,ou=users,dc=franksdank,dc=cloud` — **verified by a live bind 2026-11-04** (the bind and the `memberOf` search both succeed, so the `ou=users` path is right) |
|
||||
| Service account | `svc-jellyfin-ldap` (user pk 8). It has a usable password and authenticates through the default flow — that password is held by Jellyfin in the plugin config (`LdapBindPassword`) and is what the bind actually uses (live bind 2026-11-04) |
|
||||
| Bind token | exactly one row for pk 8, **`intent=app_password`**, non-expiring — and **unused by Jellyfin**, which has no token field (see the rule below) |
|
||||
| RBAC role | `svc-jellyfin-ldap-search`, carrying exactly one object-level permission `authentik_providers_ldap.search_full_directory` (verified via `guardian_roleobjectpermission`) |
|
||||
| Groups | `jellyfin_users`, `jellyfin_admins` (both exist in `authentik_core_group`) |
|
||||
| Application | name `Jellyfin`, **slug `jellyfin-ldap`** (an earlier revision said slug `jellyfin`, which does not exist) |
|
||||
|
||||
Hard-won rules (the first is now contested by the live database):
|
||||
Hard-won rules:
|
||||
|
||||
- **Token intent: contested, do not trust either value blindly.** An earlier
|
||||
revision asserted that a bind token with `intent=app_password` is rejected by
|
||||
the flow's password stage with `Invalid password` and only
|
||||
`intent=verification` passes. The live token for `svc-jellyfin-ldap` is
|
||||
`app_password` (`authentik_core_token`, 2026-10-18), so either the rule is
|
||||
wrong or the current Jellyfin bind path is not that token. Nothing observable
|
||||
settles it today: the LDAP outpost's retained log holds only its 5-minute
|
||||
`initialized direct binder` / `Update providers` config cycles and no bind
|
||||
activity *(unverified 2026-10-18: no bind was performed — reproducing it
|
||||
requires the bind secret)*. Re-verify with a real bind before changing the
|
||||
token's intent either way.
|
||||
- **Token intent is moot for Jellyfin.** The earlier argument (a bind token with
|
||||
`intent=app_password` is rejected by the flow's password stage, only
|
||||
`intent=verification` passes) never applied here: LDAP-Auth sends the service
|
||||
account's **password** from `LdapBindPassword`, not a token. A live bind plus a
|
||||
`memberOf=cn=jellyfin_users` search (11 entries) succeeded from Dankserver on
|
||||
2026-11-04. Leave the token row alone.
|
||||
- **Search needs `search_full_directory`.** Without the RBAC grant the
|
||||
outpost returns only the bind user itself — a `(memberOf=…)` filter then
|
||||
matches zero entries and the plugin logs `Found no users matching X`.
|
||||
The permission is object-level on the provider; grant it via an RBAC role
|
||||
and retest.
|
||||
- **The outpost log never shows a bind.** Its retained journal is only the
|
||||
5-minute `initialized direct binder` / `Update providers` config cycle; the
|
||||
absence of a bind line is not evidence of anything. Instruments that do answer:
|
||||
`ldapsearch` with the plugin's own DN, password and filter, and the authentik
|
||||
event registry (`login` / `login_failed` with `client_ip` from the podman
|
||||
gateway, since the published port SNATs).
|
||||
- `ak shell` import path for the provider model in 2026.8:
|
||||
`from authentik.providers.ldap.models import LDAPProvider`.
|
||||
- (unverified 2026-10-18: importing it outside a configured Django shell fails
|
||||
with `ImproperlyConfigured`, and no `ak shell` was driven against the live
|
||||
auth service for this check)*
|
||||
`from authentik.providers.ldap.models import LDAPProvider` (verified inside a
|
||||
live `ak shell`, 2026-11-04 — it only fails outside a configured Django shell).
|
||||
- **`ak shell` field names, 2026.8:** `Event` is keyed by `created` and `action`
|
||||
— there is no `timestamp` and no `type`; `Outpost` has `providers` but **no
|
||||
`apps`**. Introspect first: `[f.name for f in Model._meta.get_fields()]`.
|
||||
- **Never pipe `ak shell` inside a heredoc.** `podman exec -i … ak shell | grep
|
||||
-v '^{'` binds the heredoc to `grep` (the redirection belongs to the LAST
|
||||
element of the pipeline), so the shell gets no stdin and prints nothing at all.
|
||||
And `2>/dev/null` on `ak shell` eats the traceback, which looks identical to "no
|
||||
rows". Clean pattern: have the script write its results to a file and read that
|
||||
file back —
|
||||
`podman exec -i authentik-server ak shell >/dev/null 2>/dev/null <<'PY' … open("/tmp/x.txt","w") … PY`
|
||||
then `podman exec authentik-server cat /tmp/x.txt`.
|
||||
|
||||
## Self-serve user-settings flow (password change)
|
||||
|
||||
|
||||
@@ -145,18 +145,17 @@ relying-party quirks are reference lookup: [[Authentik Outpost Reference]].
|
||||
The second outpost — `dankclaw/authentik/quadlet/authentik-outpost-ldap.container`
|
||||
→ `authentik-outpost-ldap.service`, one of the five units in this stack —
|
||||
serves LDAP for Jellyfin's LDAP-Auth plugin (`enabled`=generated, `active`
|
||||
`Restart=always`, container up, 2026-10-18). The image is pinned by its **amd64
|
||||
single-platform digest**
|
||||
`ghcr.io/goauthentik/ldap@sha256:2e7b259eb8c37faac916946ddf48ec681ee9d3a9f724fe8521bf161973f13cf9`
|
||||
(`dankclaw/authentik/quadlet/authentik-outpost-ldap.container:26`, matching the
|
||||
running image in `podman ps`, 2026-10-18). The digest in this page before 2026-10-18
|
||||
(`…fa33bc8f…`) is superseded. The reason for pinning a digest at all: podman
|
||||
5.7.0 refuses the plain tag because it resolves to a *nested* manifest list
|
||||
(index → per-platform manifest lists) — `unexpectedly received a manifest list
|
||||
instead of a manifest for a single image` — first hit on `:2026.8.0`, still the
|
||||
rule at the current `2026.8.3` pin. Resolve once with `podman manifest inspect
|
||||
<image> | jq -r '.manifests[] | select(.platform.architecture=="amd64") |
|
||||
.digest'` and pin that digest.
|
||||
`Restart=always`, container up, re-checked 2026-11-04). **The image pin has one
|
||||
owner: the `Image=` line of
|
||||
`dankclaw/authentik/quadlet/authentik-outpost-ldap.container`** — never restate
|
||||
the digest here, because every pin bump turns this page into a lie (the digest
|
||||
cited here went stale twice: `…fa33bc8f…`, then `…2e7b259e…`). The reason a
|
||||
digest is pinned at all: podman 5.7.0 refuses the plain tag because it resolves
|
||||
to a *nested* manifest list (index → per-platform manifest lists) —
|
||||
`unexpectedly received a manifest list instead of a manifest for a single image`
|
||||
— first hit on `:2026.8.0` and still the rule. Resolve once with
|
||||
`podman manifest inspect <image> | jq -r '.manifests[] |
|
||||
select(.platform.architecture=="amd64") | .digest'` and pin that digest.
|
||||
|
||||
- Ports **389** (ldap) and **636** (ldaps) publish on the LAN
|
||||
(`PublishPort=389:3389`, `636:6636`
|
||||
|
||||
Reference in new issue
Block a user