Establish scoped KeyCape factor custody and verified automatic renewal
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
This commit is contained in:
parent
b75729b799
commit
2e2c31d237
22 changed files with 1169 additions and 1 deletions
55
docs/evidence/2026-09-13-keycape-factor-custody.md
Normal file
55
docs/evidence/2026-09-13-keycape-factor-custody.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# KeyCape factor credential custody — 2026-09-13
|
||||
|
||||
User-authorized attended setup is live under RPF-WP-0040 / CCR-2026-0023.
|
||||
The operator authenticated through the contained OpenBao login lane. No
|
||||
credential value was included in source, receipts, chat or ordinary logs.
|
||||
|
||||
Dedicated privacyIDEA identity `keycape-factor-reader` has `tokenlist` permission
|
||||
for `coulomb`. Its password is in a separate OpenBao issuer path; KeyCape
|
||||
receives only a provider-issued, expiring JWT via External Secrets. Explicit
|
||||
fallback policy preserves existing administrators' prior rights and excludes
|
||||
this service identity. The service cannot read provider administration policy.
|
||||
|
||||
Live checks passed:
|
||||
|
||||
- Exact OpenBao policy/role readback, audience and service-account bindings.
|
||||
- Provider-issued credential, cross-user factor visibility, and the actual
|
||||
KeyCape `user` + `realm` + `active=True` lookup shape.
|
||||
- Wrong service account rejected; issuer password and sibling KV reads denied.
|
||||
- ExternalSecret `sso/keycape-factor-read`: Ready=True / SecretSynced.
|
||||
- Initial renewal Job published KV version 2 and revoked its OpenBao session.
|
||||
A second Job published version 3; the existing mounted file received the
|
||||
replacement and provider lookup accepted it without a pod restart.
|
||||
- KeyCape config activation passed server dry-run and UID/resource-version CAS;
|
||||
readback confirmed all other Secret data and configuration bytes preserved.
|
||||
Deployment rolled out successfully with the existing image
|
||||
`sha256:c9eb584d60efecfe00e1745a7e8cd3ebb4ae0f94faa715ef9594cd58a5dcebb6`.
|
||||
- Fourteen offline tests cover identity/expiry/status rejection, scope boundaries,
|
||||
failed-provider preservation, CAS failure, session-cleanup failure, retry,
|
||||
ambiguous config rejection and byte-preserving activation.
|
||||
|
||||
Runtime source: `manifests/keycape-factor-custody.yaml`; renewal every ten minutes,
|
||||
ESO refresh every minute. Renewal runs as non-root with an immutable ConfigMap,
|
||||
digest-pinned image, dedicated service account and restricted network policy.
|
||||
The reader has no issuer-password permission; the renewer cannot write that
|
||||
password. OpenBao sessions last at most fifteen minutes (ESO) or five minutes
|
||||
(renewer). Successful renewal workers revoke their sessions explicitly.
|
||||
|
||||
Repeat: `make keycape-factor-tests` and `make keycape-factor-live-acceptance`.
|
||||
Exact-commit CI is `.forgejo/workflows/keycape-factor-acceptance.yaml`.
|
||||
The first proof Job was `keycape-factor-delivery-acceptance`; its short-lived
|
||||
reader session expires by its fifteen-minute maximum TTL. The final reusable
|
||||
proof also revokes its session; its result is recorded below after execution.
|
||||
|
||||
Limits: this establishes credential custody, renewal and native delivery.
|
||||
Genuine provider JWT expiry/revocation drills remain RPF-WP-0040-T04; renewing a
|
||||
JWT does not revoke its predecessor. Optional-after-enrollment policy remains
|
||||
unchanged. OTP onboarding, lost-factor recovery and their complete platform-admin
|
||||
browser journeys remain KEY-WP-0035 / USER-WP-0030. Historical resolver incident
|
||||
NK-WP-0033 is not closed by this new service lane.
|
||||
|
||||
Final reusable proof passed: `keycape-factor-proof-0c4a6929`. All eight checks
|
||||
were true: wrong-SA rejection, issuer-password denial, sibling-secret denial,
|
||||
mounted-token/custody equality, KeyCape per-user lookup, provider administration
|
||||
denial, renewed projection acceptance, and explicit reader-session revocation.
|
||||
KeyCape remained Ready 1/1 at deployment generation 46. CCR validation passed.
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
# NetKingdom credential custody contract
|
||||
|
||||
Status: **draft / blocked pending OpenBao owner confirmation**
|
||||
Historical resolver lanes: **draft / blocked**.
|
||||
KeyCape factor service lane: **active**, established and verified 2026-09-13 (below).
|
||||
Incident: `KEYCAPE-EXPOSURE-20260823-01`
|
||||
Consumer procedure: NetKingdom `NK-WP-0033`, resolver reconciliation revision
|
||||
`eec7007` / checkout `f2e578c`
|
||||
|
|
@ -43,3 +44,75 @@ The routing lane is registered but remains `resolvable: false`. No
|
|||
`warden access --fetch`, proxy execution, resolver retry, or predecessor
|
||||
restoration is authorized until the OpenBao owner publishes the missing
|
||||
metadata and the attended handoff is approved.
|
||||
|
||||
## KeyCape factor service lane — authorized setup, 2026-09-13
|
||||
|
||||
RPF-WP-0040 / CCR-2026-0023 establish a new dedicated service lane. The user,
|
||||
acting as platform operator, authorized establishing storage/delivery and performed
|
||||
attended OpenBao authentication. This is separate from the historical resolver
|
||||
repair lanes above; their incident receipts are not retroactively completed.
|
||||
|
||||
| Purpose | KV v2 CLI path | Fields | Consumer |
|
||||
| --- | --- | --- | --- |
|
||||
| Scoped provider JWT | platform/workloads/net-kingdom/keycape-factor-read | TOKEN, EXPIRES_AT, REQUEST | KeyCape via namespace-scoped ESO and adminTokenFile |
|
||||
| Renewable service login | platform/workloads/net-kingdom/keycape-factor-issuer | USERNAME, PASSWORD, REQUEST | Owner bootstrap, then dedicated renewal worker; never KeyCape |
|
||||
|
||||
Provider identity: keycape-factor-reader (local privacyIDEA service admin).
|
||||
Effective permission: tokenlist for realm coulomb only. Preserve the existing
|
||||
no-admin-policy baseline for other administrators using an explicit fallback
|
||||
that excludes this service identity. Only the two reviewed named policies may
|
||||
be introduced; unexpected existing admin policies abort setup.
|
||||
|
||||
Generate the service password in owner-process memory and persist with KV CAS=0
|
||||
before provider creation, allowing interrupted setup to resume. The attended
|
||||
worker passes values through stdin/captured pipes only, never argv or receipts.
|
||||
Only explicit KV absence permits creation. Existing request provenance and
|
||||
policy definitions must match; retries do not rotate an existing identity.
|
||||
|
||||
Reader policy: workload-kv-read-keycape-factor-read, role
|
||||
keycape-factor-workload-kv-read, bound sso/keycape-factor-eso, audience openbao,
|
||||
15-minute maximum OpenBao session, no default policy. Metadata apply/readback
|
||||
passed via the attended owner envelope. Provider issuance, native ESO delivery,
|
||||
KeyCape mounted-file activation, per-user lookup and rotation without restart
|
||||
passed. The reader can inspect and revoke only its own OpenBao session in
|
||||
addition to reading the exact JWT data/metadata paths. See the acceptance record
|
||||
`docs/evidence/2026-09-13-keycape-factor-custody.md`.
|
||||
|
||||
Provider JWTs expire independently of OpenBao sessions. Require 5 minutes to
|
||||
2 hours remaining lifetime at issuance; renew well before expiry. Disable the
|
||||
service's tokenlist policy (while retaining fallback exclusion) to withdraw its
|
||||
factor access; do not rotate privacyIDEA's global signing key as routine renewal.
|
||||
Verify expiry/permission refusal separately from OpenBao self-revocation.
|
||||
|
||||
Renewal: `sso/keycape-factor-renewer` runs every ten minutes under its own
|
||||
service account, with a five-minute maximum OpenBao session. Its separate policy
|
||||
reads the issuer password and reads/writes only the delivered JWT path. The
|
||||
worker verifies provider identity, expiry, factor lookup and denied policy
|
||||
administration before a CAS write and readback, then revokes its session.
|
||||
ESO refreshes once per minute. KeyCape mounts only `admin-token`, mode 0440,
|
||||
through fsGroup 65534; the issuer password never enters its namespace Secret.
|
||||
|
||||
Operations: `make keycape-factor-tests` runs offline failure/retry and config
|
||||
preservation tests. `make keycape-factor-live-acceptance` creates bounded Jobs
|
||||
that verify native delivery scope and a second renewal reaching the mounted
|
||||
file. The runner emits booleans and resource identifiers only. CronJob failures
|
||||
and ExternalSecret Ready=False are the native degraded-state signals. A failed
|
||||
renewal leaves the last verified token in custody; expiry causes authentication
|
||||
to fail closed. Restore provider/custody connectivity and launch a renewal Job
|
||||
from the CronJob; verify SecretSynced and repeat live acceptance.
|
||||
|
||||
Attended setup is reproducible through `openbao-attended-exec.py` with
|
||||
`keycape_factor_metadata.py --apply --receipt <new-file>`,
|
||||
`keycape_factor_bootstrap.py --receipt <new-file>` and
|
||||
`keycape_factor_renewal_metadata.py --receipt <new-file>` in that order, followed
|
||||
by the custody manifest and guarded `keycape_factor_activate.py --apply`.
|
||||
Use unique receipt paths. Existing mismatched policies/roles/provenance fail
|
||||
closed; never replace an unknown owner credential. The separate
|
||||
`keycape_factor_eso_metadata.py` records the migration from initial read-only
|
||||
metadata to ESO's self-session permissions; fresh setup uses the final policy.
|
||||
|
||||
Outstanding acceptance is explicit in RPF-WP-0040-T04: genuine provider JWT
|
||||
expiry and provider-policy revocation/recovery drills. A newly issued JWT does
|
||||
not itself revoke its predecessor. Native mounted rotation has passed, but it
|
||||
is not evidence of predecessor revocation. Optional MFA rollout and lost-factor
|
||||
recovery remain KEY-WP-0035 / USER-WP-0030, not completed by this service lane.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue