net-kingdom/workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md
tegwick 8f808577d6
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
fix(security): constrain SOPS writes and custody argument handoff
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a1346b-72a4-7d40-b8bd-8b8ca346cfa2
2026-10-13 21:03:24 +02:00

15 KiB

id type title domain repo status owner topic_slug created updated flavor depends_on state_hub_workstream_id
NK-WP-0049 workplan Remove bootstrap secret output and verify separated recovery custody infotech net-kingdom active codex netkingdom 2026-10-12 2026-10-13 implementation
17d315f0-829e-57c7-85cc-3a7914152804

Native owner work for CUST-WP-0080 G08. NK-WP-0045 proves recovery of the non-secret root-grant record, not custody of the bootstrap private key or secret-bearing emergency bundle. Existing founder-deferred exposure rotation remains CUST-WP-0073-T05; this work does not override it or rotate any root.

Remove secret-bearing output and ambient root discovery

id: NK-WP-0049-T01
status: done
priority: high
state_hub_task_id: "d7ca8d5f-c5ff-5cba-bd25-038cb53ffe4e"

2026-10-12: regression reproduced emergency/reprint root export, generation prefix disclosure, permissive file modes, dry-run side effects and unattended entry. Replaced emergency export with a non-secret guide (legacy secret-input arguments refused), suppressed generation prefixes/tracing and set private permissions. Bootstrap requires the registered public recipient and an attended terminal; never generates/discovers a permanent private key. Encrypted resume requires an explicit matching temporary custody key before cluster activity. Dry-run is a non-mutating plan. The live cluster/apply path was not executed.

A fresh uniquely named encrypted ops bundle follows apply; old delivery flags cannot skip current acknowledgment. New acknowledgment state binds its SHA256 and timestamp. Declining stops before cleanup/completion. This is an operator statement, not an independent restore receipt. Historical fields remain unchanged. Make/help/status/console and current credential guidance reflect that distinction. Evidence and exact source manifest: Custodian docs/evidence/2026-10-12-netkingdom-security/bootstrap-handoff.json.

Reconcile other legacy custody callers and root-output boundaries

id: NK-WP-0049-T02
status: progress
priority: high
state_hub_task_id: "479d6970-6516-50f5-9436-bb5b56d58aad"

Inspect remaining direct encrypt/decrypt defaults, permanent-key setup commands, custody helper tracing/error handling and delegated apply/init helpers against the accepted single-founder attended custody decision. Public-recipient-only encryption and explicit temporary unlock must hold at their actual call sites. Do not treat a tty check as identity, authority or agent isolation. Reuse CUST-WP-0073/GLAS-WP-0012 for runtime isolation and Platform for root custody; record any further exact source defects and repair them before native acceptance.

2026-10-12 caller audit: direct encrypt/decrypt helpers and custody unlock repaired. Eight initial regressions failed against prior source. Encryption defaults to the registered public file and refuses private-key inputs; decryption has no HOME fallback and publishes only complete private output from staging, preserving existing outputs and removing partial failures. Unlock suppresses xtrace before hidden input, rejects ambiguous modes, removes competing inline/key-command credentials and preserves signal failure status with EXIT cleanup. Disposable crypto and prompt/child tests cover these boundaries. Bash foreground-child signal deferral and arbitrary-child copy retention are explicitly not solved by this helper. Evidence: Custodian custody-entrypoints.json.

Concrete uncovered callers remain (T02 stays progress):

  • The KeyCape personal-admin token mint/refresh paths are retired by the October 13 repair below. Historical token copies still need owner inventory/revocation; the old privacyidea/check-user-mfa-state.sh also consumes that legacy Secret with insecure curl/argv handling and still requires reconciliation.
  • Legacy trigger account/Secret inventory and owner-approved revocation are still required; removing automatic creation does not revoke historical credentials.
  • RSA and Authelia-to-KeyCape client-secret rotation need configuration-preserving updates, retained rollback custody and native multi-consumer acceptance. The legacy replacement paths now refuse before mutation; this is not rotation closure.
  • PostgreSQL/privacyIDEA/LLDAP/Authelia generators and the batch apply caller are repaired below. Other diagnostic/bootstrap/key-export callers and direct SOPS edit/encrypt/rotate are repaired below; native effect/recovery evidence is mandatory.

2026-10-12 Make caller repair: sops-setup now validates only the public recipient; creds-init no longer requires or installs a permanent private key. sops-decrypt uses the new age-only SOPS recovery adapter: explicit matching private custody file, isolated HOME/XDG/environment, pinned file descriptor, sanitized diagnostics, private no-overwrite output outside Git, atomic publication and failure cleanup. FILE/OUTPUT are passed as environment data instead of interpolated shell code. The old targets failed all 11 initial real-SOPS regressions. Additional format, parser, environment-command and credential-mode tests cover the final adapter. Existing HOME copies were not deleted; native recovery and the remaining direct editor/rotate/delegated paths are unverified. Evidence is Custodian sops-make-entrypoints.json; T02 remains progress, not source-surface closure.

2026-10-12 rotation caller repair: 20 initial regressions failed against prior source. New values and delegated diagnostics are suppressed, xtrace disabled, private owned no-symlink/single-link service files validated and replaced atomically, and Make passes SECRET as data. Database discovery fails closed before local mutation; SQL travels on stdin with ON_ERROR_STOP. Unattended LDAP password rotation is refused while the server update remains manual. Break-glass uses file input/server-side apply rather than argv literals/client annotations. Additional refusal/failure/cancel regressions cover final source. Successful script timestamps are not native consumer/revocation evidence; partial updates retain the local candidate for recovery. No live rotation occurred. Evidence: Custodian rotation-output.json. T02 and the founder-deferred rotation stay open.

2026-10-12 admin-bootstrap reconciliation: current SCOPE marks Keycloak backlog; KeyCape uses the separate pi-admin token path. Removed automatic legacy trigger account/policy/Secret creation and corresponding current readiness expectations. Fourteen old-source tests failed (two passed). The new entry point parses owned private input without executing shell code, sends credentials only on kubectl stdin, sanitizes failures and requires one pod plus a strict value-free receipt.

Upstream 3.12.2 inspection corrects the earlier mismatch hypothesis: admin add uses an upsert and may reset an existing password. The new version-scoped in-pod adapter verifies existing credentials and refuses mismatches or conflicting policies; new rows use insert-only transactional writes and a policy scoped to pi-admin. Disposable actual privacyIDEA 3.12.2/SQLite tests verify repeat stability, wrong-password refusal, legacy-policy/other-admin refusal, rollback and conflict handling. These do not prove the deployed image/PostgreSQL or effective MFA. Existing objects are not deleted or rotated. Current callers and the native acceptance gate remain open. Evidence: Custodian admin-bootstrap.json.

2026-10-13 KeyCape caller reconciliation: RPF-WP-0040 is finished and its owner contract/deployment use the scoped renewable factor-reader credential with ESO and adminTokenFile. Retired create-pi-token.sh and refresh-pi-token-live.sh before input/credential/cluster access; neither personal admin JWT delivery nor automatic realm/MFA-policy replacement is a current consumer requirement. Existing provider identities/Secrets were not revoked.

The first-bootstrap config generator now uses that mounted-file source, private literal input parsing, captured child diagnostics, stdin-only Secret data and atomic private signing-key publication without replacement. It requires Ready factor delivery and refuses existing config; API creation cannot overwrite a concurrent creator and adds no client-side apply annotation. A failed API call leaves its remote outcome unverified and retains the private key for recovery. Existing installations require a scoped owner update preserving clients/policy.

Bootstrap callers and recovery guidance no longer invoke retired token helpers. Legacy RSA/client-secret replacement rotations refuse before local or remote mutation: the former destroyed rollback custody before attempting publication, and both would overwrite newer live configuration. The remaining coordinated rotation work stays here, not silently completed by refusal. Native recovery, current deployed consumer acceptance and other create/apply/SOPS callers remain open. Source verification and its limits: Custodian docs/evidence/2026-10-13-netkingdom-security/keycape-caller-reconciliation.json.

2026-10-13 delegated service caller repair: the PostgreSQL, privacyIDEA, LLDAP and Authelia generators now share private literal-input parsing, captured child diagnostics and stdin-only Secret transfer. Ordinary invocation creates an absent object or verifies matching fields; it cannot silently rotate an existing object. Named rotation updates only its allowed field. UID/resource version/data tests guard patches; unknown data and unrelated metadata survive, and last-applied copies are removed under the same guard. Owner references, ESO management labels, immutable objects and wrong target identity are refused. The returned API object is checked before success; write failure remains an unknown remote outcome requiring reconciliation, never blind retry.

Authelia first creation retains a private signing key atomically; API failure keeps it for recovery. Existing issuer keys and matching bcrypt hashes survive repeat apply and session rotation. Explicit unsafe/differing keys are refused. The batch caller suppresses child output and records only successful components. Rotation callers pass exact field selectors; database rotation now updates the CNPG application Secret as well as the privacyIDEA URI. The attended LDAP path also depended on retired full KeyCape replacement, so now refuses before any change. SQL, Secrets and restarts remain non-atomic; coordinated recovery and native database/consumer acceptance are still required.

All 63 initial regression cases failed on the old helpers; the replacement and expanded caller tests pass, including real OpenSSL/bcrypt and actual kubectl local stdin patch/stale-version refusal with no cluster. Final source-bound suite results, dependency skips and test limitations are in Custodian docs/evidence/2026-10-13-netkingdom-security/service-secret-callers.json. No live Secret, signing key, credential or deployment was changed. T02 remains progress for the explicit callers/lifecycle work above; T03 remains waiting for its owner-scoped native recovery ceremony.

2026-10-13 SOPS write caller repair: Make passes paths and custody arguments literally, removing shell/Make execution before handoff. Public-only encryption refuses tracked plaintext. Edit consumes private replacement JSON/YAML outside Git and verifies the original MAC; rotate changes the data key only. Both require explicit matching custody and exactly the independently registered recipient, refusing metadata-injected recipients. Child diagnostics/plaintext output are suppressed, policy is preserved, and ciphertext publication checks source version before atomic replacement. Shared private-key validation rejects links/FIFOs. A stat/rename race against noncooperating same-UID writers remains; no OS isolation or live recipient migration is claimed. Validation and exact source binding: Custodian docs/evidence/2026-10-13-netkingdom-security/sops-write-callers.json.

Remaining closure checklist (source review, not proof of current live use):

  • privacyidea/enckey-bootstrap.sh is still called by creds-bootstrap-agent.sh and documented by creds-apply.sh and the privacyIDEA README. It exports keys to ordinary overwriteable paths and uses client-side Secret apply. Repair private no-overwrite export, existing-key preservation and conditional DR Secret writes; verify the actual backup/restore contract before recovery acceptance.
  • privacyidea/check-user-mfa-state.sh, linked by the smooth-bootstrap guide, still reads retired keycape-pi-token, disables TLS verification, puts its token in curl argv and prints provider error text. Reconcile with the current scoped factor-reader owner contract and a bounded diagnostic interface.
  • lldap/bootstrap-users.sh still sources credentials, sends password/token in argv and can print auth responses. Inspect its group/user provisioning callers against the current User Engine owner path before replacing or retiring them. Targeted source findings also include manage-group-members.sh, create-user.sh, break-glass.sh, dry-run-nonroot-user.sh and verify-t07.sh; their caller and lifecycle review remains incomplete.
  • privacyidea/repair-realm-live.sh writes shell-escaped credentials for bootstrap-realm.sh, which still sources them. Reconcile both ends together; a private temporary directory alone does not establish the transport contract.
  • Reconcile the complete docs/attended-procedure-inventory.md against current scripts and Make/bootstrap/root-output callers. The selected findings above are not an exhaustive closure audit. Retain historical revocation and configuration-preserving rotation requirements already recorded here.
  • After source coverage, execute T03's exact owner-approved off-host recovery and current-consumer checks. Source regressions do not satisfy that gate.

Verify owner-approved recovery and current native callers

id: NK-WP-0049-T03
status: wait
priority: high
depends_on: [NK-WP-0049-T02]
blocking_reason: "Source caller reconciliation and exact owner-scoped isolated recovery ceremony are required; no live bootstrap or root retrieval is authorized by this source receipt."
state_hub_task_id: "d68cd4d3-1ba3-530e-88f9-26ebb8dd6eac"

Review the final scoped procedure, current consumer/caller usage and independent key/snapshot availability. Under an exact approved ceremony, verify off-host ciphertext retrieval, matching independently held key, isolated decryption and required current service-file coverage, failed-key refusal and cleanup. Record only safe identities/digests/statuses. Do not run the whole live bootstrap merely to test this patch, or infer current credential validity from a historical bundle. Historical disclosure is separate incident/rotation work; terminal clearing and source repair do not revoke any earlier exposed value. G08 stays open until caller coverage, owner review and native recovery evidence are adequate.