Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
118 lines
6.8 KiB
Markdown
118 lines
6.8 KiB
Markdown
# NetKingdom credential custody contract
|
|
|
|
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`
|
|
|
|
This document defines the Railiance-side contract without containing or
|
|
deriving any credential value. It is not an authorization to fetch, export,
|
|
apply, or rotate a secret.
|
|
|
|
## Ownership
|
|
|
|
- `railiance-platform` owns OpenBao custody, policy, delivery, rotation
|
|
metadata, and revocation evidence.
|
|
- `net-kingdom` owns the privacyIDEA resolver contract and attended consumer
|
|
reconciliation.
|
|
- `ops-warden` routes the lane only; it does not fetch or proxy either value.
|
|
|
|
## Required OpenBao metadata (owner must fill, never guess)
|
|
|
|
| Lane | Canonical path | Field | Delivery | Status |
|
|
| --- | --- | --- | --- | --- |
|
|
| `net-kingdom-lldap-bind-credential` | `REQUIRED_FROM_OPENBAO_OWNER` | `REQUIRED_FROM_OPENBAO_OWNER` | protected attended input to NetKingdom resolver helper | blocked |
|
|
| `net-kingdom-privacyidea-admin-token` | `REQUIRED_FROM_OPENBAO_OWNER` | `REQUIRED_FROM_OPENBAO_OWNER` | protected attended input to provider-admin helper | blocked |
|
|
|
|
The owner receipt must also identify the KV mount/version semantics, the
|
|
least-privilege read/update policy, the authentication method, the expiry or
|
|
rotation behavior, and the approved attended handoff. No value belongs in this
|
|
file, State Hub, Git, chat, argv, or ordinary logs.
|
|
|
|
## Consumer handoff
|
|
|
|
Once the OpenBao owner supplies the metadata above, Railiance-platform records
|
|
the receipt identifier and passes only protected input to the pinned
|
|
NetKingdom procedure. The resolver repair must use the minimal two-input
|
|
`--reconcile` flow; audit proofs (lookup, MFA, predecessor denial) remain a
|
|
separate read-only operation.
|
|
|
|
## Current gate
|
|
|
|
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.
|