NK-WP-0054-T05: post-split restore drill passed as admin; SI-X1 retired
All five NK-WP-0053 stages passed on generation 20261031T034845Z-a13dc92e2c61, the first capture after the 2026-10-30 bind split, operated by admin. Uncounted attempts are kept with their causes. The drill document gains the OpenBao platform-admin login that stage 1 silently requires. The LLDAP declaration moves ld-admin and the svc-* binds to active and admin to human-only; SI-X1 is closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 896183@bnt-lap001 Assistant-Session: 3a18c01a-3205-4d02-94db-9efff25649a2
This commit is contained in:
parent
87e57aa710
commit
436c5fe585
16 changed files with 412 additions and 45 deletions
|
|
@ -315,7 +315,7 @@
|
|||
| task | NK-WP-0054-T02 | done | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T03 | done | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T04 | wait | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T05 | progress | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T05 | done | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T06 | done | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T07 | progress | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T08 | done | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
|
|
|
|||
|
|
@ -288,13 +288,15 @@ DSM-EXC-001 binds violations of a canon MUST; an exception against a rule that i
|
|||
|
||||
| Id | Rules | Account | Reason | Elimination path |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| SI-X1 | SI-1, SI-2, SI-3, SI-4, SI-9, naming | LLDAP `admin` | The human, the super-admin and the shared bind of four services in one account; password in a person's safe and a Kubernetes Secret | NK-WP-0054-T05: `svc-*` binds, provisioner account, `ld-admin`, `admin` human-only |
|
||||
| SI-X2 | SI-10 | LDAP bind accounts | Static long-lived credential; LDAP has no lease | Governed residual secret (DSM-TRANS-002) until a federated directory lookup exists |
|
||||
| SI-X3 | naming, SI-11 | `trigger-admin` (privacyIDEA) | Legacy account, consumers uninventoried | NK-WP-0049-T22 deleted the unreferenced Kubernetes copy (2026-10-27); revoking the privacyIDEA account stays under CUST-WP-0073-T05 / NK-WP-0055-T06 |
|
||||
| SI-X5 | SI-4, SI-5 | privacyIDEA | Only a local administrator is supported | Upstream support for attributed administrators |
|
||||
| SI-X6 | SI-3 | LLDAP `svc-provisioner` | LLDAP v0.6.2 has no group narrower than `lldap_admin` that can create users or add members (rehearsal 2026-10-28); own account and Secret, but super-admin rights | A narrower LLDAP role, or a provisioner that no longer creates users (founder decision 2026-10-28) |
|
||||
|
||||
**Closed:** SI-X4 (OpenBao `netkingdom-platform-admin` bound the group claim only) closed 2026-10-27: the founder applied the
|
||||
**Closed:** SI-X1 (LLDAP `admin` as human, super-admin and shared bind of four services) closed 2026-10-31: the
|
||||
bind split ran live on 2026-10-30 (`verify --old-admin-retired` passed) and the restore drill on the first post-split
|
||||
generation passed all five stages as `admin` (NK-WP-0054-T05,
|
||||
[evidence](../../docs/evidence/2026-10-31-identity-restore-drill/drill.json)). SI-X4 (OpenBao `netkingdom-platform-admin` bound the group claim only) closed 2026-10-27: the founder applied the
|
||||
`aal2` binding (RPF-WP-0065-T04; receipts show `bound_claims` = `net-kingdom-admins` plus `/assurance/level: aal2`, and a
|
||||
fresh MFA login verified).
|
||||
|
||||
|
|
@ -316,7 +318,7 @@ owner accepts that exception or the issuer and role change land.
|
|||
- **IAM Profile v0.3:** governs tokens, including the agent principal. KeyCape's confidential clients stay governed
|
||||
by the profile plus a declaration (settled 2026-10-24).
|
||||
- **Credential Management v0.2 §6** lists `LLDAP_LDAP_USER_PASS` as a three-way coupling; the inventory found five
|
||||
consumers. That row is corrected when SI-X1 is eliminated.
|
||||
consumers. SI-X1 was eliminated on 2026-10-31; that row is now out of date and is a follow-up of NK-WP-0054.
|
||||
- **Crisis custody list** (`docs/crisis-custody-list.md`) is the recovery companion to SI-9 and SI-12, and the
|
||||
enumeration of roots that DSM-ROOT-001 asks for.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"schema": "netkingdom.isolated-lldap-recovery.v1",
|
||||
"status": "failed",
|
||||
"production_contacted": false,
|
||||
"secret_values_exported": false,
|
||||
"container_cleanup_verified": false,
|
||||
"temporary_plaintext_cleanup_verified": false,
|
||||
"source_sha256": "35bfa8411186ad936a199bd450c7e18c2ad51059301a6db9650e6bd055ab1b81",
|
||||
"target": {
|
||||
"engine_id": "74f93783-6cef-43a8-a3c4-f62586cd3bc1",
|
||||
"engine_name": "bnt-lap001",
|
||||
"images": [
|
||||
{
|
||||
"reference": "docker.io/lldap/lldap@sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d",
|
||||
"image_id": "sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d"
|
||||
},
|
||||
{
|
||||
"reference": "ghcr.io/gpappsoft/privacyidea-docker@sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473",
|
||||
"image_id": "sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473"
|
||||
}
|
||||
],
|
||||
"production_contacted": false,
|
||||
"network_mode": "none",
|
||||
"host_ports_published": false
|
||||
},
|
||||
"error": "isolated_lldap_check_failed_private_diagnostics_suppressed",
|
||||
"failed_stage": "input_validation",
|
||||
"finished_at": "2026-10-31T07:11:42.343583+00:00"
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"schema": "netkingdom.isolated-privacyidea-mfa.v1",
|
||||
"status": "isolated_privacyidea_mfa_verified",
|
||||
"production_contacted": false,
|
||||
"secret_values_exported": false,
|
||||
"existing_token_mfa_proven": true,
|
||||
"user_to_token_resolver_proven": true,
|
||||
"container_cleanup_verified": true,
|
||||
"temporary_plaintext_cleanup_verified": true,
|
||||
"source_sha256": "d55c5f93c085907be6ae19b0eba6ef360fe863ef8922aa5b6ec205f4837efa16",
|
||||
"reused_source_sha256": {
|
||||
"isolated-privacyidea.py": "4df7196678aac1ad594f32abf45c68dc21a9d2760d9e8a536a215a4a3c5d277f",
|
||||
"isolated-lldap.py": "35bfa8411186ad936a199bd450c7e18c2ad51059301a6db9650e6bd055ab1b81",
|
||||
"identity-bundle.py": "524a03c065db4307fcde83d33af39503f3de19cb6ef3a8ca72c991d4a8c08ee6",
|
||||
"verify-identity.py": "039864d0141b552887752bf17cfdcf1bef4bdc9126f946a9c0533e4fd54ebf4f",
|
||||
"retrieve-identity-offline.py": "3421dbb3f3280a001d33ea2deb12ab609404a0b4254cd43c21518d77d166f972"
|
||||
},
|
||||
"target": {
|
||||
"engine_id": "74f93783-6cef-43a8-a3c4-f62586cd3bc1",
|
||||
"engine_name": "bnt-lap001",
|
||||
"images": [
|
||||
{
|
||||
"reference": "docker.io/lldap/lldap@sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d",
|
||||
"image_id": "sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d"
|
||||
},
|
||||
{
|
||||
"reference": "ghcr.io/gpappsoft/privacyidea-docker@sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473",
|
||||
"image_id": "sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473"
|
||||
},
|
||||
{
|
||||
"reference": "postgres@sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70",
|
||||
"image_id": "sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70"
|
||||
}
|
||||
],
|
||||
"production_contacted": false,
|
||||
"network_mode": "none",
|
||||
"host_ports_published": false
|
||||
},
|
||||
"ciphertext_sha256": "126be7f4c5f4ff73ff8f15f4ab486c23795ac0a4ce51930e6d3ccb859031f2c1",
|
||||
"manifest_file_count": 36,
|
||||
"all_manifest_hashes_verified": true,
|
||||
"ldap_resolvers_rewritten_to_loopback": 1,
|
||||
"container_isolation_verified_before_private_import": true,
|
||||
"user_resolved_through_restored_resolver_and_directory": true,
|
||||
"selected_user_sha256": "2868946f999c23cc527736fa53c6e35ca7960cf01d06b33ea792e1e164aabcc1",
|
||||
"user_token_count": 1,
|
||||
"user_usable_token_count": 1,
|
||||
"user_usable_token_types": [
|
||||
"totp"
|
||||
],
|
||||
"wrong_key_refused": true,
|
||||
"wrong_key_refusal": "error_907",
|
||||
"wrong_code_refused": true,
|
||||
"wrong_code_refusal": "wrong_value",
|
||||
"existing_token_native_check_accepted": true,
|
||||
"only_loopback_interface_verified": true,
|
||||
"external_routes_absent": true,
|
||||
"finished_at": "2026-10-31T07:26:57.126681+00:00"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"status": "failed",
|
||||
"source_sha256": "ec123d47a07305253570dacab7e103f8c193a23f20c9fc87466ea4a95ae0ec13",
|
||||
"error": "contained_owner_retrieval_failed"
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"schema": "netkingdom.isolated-authelia-recovery.v1",
|
||||
"status": "isolated_authelia_verified",
|
||||
"production_contacted": false,
|
||||
"secret_values_exported": false,
|
||||
"container_cleanup_verified": true,
|
||||
"temporary_plaintext_cleanup_verified": true,
|
||||
"source_sha256": "6139294f6ebdcbaad35934f04b11177d8cf89d134506a730f0c0ebbb3a4967be",
|
||||
"reused_source_sha256": {
|
||||
"isolated-lldap.py": "35bfa8411186ad936a199bd450c7e18c2ad51059301a6db9650e6bd055ab1b81",
|
||||
"identity-bundle.py": "524a03c065db4307fcde83d33af39503f3de19cb6ef3a8ca72c991d4a8c08ee6",
|
||||
"verify-identity.py": "039864d0141b552887752bf17cfdcf1bef4bdc9126f946a9c0533e4fd54ebf4f",
|
||||
"retrieve-identity-offline.py": "3421dbb3f3280a001d33ea2deb12ab609404a0b4254cd43c21518d77d166f972"
|
||||
},
|
||||
"target": {
|
||||
"engine_id": "74f93783-6cef-43a8-a3c4-f62586cd3bc1",
|
||||
"engine_name": "bnt-lap001",
|
||||
"images": [
|
||||
{
|
||||
"reference": "docker.io/lldap/lldap@sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d",
|
||||
"image_id": "sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d"
|
||||
},
|
||||
{
|
||||
"reference": "ghcr.io/gpappsoft/privacyidea-docker@sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473",
|
||||
"image_id": "sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473"
|
||||
},
|
||||
{
|
||||
"reference": "docker.io/authelia/authelia@sha256:bd97cff4fcbf715b5ff1f9ae286afbe6033afce385302520b0368122d43a6f54",
|
||||
"image_id": "sha256:bd97cff4fcbf715b5ff1f9ae286afbe6033afce385302520b0368122d43a6f54"
|
||||
}
|
||||
],
|
||||
"production_contacted": false,
|
||||
"network_mode": "none",
|
||||
"host_ports_published": false
|
||||
},
|
||||
"ciphertext_sha256": "126be7f4c5f4ff73ff8f15f4ab486c23795ac0a4ce51930e6d3ccb859031f2c1",
|
||||
"manifest_file_count": 36,
|
||||
"all_manifest_hashes_verified": true,
|
||||
"container_isolation_verified_before_private_import": true,
|
||||
"original_configuration_validated": true,
|
||||
"storage_encryption_key_verified": true,
|
||||
"existing_user_first_factor": true,
|
||||
"wrong_password_refused": true,
|
||||
"archived_client_secret_accepted": true,
|
||||
"wrong_client_secret_refused": true,
|
||||
"grant_issued": false,
|
||||
"only_loopback_interface_verified": true,
|
||||
"external_routes_absent": true,
|
||||
"issuer_key_matches_archive": true,
|
||||
"issuer_jwk_thumbprint_sha256": "El5Prcg0wUe1l9mAV1IC55i45-zrLXwcBnl7WamASm8",
|
||||
"notifications_confined_to_tmpfs_file": true,
|
||||
"notification_written": false,
|
||||
"finished_at": "2026-10-31T07:24:21.595569+00:00"
|
||||
}
|
||||
32
docs/evidence/2026-10-31-identity-restore-drill/drill.json
Normal file
32
docs/evidence/2026-10-31-identity-restore-drill/drill.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"schema": "netkingdom.identity-restore-drill.v1",
|
||||
"work": "NK-WP-0054-T05 (post-split drill), NK-WP-0053 cadence",
|
||||
"date": "2026-10-31",
|
||||
"operator": "admin (founder)",
|
||||
"verdict": "pass",
|
||||
"generations": {
|
||||
"20261031T034845Z-a13dc92e2c61": "stages 1-5"
|
||||
},
|
||||
"generation_context": "First daily capture after the 2026-10-30 LLDAP bind split (steps 1-10 and verify --old-admin-retired): the archived configuration binds ld-admin, svc-authelia and svc-privacyidea, and admin's password is the human-only one set at step 10.",
|
||||
"stages": [
|
||||
{"stage": 1, "name": "independent retrieval", "result": "pass", "receipt": "transfer.json",
|
||||
"identity": "admin, OpenBao platform-admin session (auth/netkingdom, aal2) reading the Backup account custody"},
|
||||
{"stage": 2, "name": "LLDAP", "result": "pass", "receipt": "lldap-recovery.json", "login_tested": "admin (post-split password)"},
|
||||
{"stage": 3, "name": "privacyIDEA", "result": "pass", "receipt": "privacyidea-recovery.json"},
|
||||
{"stage": 4, "name": "Authelia", "result": "pass", "receipt": "authelia-recovery.json", "login_tested": "admin (post-split password)",
|
||||
"bind_note": "Authelia ran on the archived post-split configmap, i.e. the svc-authelia bind; the receipt does not name the bind account."},
|
||||
{"stage": 5, "name": "by-user MFA", "result": "pass", "receipt": "privacyidea-mfa-recovery.json", "login_tested": "admin (realm coulomb)",
|
||||
"bind_note": "user resolved through the restored resolver, which keeps the archived svc-privacyidea bind"}
|
||||
],
|
||||
"attempts_not_counted": [
|
||||
{"receipt": "attempts/transfer-1-failed.json",
|
||||
"cause": "no valid OpenBao platform-admin token in the operator shell; retrieve-identity.py reads the Backup credential from OpenBao and fails silently without one (drill document lacked the login step; added 2026-10-31)"},
|
||||
{"receipt": "attempts/lldap-1-refused.json",
|
||||
"cause": "given the failed transfer receipt; refused at input_validation before any password prompt"},
|
||||
{"receipt": "attempts/privacyidea-mfa-platform-root.json",
|
||||
"cause": "valid pass, but tested platform-root rather than admin; stage 5 rerun with admin"}
|
||||
],
|
||||
"si_x1_retired": true,
|
||||
"next_due": "2027-01-31",
|
||||
"secret_values_recorded": false
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"schema": "netkingdom.isolated-lldap-recovery.v1",
|
||||
"status": "isolated_lldap_verified",
|
||||
"production_contacted": false,
|
||||
"secret_values_exported": false,
|
||||
"container_cleanup_verified": true,
|
||||
"temporary_plaintext_cleanup_verified": true,
|
||||
"source_sha256": "35bfa8411186ad936a199bd450c7e18c2ad51059301a6db9650e6bd055ab1b81",
|
||||
"target": {
|
||||
"engine_id": "74f93783-6cef-43a8-a3c4-f62586cd3bc1",
|
||||
"engine_name": "bnt-lap001",
|
||||
"images": [
|
||||
{
|
||||
"reference": "docker.io/lldap/lldap@sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d",
|
||||
"image_id": "sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d"
|
||||
},
|
||||
{
|
||||
"reference": "ghcr.io/gpappsoft/privacyidea-docker@sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473",
|
||||
"image_id": "sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473"
|
||||
}
|
||||
],
|
||||
"production_contacted": false,
|
||||
"network_mode": "none",
|
||||
"host_ports_published": false
|
||||
},
|
||||
"ciphertext_sha256": "126be7f4c5f4ff73ff8f15f4ab486c23795ac0a4ce51930e6d3ccb859031f2c1",
|
||||
"manifest_file_count": 36,
|
||||
"all_manifest_hashes_verified": true,
|
||||
"container_isolation_verified_before_private_import": true,
|
||||
"existing_user_native_ldap_bind": true,
|
||||
"wrong_password_refused": true,
|
||||
"only_loopback_interface_verified": true,
|
||||
"external_routes_absent": true,
|
||||
"directory_identity_rows_preserved": true,
|
||||
"directory_comparison_scope": [
|
||||
"group_attribute_schema",
|
||||
"group_attributes",
|
||||
"groups",
|
||||
"memberships",
|
||||
"user_attribute_schema",
|
||||
"user_attributes",
|
||||
"users"
|
||||
],
|
||||
"directory_table_counts": {
|
||||
"users": 12,
|
||||
"groups": 17,
|
||||
"memberships": 20,
|
||||
"user_attributes": 14,
|
||||
"group_attributes": 0,
|
||||
"user_attribute_schema": 3,
|
||||
"group_attribute_schema": 0
|
||||
},
|
||||
"finished_at": "2026-10-31T07:20:44.085127+00:00"
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"schema": "netkingdom.isolated-privacyidea-mfa.v1",
|
||||
"status": "isolated_privacyidea_mfa_verified",
|
||||
"production_contacted": false,
|
||||
"secret_values_exported": false,
|
||||
"existing_token_mfa_proven": true,
|
||||
"user_to_token_resolver_proven": true,
|
||||
"container_cleanup_verified": true,
|
||||
"temporary_plaintext_cleanup_verified": true,
|
||||
"source_sha256": "d55c5f93c085907be6ae19b0eba6ef360fe863ef8922aa5b6ec205f4837efa16",
|
||||
"reused_source_sha256": {
|
||||
"isolated-privacyidea.py": "4df7196678aac1ad594f32abf45c68dc21a9d2760d9e8a536a215a4a3c5d277f",
|
||||
"isolated-lldap.py": "35bfa8411186ad936a199bd450c7e18c2ad51059301a6db9650e6bd055ab1b81",
|
||||
"identity-bundle.py": "524a03c065db4307fcde83d33af39503f3de19cb6ef3a8ca72c991d4a8c08ee6",
|
||||
"verify-identity.py": "039864d0141b552887752bf17cfdcf1bef4bdc9126f946a9c0533e4fd54ebf4f",
|
||||
"retrieve-identity-offline.py": "3421dbb3f3280a001d33ea2deb12ab609404a0b4254cd43c21518d77d166f972"
|
||||
},
|
||||
"target": {
|
||||
"engine_id": "74f93783-6cef-43a8-a3c4-f62586cd3bc1",
|
||||
"engine_name": "bnt-lap001",
|
||||
"images": [
|
||||
{
|
||||
"reference": "docker.io/lldap/lldap@sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d",
|
||||
"image_id": "sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d"
|
||||
},
|
||||
{
|
||||
"reference": "ghcr.io/gpappsoft/privacyidea-docker@sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473",
|
||||
"image_id": "sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473"
|
||||
},
|
||||
{
|
||||
"reference": "postgres@sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70",
|
||||
"image_id": "sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70"
|
||||
}
|
||||
],
|
||||
"production_contacted": false,
|
||||
"network_mode": "none",
|
||||
"host_ports_published": false
|
||||
},
|
||||
"ciphertext_sha256": "126be7f4c5f4ff73ff8f15f4ab486c23795ac0a4ce51930e6d3ccb859031f2c1",
|
||||
"manifest_file_count": 36,
|
||||
"all_manifest_hashes_verified": true,
|
||||
"ldap_resolvers_rewritten_to_loopback": 1,
|
||||
"container_isolation_verified_before_private_import": true,
|
||||
"user_resolved_through_restored_resolver_and_directory": true,
|
||||
"selected_user_sha256": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
||||
"user_token_count": 1,
|
||||
"user_usable_token_count": 1,
|
||||
"user_usable_token_types": [
|
||||
"totp"
|
||||
],
|
||||
"wrong_key_refused": true,
|
||||
"wrong_key_refusal": "error_907",
|
||||
"wrong_code_refused": true,
|
||||
"wrong_code_refusal": "wrong_value",
|
||||
"existing_token_native_check_accepted": true,
|
||||
"only_loopback_interface_verified": true,
|
||||
"external_routes_absent": true,
|
||||
"finished_at": "2026-10-31T07:28:59.115112+00:00"
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"schema": "netkingdom.isolated-privacyidea-recovery.v1",
|
||||
"status": "isolated_privacyidea_checked",
|
||||
"production_contacted": false,
|
||||
"secret_values_exported": false,
|
||||
"existing_token_mfa_proven": false,
|
||||
"audit_signature_continuity_proven": true,
|
||||
"container_cleanup_verified": true,
|
||||
"temporary_plaintext_cleanup_verified": true,
|
||||
"source_sha256": "4df7196678aac1ad594f32abf45c68dc21a9d2760d9e8a536a215a4a3c5d277f",
|
||||
"target": {
|
||||
"engine_id": "74f93783-6cef-43a8-a3c4-f62586cd3bc1",
|
||||
"engine_name": "bnt-lap001",
|
||||
"images": [
|
||||
{
|
||||
"reference": "docker.io/lldap/lldap@sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d",
|
||||
"image_id": "sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d"
|
||||
},
|
||||
{
|
||||
"reference": "ghcr.io/gpappsoft/privacyidea-docker@sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473",
|
||||
"image_id": "sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473"
|
||||
},
|
||||
{
|
||||
"reference": "postgres@sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70",
|
||||
"image_id": "sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70"
|
||||
}
|
||||
],
|
||||
"production_contacted": false,
|
||||
"network_mode": "none",
|
||||
"host_ports_published": false
|
||||
},
|
||||
"ciphertext_sha256": "126be7f4c5f4ff73ff8f15f4ab486c23795ac0a4ce51930e6d3ccb859031f2c1",
|
||||
"manifest_file_count": 36,
|
||||
"all_manifest_hashes_verified": true,
|
||||
"container_isolation_verified_before_private_import": true,
|
||||
"retained_tokens_total": 7,
|
||||
"retained_tokens_with_native_decryption": 7,
|
||||
"different_key_cannot_reproduce_material_count": 7,
|
||||
"retained_token_material_decrypted_inside_process": true,
|
||||
"historical_audit_rows": 414440,
|
||||
"historical_signed_rows": 414440,
|
||||
"historical_unsigned_rows": 0,
|
||||
"historical_invalid_signatures": 0,
|
||||
"historical_legacy_signatures": 0,
|
||||
"all_stored_audit_rows_checked": true,
|
||||
"legacy_signature_verification_enabled": false,
|
||||
"event_completeness_claimed": false,
|
||||
"new_audit_entry_signed_verified": true,
|
||||
"tampered_entry_refused": true,
|
||||
"wrong_audit_public_key_refused": true,
|
||||
"only_loopback_interface_verified": true,
|
||||
"external_routes_absent": true,
|
||||
"finished_at": "2026-10-31T07:23:19.171196+00:00"
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"age_private_key_used": false,
|
||||
"artifact": "net-kingdom-identity-20261031T034845Z-a13dc92e2c61.tar.age",
|
||||
"ciphertext_bytes": 155133096,
|
||||
"ciphertext_sha256": "126be7f4c5f4ff73ff8f15f4ab486c23795ac0a4ce51930e6d3ccb859031f2c1",
|
||||
"downloaded_at": "2026-10-31T07:15:57.213304+00:00",
|
||||
"etag": "\"d1e7409d9bff877a87a20c06a8853e57\"",
|
||||
"folder": "Backup/railiance-backups/net-kingdom-identity",
|
||||
"independent_owner_retrieval_verified": true,
|
||||
"provider": "Storage Share",
|
||||
"schema": "netkingdom.identity-offsite.v1",
|
||||
"secret_values_exported": false,
|
||||
"status": "readback_verified"
|
||||
}
|
||||
|
|
@ -38,6 +38,11 @@ effect).
|
|||
- Pick the generation from the daily backup (the newest that passed health).
|
||||
Record its date, not its content.
|
||||
- Stage the pinned images locally. Each launcher refuses if they are missing.
|
||||
- Open an OpenBao session as `admin` in the operator shell:
|
||||
`bao login -method=oidc -path=netkingdom role=platform-admin` (MFA). Stage 1 reads
|
||||
the Backup-account credential from OpenBao custody through that token and never
|
||||
prompts; without a valid `platform-admin` token it writes a failed receipt
|
||||
(`contained_owner_retrieval_failed`) and prints nothing. Check `echo $?` after stage 1.
|
||||
- Confirm no outbound notification or provider write is configured. The
|
||||
launchers enforce isolation; confirm their preflight passes.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,43 +10,37 @@ complete: false
|
|||
identities:
|
||||
- name: admin
|
||||
principal_type: HUMAN
|
||||
conventions: [bind, service-admin]
|
||||
purpose: "The human platform administrator; with the same password also the directory super-admin and the bind of four services"
|
||||
purpose: "The human platform administrator; directory rights through lldap_admin on its own password and OTP; no service binds as admin (bind split 2026-10-30)"
|
||||
grants:
|
||||
- {mode: ADMINISTER, scope: directory, activation: STANDING, environments: [PRODUCTION]}
|
||||
- {mode: USE, scope: directory, activation: STANDING, environments: [PRODUCTION], read_only: true, note: "service binds"}
|
||||
- {mode: ADMINISTER, scope: directory, activation: STANDING, environments: [PRODUCTION], note: "lldap_admin membership"}
|
||||
interactive_login: true
|
||||
second_factor: required
|
||||
custody: [delivered-secret, personal-safe]
|
||||
custody: [personal-safe]
|
||||
consumers:
|
||||
- {name: founder, kind: person}
|
||||
- {name: authelia, kind: service}
|
||||
- {name: keycape, kind: service}
|
||||
- {name: privacyidea, kind: service}
|
||||
- {name: identity-provisioner, kind: service}
|
||||
owner: "NetKingdom directory and User Engine owners"
|
||||
rotation:
|
||||
path: "LLDAP_LDAP_USER_PASS in lldap-secrets (Credential Management section 6)"
|
||||
coupling: [authelia, keycape, privacyidea-resolver, identity-provisioner, lldap-web-ui]
|
||||
path: "Human password change; no service Secret holds it"
|
||||
coupling: []
|
||||
status: active
|
||||
recovery: {needed: true, works_with_sso_down: false, in_crisis_list: true}
|
||||
- name: ld-admin
|
||||
principal_type: HUMAN
|
||||
conventions: [service-admin]
|
||||
purpose: "LLDAP only: the directory's configured super-admin, a sealed fallback; introduced by configuration after a rehearsal"
|
||||
purpose: "LLDAP only: the directory's configured super-admin (LLDAP_LDAP_USER_DN), a sealed fallback; introduced 2026-10-30"
|
||||
grants:
|
||||
- {mode: ADMINISTER, scope: lldap, activation: BREAK_GLASS, environments: [PRODUCTION]}
|
||||
interactive_login: true
|
||||
second_factor: unknown
|
||||
custody: [unknown]
|
||||
custody: [delivered-secret, offline-key]
|
||||
consumers: []
|
||||
owner: "NetKingdom directory and User Engine owners"
|
||||
rotation: {path: "To be decided in NK-WP-0054-T06", coupling: []}
|
||||
status: planned
|
||||
rotation: {path: "LLDAP_LDAP_USER_PASS in lldap-secrets and the LLDAP custody file (apply-service-secrets.py named rotation)", coupling: [lldap]}
|
||||
status: active
|
||||
- name: svc-authelia
|
||||
principal_type: SERVICE
|
||||
conventions: [bind]
|
||||
purpose: "Target: Authelia's read-only directory lookup"
|
||||
purpose: "Authelia's read-only directory lookup"
|
||||
grants: [{mode: USE, scope: directory, activation: STANDING, environments: [PRODUCTION], read_only: true}]
|
||||
interactive_login: false
|
||||
second_factor: not_applicable
|
||||
|
|
@ -54,11 +48,11 @@ identities:
|
|||
consumers: [{name: authelia, kind: service}]
|
||||
owner: "NetKingdom directory and User Engine owners"
|
||||
rotation: {path: "Secret rotation with Authelia only", coupling: [authelia]}
|
||||
status: planned
|
||||
status: active
|
||||
- name: svc-keycape
|
||||
principal_type: SERVICE
|
||||
conventions: [bind]
|
||||
purpose: "Target: KeyCape's read-only directory lookup"
|
||||
purpose: "KeyCape's read-only directory lookup"
|
||||
grants: [{mode: USE, scope: directory, activation: STANDING, environments: [PRODUCTION], read_only: true}]
|
||||
interactive_login: false
|
||||
second_factor: not_applicable
|
||||
|
|
@ -66,11 +60,11 @@ identities:
|
|||
consumers: [{name: keycape, kind: service}]
|
||||
owner: "NetKingdom directory and User Engine owners"
|
||||
rotation: {path: "Secret rotation with KeyCape only", coupling: [keycape]}
|
||||
status: planned
|
||||
status: active
|
||||
- name: svc-privacyidea
|
||||
principal_type: SERVICE
|
||||
conventions: [bind]
|
||||
purpose: "Target: the privacyIDEA resolver's read-only directory lookup"
|
||||
purpose: "The privacyIDEA resolver's read-only directory lookup"
|
||||
grants: [{mode: USE, scope: directory, activation: STANDING, environments: [PRODUCTION], read_only: true}]
|
||||
interactive_login: false
|
||||
second_factor: not_applicable
|
||||
|
|
@ -78,10 +72,10 @@ identities:
|
|||
consumers: [{name: privacyidea, kind: service}]
|
||||
owner: "NetKingdom directory and User Engine owners"
|
||||
rotation: {path: "Resolver bind password, stored encrypted in the privacyIDEA database", coupling: [privacyidea]}
|
||||
status: planned
|
||||
status: active
|
||||
- name: svc-provisioner
|
||||
principal_type: SERVICE
|
||||
purpose: "Target: identity-provisioner; member of lldap_admin, the narrowest LLDAP group that can create users (rehearsal 2026-10-28)"
|
||||
purpose: "identity-provisioner; member of lldap_admin, the narrowest LLDAP group that can create users (rehearsal 2026-10-28)"
|
||||
grants: [{mode: ADMINISTER, scope: directory, activation: STANDING, environments: [PRODUCTION], note: "lldap_admin; exception SI-X6"}]
|
||||
interactive_login: false
|
||||
second_factor: not_applicable
|
||||
|
|
@ -89,16 +83,8 @@ identities:
|
|||
consumers: [{name: identity-provisioner, kind: service}]
|
||||
owner: "NetKingdom directory and User Engine owners"
|
||||
rotation: {path: "Secret rotation with the provisioner only", coupling: [identity-provisioner]}
|
||||
status: planned
|
||||
status: active
|
||||
exceptions:
|
||||
- id: SI-X1
|
||||
rules: [SI-1, SI-2, SI-3, SI-4, SI-9, NAMING]
|
||||
identity: admin
|
||||
reason: "One account is the human, the super-admin and the shared bind of four services, and its password is in a person's safe and in a Kubernetes Secret"
|
||||
owner: "Founder"
|
||||
expires: 2027-01-31
|
||||
compensating_control: "Web UI restricted by IP allowlist; OTP on the human login"
|
||||
elimination_path: "NK-WP-0054-T05: svc-* binds, a provisioner account, ld-admin as the configured super-admin, admin human-only"
|
||||
- id: SI-X6
|
||||
rules: [SI-3]
|
||||
identity: svc-provisioner
|
||||
|
|
|
|||
|
|
@ -266,15 +266,15 @@ class CheckedInDeclarations(unittest.TestCase):
|
|||
agent = [i for r in reports for i in r["identities"] if i["identity"] == "coding-agent"][0]
|
||||
self.assertEqual((agent["result"], agent["rules"]), ("non-conformant", ["SI-14"]))
|
||||
|
||||
def test_the_shared_admin_credential_is_excepted_today_and_fails_after_expiry(self):
|
||||
def admin(as_of):
|
||||
reports, _ = V.run(self.paths(), as_of)
|
||||
return [i for r in reports for i in r["identities"] if r["service"] == "lldap" and i["identity"] == "admin"][0]
|
||||
today = admin(NOW)
|
||||
self.assertEqual(today["result"], "excepted")
|
||||
self.assertEqual(set(today["excepted"]), {"SI-1", "SI-2", "SI-3", "SI-4", "SI-9", "NAMING"})
|
||||
after = admin(dt.date(2027, 2, 1))
|
||||
self.assertEqual(after["result"], "non-conformant")
|
||||
def test_the_bind_split_retired_the_shared_admin_exception(self):
|
||||
# SI-X1 closed 2026-10-31 (NK-WP-0054-T05): admin is human-only, each service binds as its own account.
|
||||
reports, _ = V.run(self.paths(), NOW)
|
||||
lldap = [r for r in reports if r["service"] == "lldap"][0]
|
||||
results = {i["identity"]: i["result"] for i in lldap["identities"]}
|
||||
self.assertEqual(results["admin"], "conformant")
|
||||
self.assertNotIn("planned", results.values())
|
||||
doc = V.load_declaration(DECLARATIONS / "lldap.yaml")
|
||||
self.assertNotIn("SI-X1", [e["id"] for e in doc["exceptions"]])
|
||||
|
||||
def test_no_declaration_claims_owner_confirmation_yet(self):
|
||||
for path in self.paths():
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ state_hub_workstream_id: "7c80ba09-8bd5-540e-8dd3-feb85e201ef3"
|
|||
> **Finished 2026-10-30.** Drill passed 2026-10-29 (next due 2027-01-29, held in disaster-control's
|
||||
> restore-proof register); G1 decided (`e8935fa6`, Scaleway intake under NK-WP-0055-T07).
|
||||
>
|
||||
> **2026-10-31:** second drill passed on generation `20261031T034845Z-a13dc92e2c61` (post bind split, all five
|
||||
> stages as `admin`; [evidence](../docs/evidence/2026-10-31-identity-restore-drill/drill.json)). Next due **2027-01-31**.
|
||||
>
|
||||
> **2026-10-28** (founder triage): T02, T04 and T03 are finished now, not at the 2027-01-31
|
||||
> deadline. One attended `admin` run covers T02 and T04; T03 follows from its results. The
|
||||
> workplan then closes. The quarterly repeat lives in disaster-control's restore-proof register
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ The founder's live export commands are in RPF-WP-0067-T03.
|
|||
|
||||
```task
|
||||
id: NK-WP-0054-T05
|
||||
status: progress
|
||||
status: done
|
||||
priority: high
|
||||
assignee: claude-code
|
||||
needs_human: true
|
||||
|
|
@ -365,6 +365,17 @@ refused because the custody `AUTHELIA_KEYCAPE_CLIENT_SECRET` no longer matches t
|
|||
the next backup generation (closes this task, retires SI-X1) and reconciling the stale custody client
|
||||
secret.
|
||||
|
||||
**Done 2026-10-31: post-split restore drill passed** ([evidence](../docs/evidence/2026-10-31-identity-restore-drill/drill.json)).
|
||||
The founder, operating as `admin`, ran all five NK-WP-0053 stages on generation `20261031T034845Z-a13dc92e2c61`,
|
||||
the first daily capture after the live split: independent retrieval, LLDAP (`admin` with its new human-only
|
||||
password), privacyIDEA (7/7 tokens, 414,440 signed audit rows), Authelia on the archived `svc-authelia`
|
||||
configuration (T17), and `admin`'s OTP through the restored `svc-privacyidea` resolver (T19). Every receipt
|
||||
reports cleanup verified and the transfer digest. Not counted: a stage 1 run without an OpenBao `platform-admin`
|
||||
token (the tool fails silently; the drill document now has the login step), the stage 2 run it fed, and an
|
||||
MFA run that tested `platform-root`. SI-X1 is retired in the standard and `tools/service-identity/declarations/lldap.yaml`
|
||||
(`admin` and the four `svc-*` binds conformant). Follow-up: Credential Management v0.2 section 6 still lists
|
||||
`LLDAP_LDAP_USER_PASS` as the three-way shared credential.
|
||||
|
||||
## Disposition of per-service admin accounts
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue