NK-WP-0056-T09 done: syn-journey live, synthetic journey passes at aal2
Founder-run under CCR-2026-0057: KeyCape client registered, lane.py setup (privacyIDEA-generated key, KV v1) and verify passed; the live journey reached aal2 with otp against kc.coulomb.social. Receipts, findings and the one teardown of a partial run in docs/evidence. syn-journey declared active. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 994874@bnt-lap001 Assistant-Session: 6017c416-d619-47eb-bd1a-f670a02d7d87
This commit is contained in:
parent
488f5f4ece
commit
661ab6a52e
7 changed files with 181 additions and 3 deletions
|
|
@ -345,7 +345,7 @@
|
|||
| task | NK-WP-0056-T06 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md |
|
||||
| task | NK-WP-0056-T07 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md |
|
||||
| task | NK-WP-0056-T08 | done | — | workplans/NK-WP-0056-bounded-executor-path.md |
|
||||
| task | NK-WP-0056-T09 | wait | — | workplans/NK-WP-0056-bounded-executor-path.md |
|
||||
| task | NK-WP-0056-T09 | done | — | workplans/NK-WP-0056-bounded-executor-path.md |
|
||||
| task | NK-WP-0056-T10 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md |
|
||||
| intake | NK-IN-0001 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
||||
| intake | NK-IN-0002 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"action": "teardown",
|
||||
"identity": "syn-journey",
|
||||
"keycape_client_removal_separate": true,
|
||||
"kv_removed": true,
|
||||
"lldap_user_removed": true,
|
||||
"phase": "done",
|
||||
"pi_token_removed": true,
|
||||
"source_sha256": "a213b5f9fc6703dfc5ad619320a2fd083596ece7a2a1895f42829159bea42dde",
|
||||
"status": "identity_removed",
|
||||
"values_recorded": false
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"action": "setup",
|
||||
"ccr_sha256": "4a9668856389f986d4220bbd424bd6b353d86393bb931ebad293f726219d8754",
|
||||
"coding_agents_denied": true,
|
||||
"created": {
|
||||
"email": "syn-journey@synthetic.invalid",
|
||||
"groups": [],
|
||||
"kv_path": "platform/workloads/net-kingdom/synthetic-journey/syn-journey",
|
||||
"lldap_user": "syn-journey",
|
||||
"pi_realm": "coulomb",
|
||||
"pi_token_serial": "SYNJOURNEY01"
|
||||
},
|
||||
"enrollment_confirmed_with_code": true,
|
||||
"identity": "syn-journey",
|
||||
"key_generated_by": "privacyidea",
|
||||
"kv_version": 1,
|
||||
"lldap_password_effective": true,
|
||||
"phase": "done",
|
||||
"source_ccr": "CCR-2026-0057",
|
||||
"source_sha256": "a213b5f9fc6703dfc5ad619320a2fd083596ece7a2a1895f42829159bea42dde",
|
||||
"status": "identity_prepared",
|
||||
"values_recorded": false,
|
||||
"workload_access": false
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"action": "verify",
|
||||
"ccr_sha256": "4a9668856389f986d4220bbd424bd6b353d86393bb931ebad293f726219d8754",
|
||||
"coding_agents_denied": true,
|
||||
"identity": "syn-journey",
|
||||
"journey": {
|
||||
"binds_on_path": {
|
||||
"authelia_first_factor": "svc-authelia",
|
||||
"keycape_otp": "svc-privacyidea",
|
||||
"keycape_token": "svc-keycape"
|
||||
},
|
||||
"claims": {
|
||||
"assurance_level": "aal2",
|
||||
"assurance_methods": [
|
||||
"pwd",
|
||||
"otp"
|
||||
],
|
||||
"assurance_source": "key-cape",
|
||||
"kid": "key-1",
|
||||
"preferred_username_matches": true,
|
||||
"signature_verified": true
|
||||
},
|
||||
"failed_step": null,
|
||||
"finished_at": "2026-10-31T12:32:38Z",
|
||||
"otp_time_step": 59781665,
|
||||
"reason": null,
|
||||
"result": "pass",
|
||||
"run_id": "53251d54-80d1-4c23-bc6b-fd025e5d1a94",
|
||||
"runner_sha256": "c639054597be7647a3ae88ac7da53490a43b38d9f4f95e24cf4e9f54b18f997c",
|
||||
"schema": "netkingdom.synthetic-journey.receipt/v1",
|
||||
"started_at": "2026-10-31T12:32:35Z",
|
||||
"steps": [
|
||||
{
|
||||
"elapsed_ms": 117,
|
||||
"name": "discovery",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"elapsed_ms": 97,
|
||||
"name": "keycape_authorize",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"elapsed_ms": 771,
|
||||
"name": "authelia_first_factor",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"elapsed_ms": 326,
|
||||
"name": "authelia_authorization",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"elapsed_ms": 880,
|
||||
"name": "keycape_callback",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"elapsed_ms": 410,
|
||||
"name": "keycape_otp",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"elapsed_ms": 249,
|
||||
"name": "keycape_token",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"elapsed_ms": 99,
|
||||
"name": "id_token",
|
||||
"ok": true
|
||||
}
|
||||
],
|
||||
"target": {
|
||||
"acr_values": "urn:netkingdom:aal2",
|
||||
"client_id": "nk-synthetic-journey",
|
||||
"host_mapped": [],
|
||||
"issuer": "https://kc.coulomb.social",
|
||||
"username": "syn-journey"
|
||||
}
|
||||
},
|
||||
"journey_passed": true,
|
||||
"kv_is_current": true,
|
||||
"kv_version": 1,
|
||||
"phase": "done",
|
||||
"source_ccr": "CCR-2026-0057",
|
||||
"source_sha256": "a213b5f9fc6703dfc5ad619320a2fd083596ece7a2a1895f42829159bea42dde",
|
||||
"status": "identity_verified",
|
||||
"values_recorded": false,
|
||||
"workload_access": false
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# NK-WP-0056-T09: synthetic journey identity live (2026-10-31)
|
||||
|
||||
Founder-run, attended. claude-code prepared the tooling and guided the run. Under CCR-2026-0057
|
||||
(railiance-platform `2ac9786`, sha256 `4a966885…8754`) and the accepted SecurityCase
|
||||
`nk-synthetic-journey` (net-kingdom `d654f8a`):
|
||||
|
||||
1. The KeyCape client `nk-synthetic-journey` (public, PKCE, MFA pinned) was registered with
|
||||
`keycape-client.py apply` (check-config from the deployed `e2b5fa1` binary) and KeyCape restarted.
|
||||
2. `lane.py setup` fenced coding agents from the KV path and created `syn-journey` in LLDAP with no
|
||||
groups and a generated password. It enrolled TOTP token `SYNJOURNEY01` in realm `coulomb` with a
|
||||
key generated by privacyIDEA, confirmed with a code, and wrote `PASSWORD` and `TOTP_SEED` to
|
||||
`platform/workloads/net-kingdom/synthetic-journey/syn-journey` (KV v1, CAS=0). No workload role.
|
||||
3. `lane.py verify`: the KV holds exactly the two fields, the fence is present, no workload role
|
||||
exists, the KV password signs in to LLDAP, and **the synthetic journey passed against
|
||||
`https://kc.coulomb.social`**: aal2, methods `pwd`+`otp`, signature verified against KeyCape's
|
||||
JWKS, `preferred_username` matched. About 3 s end to end.
|
||||
|
||||
| Receipt | Result |
|
||||
| --- | --- |
|
||||
| `00-setup-refused-absent.json` | refused, nothing changed (privacyIDEA token lookup by a user that did not resolve yet, or the pi-admin login) |
|
||||
| `00-setup-refused-pi-admin-login.json` | refused at the pi-admin sign-in, nothing changed |
|
||||
| `00-verify-before-setup.json` | expected failure: no KV yet |
|
||||
| `01-setup.json` | stopped at `/token/init` after the user and KV v1 were written (see finding 1) |
|
||||
| `10-teardown.json` | removed that partial state: token, user, KV |
|
||||
| `11-setup.json` | `identity_prepared`, key generated by privacyIDEA, enrollment confirmed with a code |
|
||||
| `12-verify.json` | `identity_verified`, journey `pass` (embedded, value-free) |
|
||||
|
||||
All receipts were scanned for secret-shaped strings before commit. They hold identifiers, versions
|
||||
and booleans only.
|
||||
|
||||
## Findings
|
||||
|
||||
1. **privacyIDEA silently replaces a supplied TOTP key for `pi-admin`.** An admin policy with
|
||||
`action=*` (as `pi-admin-all-rights`) enables `totp_force_server_generate`, so `otpkey` is ignored
|
||||
and the confirmation with a code from the supplied key fails (`ERR905`). This was reproduced on the
|
||||
pinned 3.12.2 image. The lane now enrolls with `genkey=1` and takes the seed from the enrollment
|
||||
response, in memory only. Any tool that enrolls a known seed as `pi-admin` has the same problem.
|
||||
2. Listing privacyIDEA tokens by a user that does not resolve is an error, not an empty list. The
|
||||
lane looks tokens up by serial.
|
||||
3. Live KeyCape issues tokens to a user with no groups and no tenant mapping. The open point in the
|
||||
runbook is answered.
|
||||
4. Still open, recorded in CCR-2026-0057: KeyCape marks the token `principal_type: human`, and
|
||||
KeyCape has no per-client group gate.
|
||||
|
|
@ -94,7 +94,7 @@ identities:
|
|||
consumers: [{name: synthetic-journey, kind: service}]
|
||||
owner: "NetKingdom directory and User Engine owners"
|
||||
rotation: {path: "lane.py teardown then setup (CCR-2026-0057); password and TOTP seed together", coupling: [privacyidea]}
|
||||
status: planned
|
||||
status: active
|
||||
exceptions:
|
||||
- id: SI-X6
|
||||
rules: [SI-3]
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ or clear the resolver cache, and the attended `--rotate-bind` MFA proof has the
|
|||
|
||||
```task
|
||||
id: NK-WP-0056-T09
|
||||
status: wait
|
||||
status: done
|
||||
priority: medium
|
||||
assignee: claude-code
|
||||
needs_human: true
|
||||
|
|
@ -331,6 +331,14 @@ reader), SecurityCase `security-cases/nk-synthetic-journey.yaml` (public PKCE cl
|
|||
Open, recorded: KeyCape marks every token `principal_type: human`; KeyCape has no per-client group gate.
|
||||
Waiting on: CCR approval, case acceptance, founder run of the runbook.
|
||||
|
||||
**Done 2026-10-31** ([evidence](../docs/evidence/2026-10-31-nk56-t09-synthetic-identity/README.md)): CCR-2026-0057
|
||||
approved; case accepted; client registered; founder-run `lane.py setup` and `verify` passed. The live journey
|
||||
reached aal2 (`pwd`+`otp`, signature verified) against `kc.coulomb.social`. Two lane fixes on the way: privacyIDEA
|
||||
tokens are looked up by serial, and enrollment uses a privacyIDEA-generated key, because `pi-admin`'s `action=*`
|
||||
enables `totp_force_server_generate`, which silently replaced the supplied key (reproduced on 3.12.2). One
|
||||
partial run was removed with `lane.py teardown`. Next: T10 needs the executor identity (T03) and a
|
||||
`workload-kv-read` request for it.
|
||||
|
||||
## Unattended live journey
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue