NK-WP-0054: service identity policy plan; crisis custody list; NK-WP-0053-T04
The founder's three-identity model and the finding that the human admin is also the directory bind account lead to a proposed workplan: svc-<service> bind accounts, per-service admin accounts, a governing standard and a conformance health check. docs/crisis-custody-list.md is the first concrete list of what unlocks recovery, with unknowns marked TO CONFIRM. NK-WP-0053-T04 extends the quarterly drill to the Authelia and by-user MFA stages with admin as operator. Hub decision routes the three founder gates. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 328925@bnt-lap001 Assistant-Session: 14b795fd-c632-4c01-8d28-e7e490c7b0ed
This commit is contained in:
parent
1ce56365b9
commit
034eacab9b
6 changed files with 280 additions and 0 deletions
|
|
@ -51,6 +51,7 @@
|
|||
| workplan | NK-WP-0051 | finished | — | workplans/NK-WP-0051-sign-in-diagnosis.md |
|
||||
| workplan | NK-WP-0052 | finished | — | workplans/NK-WP-0052-wave2-resource-right-sizing.md |
|
||||
| workplan | NK-WP-0053 | active | — | workplans/NK-WP-0053-identity-restore-drill-cadence.md |
|
||||
| workplan | NK-WP-0054 | proposed | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||
| task | NK-WP-ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||
| task | NK-WP-ADHOC-2026-08-14-T01 | done | — | workplans/ADHOC-2026-08-14.md |
|
||||
|
|
@ -306,6 +307,14 @@
|
|||
| task | NK-WP-0053-T01 | done | — | workplans/NK-WP-0053-identity-restore-drill-cadence.md |
|
||||
| task | NK-WP-0053-T02 | wait | — | workplans/NK-WP-0053-identity-restore-drill-cadence.md |
|
||||
| task | NK-WP-0053-T03 | wait | — | workplans/NK-WP-0053-identity-restore-drill-cadence.md |
|
||||
| task | NK-WP-0053-T04 | todo | — | workplans/NK-WP-0053-identity-restore-drill-cadence.md |
|
||||
| task | NK-WP-0054-T01 | todo | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T02 | todo | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T03 | wait | — | 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 | wait | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T06 | wait | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T07 | todo | — | workplans/NK-WP-0054-service-identity-policy.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 |
|
||||
| intake | NET-IN-0001 | open | — | intakes/intakes.md |
|
||||
|
|
|
|||
56
docs/crisis-custody-list.md
Normal file
56
docs/crisis-custody-list.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Crisis custody list
|
||||
|
||||
NK-WP-0054-T07. Drafted 2026-10-24 from repository documents and the founder's statements
|
||||
that day. **This list holds no secret value, ever.** It names what unlocks recovery, who
|
||||
holds it, where, and whether using it needs the very services being recovered. Anything
|
||||
the repository cannot establish is marked `TO CONFIRM`; the founder confirms each one.
|
||||
|
||||
It belongs with the backup documentation:
|
||||
[disaster recovery guide](disaster-recovery-storage-share.md),
|
||||
[identity restore drill](identity-restore-drill.md) and
|
||||
[Credential Management Standard §4](../canon/standards/credential-management_v0.2.md).
|
||||
That standard names the custody categories. This list makes them concrete.
|
||||
|
||||
## The one rule
|
||||
|
||||
For every row, answer: **does using this item work when SSO and OpenBao are down?**
|
||||
A recovery path that is satisfied only by logging in to something being restored is a
|
||||
circular dependency and is recorded as a gap, not as custody.
|
||||
|
||||
## Roles used below
|
||||
|
||||
| Role | Identity | Used for |
|
||||
| --- | --- | --- |
|
||||
| Crisis operator | `admin` | Runs recovery and routine validation (founder intent, 2026-10-24) |
|
||||
| Authorizer | `platform-root` / king credential | Break-glass authority from offline custody; nothing routine |
|
||||
| Developer | `tegwick` | Builds and tests tooling; holds no recovery keys |
|
||||
|
||||
## Items
|
||||
|
||||
| # | Item | Unlocks | Holder and location | Works with SSO and OpenBao down? | Last proven use | Gap or action |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| 1 | Custodian/backup age private key (recipient `age1zvry…50syr3tsz`, labelled `railiance-backup`) | Decrypts the identity backup bundle and the SOPS custody files | Founder, independently held password safe or offline packet (Standard §4) | Yes, offline | Entered by the founder at hidden prompts 2026-10-24 (LLDAP, Authelia, privacyIDEA stages) | `TO CONFIRM`: number of copies, who else could recover if the founder cannot |
|
||||
| 2 | Storage Share `Backup` account app password | Downloads the protected ciphertext without OpenBao or SSO | Founder, independent custody `TO CONFIRM` | Yes, provider is outside the platform | Fresh retrieval 2026-10-24 | One provider copy only; residual risk accepted until 2027-01-31 (G1, NK-WP-0053) |
|
||||
| 3 | OpenBao unseal shares (2 of 3 required in the restore drill) | Unseals a restored or production OpenBao | Holders `TO CONFIRM` (custody doc: escrow holders "to be named") | Yes, offline | Original shares used in the isolated restore RPF-WP-0064-T07, 2026-10-24 | Name the three holders and where each share is kept; confirm none depends on SSO |
|
||||
| 4 | `platform-root` / king account and factors | Final authority; break-glass | Independently held password safe or offline packet (Standard §4; custody doc) | **No if used through KeyCape**: its login and OTP are validated by the services being recovered | OTP validated against restored privacyIDEA 2026-10-24 (isolated) | `TO CONFIRM` the offline path that works with SSO down (OpenBao shares, item 3) and record it |
|
||||
| 5 | Founder admission signing key (SSH, principal `bernd`) | Signs attempt and restore admissions | Founder, workstation key; verifier anchor `/etc/secrets-engine/admission/allowed_signers` | Yes | Signed the T40 admission 2026-10-23 | `TO CONFIRM` which identity this key belongs to; the anchor is workstation-bound, so a backup of key and anchor is needed |
|
||||
| 6 | `admin` password and OTP token | Day-to-day platform administration and, per intent, recovery operation | Founder; OTP enrolled 2026-10-24 | **No**: login and token checks need KeyCape, LLDAP and privacyIDEA | Token not yet in any restored backup; first restore proof after the 2026-10-25 03:45 UTC capture | Same account as the directory bind used by Authelia and privacyIDEA until the split in NK-WP-0054-T05 |
|
||||
| 7 | `pi-admin` credentials (privacyIDEA internal admin) | Token repair and re-enrolment | Founder's personal password safe (stated 2026-10-24) | Custody yes if the safe opens offline; use needs a running privacyIDEA | Not yet drilled | Confirm the safe is reachable offline and mirrored; add a pi-admin check to the drill |
|
||||
| 8 | Ops bundle (age-encrypted tar of service secrets, `make creds-bundle`) | Point-in-time service secrets | Offsite location `TO CONFIRM` (Standard §7) | Yes, needs item 1 | Not drilled | Confirm location and age; decide whether it duplicates the identity bundle |
|
||||
| 9 | Operator access to the production host or cluster for a real restore (SSH certificate or kubeconfig) | Applying a restore to production | `TO CONFIRM` | `TO CONFIRM`: ops-warden issues SSH certificates; whether that works with SSO down is unknown | Not drilled | Establish the crisis path and record it |
|
||||
|
||||
## Non-secret recovery assets
|
||||
|
||||
These need no custody but must be reachable: the published recovery kit on Storage Share
|
||||
(guide, tools ZIP, checksum; verify the checksum against a trusted copy), the pinned
|
||||
container images, and a trusted checkout of this repository. TLS for `kc.coulomb.social`
|
||||
has no custody: it is reissued through ACME and needs working public DNS and HTTP-01.
|
||||
|
||||
## Maintenance
|
||||
|
||||
- Review at every restore drill (NK-WP-0053), and record the date each item was last
|
||||
proven. An item never proven stays marked as such.
|
||||
- Loss or suspected exposure of any item goes to its owner's rotation and revocation
|
||||
process (Standard §6); this list rotates nothing.
|
||||
- A machine-readable, non-secret record with a freshness check, modelled on
|
||||
`tools/custody-record`, is proposed in NK-WP-0054-T07. Until then this document is the list.
|
||||
|
|
@ -120,6 +120,11 @@ approve a production cutover, including target identity, rollback, DNS/ingress,
|
|||
current consumers, credential dispositions and monitoring. This guide does not
|
||||
authorize a production restore or automatic credential rotation.
|
||||
|
||||
## Who and what unlocks recovery
|
||||
|
||||
The [crisis custody list](crisis-custody-list.md) names every item that unlocks recovery, who holds
|
||||
it and whether it still works with SSO and OpenBao down. It holds no secret values.
|
||||
|
||||
## Schedule, protection and limits
|
||||
|
||||
Daily capture is scheduled at 03:45 UTC with up to five minutes jitter. Hourly
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ effect).
|
|||
|
||||
## Before the drill
|
||||
|
||||
- Review the [crisis custody list](crisis-custody-list.md): every row must have a holder, a location and a last-proven date, and no row may rely on a login to a service being restored. The operator of record is `admin`.
|
||||
- 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.
|
||||
|
|
|
|||
|
|
@ -74,6 +74,32 @@ silently.
|
|||
**Done when:** a dated, verified evidence set exists and the next due date is
|
||||
recorded.
|
||||
|
||||
## Extend the drill to Authelia and by-user MFA, operated by admin
|
||||
|
||||
```task
|
||||
id: NK-WP-0053-T04
|
||||
status: todo
|
||||
priority: medium
|
||||
assignee: claude-code
|
||||
depends_on: [NK-WP-0053-T01]
|
||||
```
|
||||
|
||||
The drill document was written before the Authelia stage (`run-isolated-authelia.sh`) and
|
||||
the by-user MFA stage (`run-isolated-privacyidea-mfa.sh`) existed, and it lists retained-factor
|
||||
MFA as out of scope. Add both stages to `docs/identity-restore-drill.md`, and name `admin`
|
||||
as the operator of record and the MFA test subject (founder intent, 2026-10-24: backup
|
||||
validation is platform operations, with `platform-root` kept for break-glass). Add the check
|
||||
of `docs/crisis-custody-list.md` to the drill's preparation, and record in the evidence which
|
||||
identity ran each stage.
|
||||
|
||||
`admin@coulomb` received an OTP on 2026-10-24, so it is first present in the backup captured
|
||||
after 2026-10-25 03:45 UTC. The first `admin`-operated MFA run must use a generation from
|
||||
after that capture; an older generation holds no token for `admin` and proves nothing.
|
||||
|
||||
**Done when:** the drill document lists all five stages with `admin` as operator, the first
|
||||
`admin`-operated MFA run has passed on a post-enrolment generation, and its evidence names
|
||||
the identity used.
|
||||
|
||||
## Review the G1 residual-risk acceptance
|
||||
|
||||
```task
|
||||
|
|
|
|||
183
workplans/NK-WP-0054-service-identity-policy.md
Normal file
183
workplans/NK-WP-0054-service-identity-policy.md
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
---
|
||||
id: NK-WP-0054
|
||||
type: workplan
|
||||
title: "Service identity and privilege policy, conformance check and bind-account split"
|
||||
domain: infotech
|
||||
repo: net-kingdom
|
||||
status: proposed
|
||||
flavor: implementation
|
||||
owner: claude
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-24"
|
||||
updated: "2026-10-24"
|
||||
related:
|
||||
- NK-WP-0049
|
||||
- NK-WP-0053
|
||||
- NK-WP-0031
|
||||
---
|
||||
|
||||
# NK-WP-0054 — Service identity and privilege policy
|
||||
|
||||
Intake from the founder's direction on 2026-10-24, during NK-WP-0049 backup validation.
|
||||
Three identities exist today: `platform-root` (all-mighty build-mode access, not for
|
||||
production use), `tegwick` (developer) and `admin` (platform administrator, intended
|
||||
for operations and for backup validation and crisis recovery). Findings that led here:
|
||||
|
||||
- The T19 MFA proof ran as `platform-root`, the identity meant to leave production use.
|
||||
- The human `admin` is LLDAP `uid=admin`, which is also the directory bind account for
|
||||
Authelia and the privacyIDEA resolver. One account has one password, so the human
|
||||
password is the service secret held in Kubernetes Secrets and in every backup bundle.
|
||||
An OTP on `admin` protects KeyCape logins but not the bind path.
|
||||
- `pi-admin` already exists for privacyIDEA and is separate; its credentials are in the
|
||||
founder's personal password safe.
|
||||
- No concrete crisis custody list existed. A first draft is `docs/crisis-custody-list.md`.
|
||||
|
||||
Direction: dedicated `svc-<service>` bind accounts, a dedicated admin account per
|
||||
service (`ld-admin`, `pi-admin`, …), a NetKingdom standard that governs which identities
|
||||
and privileges may exist for the services that are part of NetKingdom, and a health check
|
||||
that validates conformance. This plan prepares and proposes; it grants nothing and changes
|
||||
no live service. IAM Profile v0.3 already distinguishes human, service and agent
|
||||
principals in tokens; this standard governs the accounts behind them and must extend,
|
||||
not contradict, that profile and Credential Management Standard v0.2.
|
||||
|
||||
## Inventory current service identities and privileges
|
||||
|
||||
```task
|
||||
id: NK-WP-0054-T01
|
||||
status: todo
|
||||
priority: high
|
||||
assignee: claude-code
|
||||
```
|
||||
|
||||
Read-only inventory from repository manifests, ConfigMaps and owner-provided metadata.
|
||||
No Secret is read. For each NetKingdom service (LLDAP, Authelia, KeyCape, privacyIDEA,
|
||||
OpenBao, user-engine, tenant-engine, flex-auth and the platform consumers) list every
|
||||
identity: name, class (human, break-glass, service bind, service admin, workload),
|
||||
privileges, the credential's custody location, its consumers and its owner.
|
||||
|
||||
**Done when:** `docs/service-identity-inventory.md` lists every identity with its source,
|
||||
and states plainly which entries are unknown and who must confirm them.
|
||||
|
||||
## Draft the Service Identity and Privilege standard
|
||||
|
||||
```task
|
||||
id: NK-WP-0054-T02
|
||||
status: todo
|
||||
priority: high
|
||||
assignee: claude-code
|
||||
depends_on: [NK-WP-0054-T01]
|
||||
```
|
||||
|
||||
Write `canon/standards/service-identity-privilege_v0.1.md` as a proposed standard:
|
||||
identity classes; naming (`svc-<service>` for binds, `<service>-admin` for admins);
|
||||
the rules the founder's findings imply (one identity, one purpose; a bind account is
|
||||
read-only and never a login; no human uses a service credential; humans and admins
|
||||
require MFA; every identity has an owner, a custody class and a rotation path; the
|
||||
break-glass identity is not routine); the exception record; and its relation to IAM
|
||||
Profile v0.3, Credential Management v0.2 and the Security Zones standard.
|
||||
|
||||
**Done when:** the draft is reviewed by the founder and the service owners it names.
|
||||
|
||||
## Schema and offline conformance validator
|
||||
|
||||
```task
|
||||
id: NK-WP-0054-T03
|
||||
status: wait
|
||||
priority: medium
|
||||
assignee: claude-code
|
||||
depends_on: [NK-WP-0054-T02]
|
||||
blocking_reason: "Needs the reviewed T02 standard."
|
||||
```
|
||||
|
||||
A declaration schema in `canon/schemas/` and an offline validator in
|
||||
`tools/service-identity/`, in the style of `tools/tenancy-posture` and
|
||||
`tools/custody-record`: each service declares its identities, the validator checks the
|
||||
declaration against the standard, refuses secret-shaped values and reports counts only.
|
||||
|
||||
**Done when:** valid and invalid fixtures pass and fail as expected, and the declarations
|
||||
for the current services are checked in.
|
||||
|
||||
## Live conformance health check
|
||||
|
||||
```task
|
||||
id: NK-WP-0054-T04
|
||||
status: wait
|
||||
priority: medium
|
||||
assignee: claude-code
|
||||
depends_on: [NK-WP-0054-T03]
|
||||
blocking_reason: "Needs the validator and owner agreement on what read-only metadata each service owner can provide."
|
||||
```
|
||||
|
||||
Compare declared against observed: for example, which DN each consumer's configuration
|
||||
binds as, which group the bind accounts belong to, and which policies the admin roles
|
||||
carry. Observed state comes from owner-provided read-only metadata and repository
|
||||
manifests, never from reading Secrets. Findings feed the existing proposal-only posture
|
||||
feedback (`tools/posture-feedback`) with an evidence-freshness bound. It changes nothing.
|
||||
|
||||
**Done when:** a dated run reports conformant, non-conformant and unknown per identity,
|
||||
with each owner named for every finding.
|
||||
|
||||
## Split the directory bind from the human admin
|
||||
|
||||
```task
|
||||
id: NK-WP-0054-T05
|
||||
status: wait
|
||||
priority: high
|
||||
assignee: claude-code
|
||||
needs_human: true
|
||||
decision_id: "9f0f7a87-7adb-44f6-8360-db6a80e3ceca"
|
||||
depends_on: [NK-WP-0054-T01]
|
||||
blocking_reason: "Production change across LLDAP, Authelia, privacyIDEA and their Secrets; needs the founder's go and the platform owners. The founder runs live Secret and rollout steps."
|
||||
```
|
||||
|
||||
Create `svc-authelia` and `svc-privacyidea` as read-only directory binds, switch the
|
||||
Authelia and privacyIDEA resolver configurations to them, make `admin`'s password
|
||||
human-only and rotate it, and remove the shared secret from the service Secrets. Update
|
||||
the backup profile, the restore adapters (`isolated-lldap.py`, `isolated-authelia.py`,
|
||||
`isolated-privacyidea-mfa.py`, which read the directory password from `lldap-secrets` and
|
||||
rewrite the resolver address) and the restore packet. Prepare guarded helpers and
|
||||
filled-in commands; the founder runs the live steps.
|
||||
|
||||
**Done when:** both services bind with their own read-only accounts, the human `admin`
|
||||
password is not present in any service Secret, a fresh backup restores and passes the T17
|
||||
and T19 drills under the new accounts, and the old shared credential is retired.
|
||||
|
||||
## Disposition of per-service admin accounts
|
||||
|
||||
```task
|
||||
id: NK-WP-0054-T06
|
||||
status: wait
|
||||
priority: medium
|
||||
assignee: claude-code
|
||||
needs_human: true
|
||||
decision_id: "9f0f7a87-7adb-44f6-8360-db6a80e3ceca"
|
||||
depends_on: [NK-WP-0054-T02]
|
||||
blocking_reason: "Founder decision per service: which dedicated admin account exists, its name and its custody."
|
||||
```
|
||||
|
||||
For each service decide whether a dedicated admin account exists and what it is called:
|
||||
LLDAP (`ld-admin`), privacyIDEA (`pi-admin`, existing), Authelia, KeyCape (client
|
||||
registry), OpenBao (the `platform-admin` role) and others. Record the result in the
|
||||
inventory and the standard. LLDAP's built-in admin name is configurable but renaming it
|
||||
on an existing directory is untested here, so it needs a rehearsal before any decision.
|
||||
|
||||
**Done when:** every service has a recorded disposition and a custody location.
|
||||
|
||||
## Maintain the crisis custody list
|
||||
|
||||
```task
|
||||
id: NK-WP-0054-T07
|
||||
status: todo
|
||||
priority: high
|
||||
assignee: claude-code
|
||||
needs_human: true
|
||||
decision_id: "9f0f7a87-7adb-44f6-8360-db6a80e3ceca"
|
||||
blocking_reason: "The founder confirms every TO CONFIRM row in docs/crisis-custody-list.md: share holders, copies of the age key, the crisis path to production, bundle location."
|
||||
```
|
||||
|
||||
Keep `docs/crisis-custody-list.md` current, and propose a non-secret machine-readable
|
||||
record with a freshness check, modelled on `tools/custody-record`, so a drill can verify
|
||||
that each row has a holder, a location and a last-proven date.
|
||||
|
||||
**Done when:** no row is marked `TO CONFIRM`, the freshness check exists and the first
|
||||
`admin`-operated drill (NK-WP-0053) has verified the list.
|
||||
Loading…
Add table
Add a link
Reference in a new issue