diff --git a/docs/keycape-exposure-resolver-reconciliation.md b/docs/keycape-exposure-resolver-reconciliation.md new file mode 100644 index 0000000..959cc27 --- /dev/null +++ b/docs/keycape-exposure-resolver-reconciliation.md @@ -0,0 +1,100 @@ +# KeyCape exposure: privacyIDEA resolver reconciliation + +Incident: `KEYCAPE-EXPOSURE-20260823-01` +Workplan: `NK-WP-0033` +NetKingdom procedure revision: `f2e578c` +Platform recovery contract: railiance-platform `453fed3` +Owner cutover receipt: State Hub message `45b236c8-052f-43d3-a472-44f8e9694da2` + +This is the remaining attended provider-admin operation after the owner-reported +four-class cutover. It updates only privacyIDEA resolver `lldap-coulomb` so the +resolver uses the replacement LLDAP bind credential. It does not modify realms, +policies, tokens, KeyCape Secrets, or any other resolver. + +No password, hash, token, Secret payload, or manifest belongs in this document, +State Hub, Git, chat, command arguments, or ordinary logs. + +## Authority and pinning gate + +The operator must record a private approval receipt containing, at minimum: + +- incident `KEYCAPE-EXPOSURE-20260823-01`; +- NetKingdom revision `f2e578c` and platform contract `453fed3`; +- the owner receipt `45b236c8-052f-43d3-a472-44f8e9694da2`; +- the exact start/end window, attended driver, and independent abort operator; +- confirmation that the replacement LLDAP credential is the provider-approved + value and that no exposed predecessor will be restored. + +No live action is permitted if any revision, owner, cluster, or approval +identifier differs from the receipt. + +## Preflight (metadata and health only) + +Run from the approved operator workstation, with the cluster context and +provider endpoint already authorized. Do not render any Secret data. + +1. Verify the checked-out revision is exactly `f2e578c` and the helper has mode + `0755`; inspect its source, not live credential material. +2. Confirm LLDAP, privacyIDEA, KeyCape, Authelia, and + identity-provisioner are Ready using deployment/pod status fields only. +3. Confirm privacyIDEA and KeyCape health endpoints return an HTTP success + status, discarding response bodies. Do not use a command that prints a + bearer token or configuration response. +4. Confirm the approved window, driver, abort operator, provider custody, and + cleanup workspace are ready. Stop on any drift or missing owner. + +## Apply (one attended operation) + +1. Create one private mode-`0700` workspace with a cleanup trap. Keep the + pi-admin password and replacement LLDAP bind password in separate + mode-`0600` files or supply them only through the helper's hidden prompts. +2. From the pinned checkout, run exactly: + + ```text + bash sso-mfa/k8s/privacyidea/update-lldap-resolver-live.sh --apply + ``` + + The helper prompts for both passwords, authenticates to privacyIDEA, and + performs one `POST /resolver/lldap-coulomb`. It passes only protected file + paths to its child process, never prints values, and prints only a boolean + result. It must not be combined with `repair-realm-live.sh`, + `bootstrap-realm.sh`, `creds-rotate.sh`, or any full-bundle generator. +3. Stop immediately on any non-success response, timeout, unexpected endpoint, + or output that is not the documented boolean result. Do not restore the + exposed bundle or predecessor credential. + +## Postflight and predecessor denial + +Record only status codes, readiness, timestamps, revision identifiers, and +boolean results. + +1. Confirm privacyIDEA, LLDAP, KeyCape, Authelia, and identity-provisioner are + Ready again. Confirm the privacyIDEA health endpoint succeeds and the + resolver endpoint returns success without retaining its response body. +2. Exercise one approved KeyCape MFA path that requires the `coulomb` realm. + Record pass/fail only; never record the token or response body. +3. Using protected file inputs, prove an LDAP bind with the replacement value + succeeds and a bind with the exposed predecessor fails. The predecessor + test must be a boolean result and must not put the password in argv or + stdout. A failed predecessor bind is required evidence; do not retry it + against another provider. +4. Confirm the KeyCape owner’s existing four-class positive/negative receipt + remains associated with this resolver update. If any class lacks a receipt, + keep T05 open. +5. Securely remove the temporary workspace and record only cleanup success. + +## Abort and rollback + +Abort before mutation on revision drift, missing authority, unavailable health, +uncertain workspace cleanup, or any unsafe helper output. Abort forward after +mutation on a failed resolver response, failed readiness, failed replacement +MFA, or missing predecessor denial. The exposed bundle and every exposed +predecessor are never rollback material. Recovery after a partial write must +use a newly approved replacement value and revision, not a stale local bundle. + +## Completion evidence + +T03 may move to done only after the helper run and cleanup receipt are recorded +by the attended operator. T05 may move to done only after the resolver’s +replacement success, predecessor denial, owner cutover receipt, and all +residual limitations are recorded as sanitized evidence. diff --git a/workplans/NK-WP-0033-keycape-secret-exposure-rotation.md b/workplans/NK-WP-0033-keycape-secret-exposure-rotation.md index 216d474..9ea2053 100644 --- a/workplans/NK-WP-0033-keycape-secret-exposure-rotation.md +++ b/workplans/NK-WP-0033-keycape-secret-exposure-rotation.md @@ -121,7 +121,8 @@ NetKingdom also added the unattended-safe shape of the remaining provider operation in `sso-mfa/k8s/privacyidea/update-lldap-resolver-live.sh`. It is explicitly gated by `--apply`, requires an interactive terminal, uses protected 0600 files, updates only `lldap-coulomb`, and emits no credential values. It -has not been run; attended provider-admin reconciliation remains pending. +has not been run; the exact attended runbook is pinned in +`docs/keycape-exposure-resolver-reconciliation.md`. Do not use `sso-mfa/bootstrap/creds-rotate.sh` through an agent as currently written: it prints generated replacement values and its signing-key path