Commit Graph
25 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 c925150848 fix(web-app-openbao): ship the LDAP auth method as an external plugin
OpenBao 2.7 removed the builtin LDAP auth engine from the server binary
(openbao/openbao GH-3882). `bao auth enable ldap` answered
"plugin not found in the catalog: ldap" and the deploy stopped there.

The role now builds its own image. A multi-stage Dockerfile copies the
openbao-plugin-auth-ldap binary out of the image declared as the
`auth-ldap` service entry into /opt/openbao/plugins. A second server
config file, plugins.hcl, names that directory and registers the plugin
declaratively. It sets plugin_auto_register = true explicitly: the image
starts the server with a config directory, and on that path 2.7.1 does
not apply the documented default, so without the line the catalog stays
empty. The Dockerfile sets the file mode in a helper stage instead of
COPY --chmod, which only BuildKit accepts.

The mount is enabled with -plugin-version=latest, so it follows whichever
plugin version the image ships. A mount created by the former builtin
engine is tuned and reloaded onto the plugin, which needs `update` on
sys/plugins/reload/backend in the ansible-admin policy.

The LDAP tasks move into tasks/addons/auth-ldap.yml and the plugin is
declared under meta/addons as a required addon bridged to `ldap`. Its
Playwright spec logs in through the mount and asserts that sys/auth
reports an external running plugin version.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 13:25:17 +02:00
evangelostsak e06a62e19b style(openbao): drop the narrative exception comments
Review had these removed across the role once already, and the 11 September
pass cleared every file that was flagged. The seal-key pin and the
recovery-key work then reintroduced the same pattern in nine new places.

Also removes two that predate the review and were never flagged: the
healthcheck note in meta/services.yml and the entrypoint note in
compose.yml.j2. Both are the same narrative-exception shape.

Param docs, file headers and the plain contract notes in the Playwright specs
stay: separate categories, none of them objected to. What the deleted
comments recorded lives in the role README.
2026-09-21 00:29:15 +02:00
evangelostsak f1c217b685 feat(openbao): keep the recovery key and fall back to it
bao operator init issued a recovery key on the first deploy and the play read
only root_token from the JSON, dropping it. That left the AppRole as the sole
administrative path, so losing both halves of it ended the deploy for good on
an instance whose data was still readable.

The key is now persisted at init and loaded before the logins that may need
it. When neither the inventory AppRole nor the recorded one is accepted, the
run rebuilds the AppRole from it instead of failing. The hard fail remains for
the case where there is no key either.

The three first-deploy tasks move into a block: a third sibling sharing the
same when is what test_no_repeated_when_runs rejects.
2026-09-20 01:22:46 +02:00
evangelostsak 7affa3d163 feat(openbao): add a break-glass path through the recovery key
Mints a root token with bao operator generate-root and hands over to
02_auth.yml, which re-pins both AppRole halves to the inventory values.

The four protocol steps run as separate tasks because the token comes back
XOR-wrapped with a one-time password and the image carries neither python nor
jq, so the CLI performs each step and Ansible parses the JSON -- the same
split the rest of the role already uses for bao status.
2026-09-20 01:08:56 +02:00
evangelostsak fcfff81f64 docs(openbao): correct what the rotation record covers
The header claimed every generated credential rotates per deploy. The seal
key no longer does; it is recorded for the deploy that follows an operator
changing it.
2026-09-19 20:58:25 +02:00
evangelostsak b7a1223481 feat(openbao): re-pin the AppRole and record what each deploy applied
Closes the rotation: the instance is moved onto this run's credentials and the
result is written to .applied.json, which is what lets the next deploy log in
before it re-pins in turn. The file sits in the compose project directory, which
already holds the seal key in plaintext in .env/env, so this adds no new
exposure class; it is written 0600 root-owned and last, after both pins, so a
run that dies partway leaves the previous generation intact.

custom-secret-id is not idempotent -- writing a SecretID the role already holds
answers 500 'SecretID is already registered' rather than doing nothing -- so the
swap is gated on the value having changed. The previously registered SecretID is
destroyed afterwards, without which the rotation would add a credential and
revoke none.
2026-09-15 02:15:23 +02:00
evangelostsak e6e27b2dac feat(openbao): let the AppRole login survive its own credentials rotating
A deploy is handed credentials the instance has not been told about yet, so the
inventory login is expected to fail on every rotation. Try it, fall back to what
the previous deploy recorded, and fail with a message naming the state file when
neither is accepted -- a bare 403 here reads like an unrelated defect.

Both logins are non-fatal so the guard is what reports the failure. Whichever
one succeeded also identifies the SecretID the instance actually holds, which is
the value the rotation has to destroy; taking it from the state file instead
would destroy the wrong one whenever the inventory credentials still worked.
2026-09-15 02:15:14 +02:00
evangelostsak 5c8721ee90 feat(openbao): read the applied credentials before the container is rendered
sys-stk-backend renders env.j2, which is what carries the previous seal key
into the container. Reading the applied state after that include leaves the
node with no way to decrypt its own store, so the read has to precede it.
2026-09-15 02:14:32 +02:00
evangelostsak 241b01f94a style(web-app-openbao): drop the narrative comments from 04_auth_methods.yml 2026-09-11 18:37:01 +02:00
evangelostsak 47217af7a1 style(web-app-openbao): drop the narrative comment from 03_policies.yml 2026-09-11 18:36:55 +02:00
evangelostsak cbda5724c8 style(web-app-openbao): drop the narrative comments from 01_init.yml 2026-09-11 18:36:50 +02:00
evangelostsak 71f2a0c414 style(web-app-openbao): drop the narrative comment from 00_core.yml 2026-09-11 18:36:45 +02:00
evangelostsak 4a206f6d52 style(web-app-openbao): drop the narrative comment from the group-alias helper 2026-09-11 18:36:41 +02:00
evangelostsak a18f8a24ee fix(web-app-openbao): stop reporting the token cleanup as a configuration change 2026-09-07 22:13:42 +02:00
evangelostsak 42070a9804 fix(web-app-openbao): derive changed state for the PKI reconciliation 2026-09-07 22:13:33 +02:00
evangelostsak 0d84d04c2f fix(web-app-openbao): derive changed state for the auth method reconciliation 2026-09-07 22:13:18 +02:00
evangelostsak 8239277fdd fix(web-app-openbao): derive changed state for the policy and KV reconciliation 2026-09-07 22:09:28 +02:00
evangelostsak 1500470c9d feat(web-app-openbao): add idempotent identity group-alias helper 2026-09-07 00:31:51 +02:00
evangelostsak ad98e188ef feat(web-app-openbao): mount the optional internal PKI behind a service flag 2026-09-07 00:31:31 +02:00
evangelostsak 8c68430c0c feat(web-app-openbao): configure OIDC and LDAP auth with group-to-policy binding 2026-09-07 00:31:17 +02:00
evangelostsak 47df1e8a11 feat(web-app-openbao): mount KV v2 and reconcile the RBAC policies 2026-09-07 00:30:59 +02:00
evangelostsak 50adf4456d feat(web-app-openbao): pin AppRole ids from inventory and revoke the root token 2026-09-07 00:30:45 +02:00
evangelostsak 9c7979f0ec feat(web-app-openbao): initialise once and wait for the static seal to auto-unseal 2026-09-07 00:30:30 +02:00
evangelostsak f2862c2868 feat(web-app-openbao): render config after the backend stack, before the handler flush 2026-09-07 00:30:06 +02:00
evangelostsak dfa2350d6f feat(web-app-openbao): add run-once role entry point 2026-09-07 00:29:52 +02:00