diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 2ac899c..678a48f 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -309,7 +309,7 @@ | 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 | done | — | 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-T02 | wait | — | 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 | diff --git a/canon/standards/service-identity-privilege_v0.1.md b/canon/standards/service-identity-privilege_v0.1.md new file mode 100644 index 0000000..b9b17b3 --- /dev/null +++ b/canon/standards/service-identity-privilege_v0.1.md @@ -0,0 +1,230 @@ +--- +id: netkingdom-service-identity-privilege +type: standard +title: "NetKingdom Service Identity and Privilege v0.1" +domain: netkingdom +status: proposed +version: "0.1" +created: "2026-10-24" +updated: "2026-10-24" +scope: service-account-governance +revision: "draft-1" +owner: net-kingdom +last_reviewed: "2026-10-24" +review_interval: 3m +standard_token: service-identity-privilege_v0.1 +related: + - canon/standards/iam-profile_v0.3.md + - canon/standards/credential-management_v0.2.md + - canon/standards/tenancy-posture_v0.1.md + - canon/standards/security-zones_v0.1.md + - canon/standards/posture-feedback_v0.1.md + - docs/service-identity-inventory.md + - docs/crisis-custody-list.md +--- + +# NetKingdom Service Identity and Privilege v0.1 + +## 0. Status + +**Proposed, draft-1.** It binds nothing until the founder ratifies it and each owner adopts it. +It was started on 2026-10-24 from the founder's direction and from the first inventory +(`docs/service-identity-inventory.md`, NK-WP-0054-T01). Its machine-readable declaration +(NK-WP-0054-T03) and conformance check (T04) do not exist yet. + +The finding behind it: one LLDAP account, `uid=admin`, is at the same time the human platform +administrator, the directory super-admin and the bind credential of Authelia, KeyCape, the +privacyIDEA resolver and the identity-provisioner. One account has one password, so the human +login, four services and every backup bundle share one secret. The Credential Management +Standard §6 already calls rotating it "3-way coordinated"; the inventory found five consumers. + +## 1. Purpose and scope + +This standard says which identities, and which privileges, may exist inside the services that +are part of NetKingdom. It governs the **accounts** in a service's own store, directory or +policy engine. It does not replace: + +- the **token profile**: human, service and agent principals in tokens belong to + [IAM Profile v0.3](iam-profile_v0.3.md); +- **credential storage and rotation procedure**: [Credential Management v0.2](credential-management_v0.2.md); +- **tenant isolation**: [Tenancy Posture](tenancy-posture_v0.1.md) and + [Security Zones](security-zones_v0.1.md). + +In scope: LLDAP, Authelia, KeyCape, privacyIDEA, the identity-provisioner, user-engine and +tenant-engine at their boundary, OpenBao authentication roles and policies, and the platform +consumers of these services. Out of scope: accounts that a tenant creates inside its own +workloads, except where they cross into a NetKingdom service. + +## 2. Vocabulary + +An **identity** is any account, role, client or principal held in a service's own store. A +**credential** is what proves an identity. A **consumer** is a person or system that presents +the credential. Every identity has exactly one **class**. + +| Class | Meaning | Examples today | +| --- | --- | --- | +| `human` | A person's attributable login | `tegwick`, `admin` | +| `break-glass` | Final authority held offline, not routine | `platform-root` (king credential) | +| `bind` | A service's own lookup in another service's directory | planned `svc-authelia` | +| `service-admin` | The administrative account of one service | `pi-admin`, planned `ld-admin` | +| `workload` | A service or agent that obtains tokens or secrets | KeyCape clients, `secrets-engine-openbao` | +| `infrastructure` | A role below the application | database role `privacyidea` | + +## 3. Rules + +Each rule has a stable id so that findings, exceptions and the health check can cite it. + +**SI-1 One identity, one class.** An identity MUST have exactly one class. An account that is +both a person and a service is non-conformant. + +**SI-2 People and services do not share credentials.** A person MUST NOT use a service +credential, and a service MUST NOT use a person's credential. A human's password MUST NOT be +stored in a Secret that a service reads. + +**SI-3 Bind identities.** A `bind` identity MUST be read-only, limited to the least subtree and +operations its consumer needs, unable to sign in interactively, and used by exactly one +consuming service. Two services MUST NOT share a bind credential. Where a service must write +(for example the identity-provisioner creating users), it gets its own `workload` identity with +the narrowest working write scope and is never given the service-admin account. + +**SI-4 Service-admin identities.** A service SHOULD have exactly one local administrative +identity, separate from every bind and every human. It MUST be named for its service (§4), +MUST require a second factor where the service can enforce one, and MUST NOT be used by +automation unless the declaration says so. + +**SI-5 Attributed administration first.** Where a service supports administrative roles granted +to an attributed human identity (for example OpenBao policies bound to a group), day-to-day +administration SHOULD use that route. The local `service-admin` is the sealed fallback and is +listed in the crisis custody list. A service that has only a local administrator (privacyIDEA +today) records that as an exception, not as a norm. + +**SI-6 Break-glass.** There MUST be a break-glass identity, held offline. Using it is an event +that MUST be recorded. It MUST NOT be used for routine operation, for backup validation or as the +test subject of a drill. It authorizes; it does not operate. + +**SI-7 The developer identity.** The developer identity MUST NOT hold break-glass or recovery +custody items and MUST NOT hold a production service credential. It builds and tests. + +**SI-8 Assurance.** Every `human` and `service-admin` identity MUST require a second factor +where the service can enforce one. A role that grants administrative policy SHOULD require the +assurance level itself (for example an `aal2` claim bound in the OpenBao role), not only rely on +an upstream service having checked. A role that does not MUST record why in an exception. + +**SI-9 Custody class.** Every credential MUST declare a custody class: `offline-key`, +`personal-safe`, `openbao-lease`, `delivered-secret` (delivered into the workload by the +platform), or `none` (federated, no stored credential). A credential used by more than one +consumer is non-conformant under SI-3. + +**SI-10 Ownership and rotation.** Every identity MUST name an owner, its consumers and a +rotation path with its coupling (what must change together). Static long-lived binds are a +stated gap under Tenancy Posture §9 (Decision 9.1 and 9.2), not a silent exclusion; their +compensating controls are SI-3 and a restricted network path. + +**SI-11 Declaration and legacy.** Every identity that exists in a service MUST be declared. +An identity that exists and is not declared is non-conformant (`unknown`). A legacy identity +MUST have a disposition (retire, accept, or defer) with an owner and a date. + +**SI-12 Recovery.** Every identity needed to recover a service MUST appear in the crisis custody +list with the answer to "does using it work when SSO and OpenBao are down?". An identity that +cannot be used while its own service is down MUST NOT be the only recovery path. + +**SI-13 Validation is operated by the operator identity.** Restore drills and backup validation +MUST be run by the production operator identity, with that identity's own token as the test +subject, and MUST record which identity ran each stage. + +## 4. Naming + +| Class | Pattern | Notes | +| --- | --- | --- | +| `bind` | `svc-` | One per consuming service; `svc--` if one service needs several | +| `service-admin` | `-admin` | The short form is registered per service (see §9): `pi-admin`; `ld-admin` proposed for LLDAP | +| `break-glass` | `platform-root` | The king credential | +| `human` | the person's own login | `tegwick`, `admin` | +| `workload` | the token subject form already in use | for example `service:secrets-engine`; IAM Profile v0.3 governs the token | + +Names are reserved for their class. A `svc-` or `-admin` name MUST NOT be used for a human. + +## 5. Target shape for today's services (informative) + +This table shows where the rules lead. The authoritative state is the inventory, and the +steps are NK-WP-0054-T05 and T06. + +| Service | `service-admin` | `bind` and write identities | Human route | +| --- | --- | --- | --- | +| LLDAP | `ld-admin` (the built-in super-admin, renamed or replaced after rehearsal) | `svc-authelia`, `svc-keycape`, `svc-privacyidea` read-only; a scoped provisioner identity | `admin` as a normal member of the admin group, no shared password | +| Authelia | none (LDAP backend only) | uses `svc-authelia` | none | +| privacyIDEA | `pi-admin` | resolver uses `svc-privacyidea` | none (local administrator only: exception) | +| KeyCape | none | uses `svc-keycape` | none | +| OpenBao | per its own policy | n/a | `net-kingdom-admins` group to `platform-admin` | + +## 6. Declaration + +Each service publishes one declaration of every identity it holds, in a machine-readable form +defined by NK-WP-0054-T03. For each identity: name, class, purpose, privileges, custody class, +consumers, owner, rotation path and coupling, second-factor requirement, status +(`active`, `legacy`, `planned`) and any exception id. A declaration holds no credential and no +secret-shaped value. + +## 7. Conformance and the health check + +For each identity the result is one of `conformant`, `non-conformant` (with the rule ids), +`excepted` (with the exception id) or `unknown`. Unknown is a finding, not a pass. + +The health check (NK-WP-0054-T04) compares declared against observed state: for example, +which DN each service binds as, which groups the bind identities belong to, and which policies +an admin role carries. It uses read-only metadata that each owner provides and repository +manifests. It MUST NOT read a Secret. Its findings are proposals routed to the identity's owner +through [Posture Feedback](posture-feedback_v0.1.md), with an evidence-freshness bound. It +changes nothing. + +## 8. Exceptions + +An exception is time-boxed and records: the rule, the identity, the reason, the owner, an expiry +date and the compensating control. It is reviewed at the standard's review interval. A rule +without a recorded exception that is broken is non-conformant. + +Initial exceptions at adoption: + +| Id | Rule | Identity | Reason | Until | +| --- | --- | --- | --- | --- | +| SI-X1 | SI-1, SI-2, SI-3 | LLDAP `admin` | One account is the human, the super-admin and the shared bind of five consumers | NK-WP-0054-T05 completes | +| SI-X2 | SI-10 | LDAP binds | Static long-lived credential; LDAP has no lease | Tenancy Posture §9 gap review | +| SI-X3 | SI-11 | `trigger-admin` (privacyIDEA) | Legacy account with uninventoried consumers | NK-WP-0049-T22 disposition | +| SI-X4 | SI-8 | OpenBao `netkingdom-platform-admin` | Role binds on the group claim only in the repository file | Decision on §10 question 1 | +| SI-X5 | SI-5 | privacyIDEA | Only a local administrator is supported | Upstream capability | + +## 9. Relationship to other documents + +- **IAM Profile v0.3** defines the principal types in tokens. This standard governs the + accounts that exist behind them and does not change the token profile. +- **Credential Management v0.2 §6** lists `LLDAP_LDAP_USER_PASS` as a three-way coupling. + On adoption that row is corrected and replaced by the per-service bind accounts. +- **Tenancy Posture §9** treats long-lived credentials as a gap; SI-10 applies it to binds. +- **Security Layer Model v0.9** should be checked for an identity-layer statement this standard + must match; that reconciliation is part of ratification. +- **Crisis custody list** (`docs/crisis-custody-list.md`) is the recovery companion to SI-9 and SI-12. +- The registry of `-admin` forms lives with the declarations (T03) and starts with + `pi-admin` and the proposed `ld-admin`. + +## 10. Open questions + +1. **OpenBao assurance.** Should the `platform-admin` role bind `aal2` itself (SI-8) or rely on + KeyCape's `requireForAll`? The other privileged roles already bind it. +2. **Renaming LLDAP's built-in admin.** The directory's super-admin name is configurable, but + renaming it on an existing directory is untested. It needs a rehearsal in isolation before + `ld-admin` is decided (T06). +3. **The human `admin` in LLDAP.** Does the human keep LLDAP admin-UI rights through group + membership, or administer users only through the governed provisioner path? +4. **The provisioner's narrowest scope.** What is the least privilege that still lets it create + users and groups? It must be tested against the pinned LLDAP image. +5. **Workload clients.** KeyCape's confidential clients are governed by IAM Profile v0.3. This + standard only requires that each is declared, owned and not shared. Is that enough? +6. **Where the declarations live.** In each service's repository (preferred, owners are + accountable) or in this repository (simpler to validate). + +## 11. Adoption + +Structure first, tooling second, as with Tenancy Posture. The order is: ratify the vocabulary and +rules; complete the declarations (T03); run the health check read-only (T04) and publish the +first report; then retire the exceptions one by one, starting with SI-X1 (T05). No exception is +closed by writing a declaration. Each is closed by the live change and an evidence run. diff --git a/workplans/NK-WP-0054-service-identity-policy.md b/workplans/NK-WP-0054-service-identity-policy.md index 725d733..614bcbb 100644 --- a/workplans/NK-WP-0054-service-identity-policy.md +++ b/workplans/NK-WP-0054-service-identity-policy.md @@ -73,10 +73,13 @@ is an uninventoried legacy admin, and OpenBao `platform-admin` binds on a group ```task id: NK-WP-0054-T02 -status: todo +status: wait priority: high assignee: claude-code +needs_human: true +decision_id: "a3582e79-4ece-4902-9bfa-e962aef2b8f2" depends_on: [NK-WP-0054-T01] +blocking_reason: "Draft-1 delivered 2026-10-24. Await the founder review and answers to the six open questions in section 10." state_hub_task_id: "5830e2eb-334a-52fa-9c55-a8c00d89c09f" ``` @@ -90,6 +93,12 @@ 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. +Draft-1 delivered 2026-10-24: `canon/standards/service-identity-privilege_v0.1.md` (proposed). Thirteen +rules (SI-1 to SI-13), naming, a target shape for today's services, the declaration fields, the +conformance result states, five initial exceptions (SI-X1 to SI-X5) and six open questions. It +extends IAM Profile v0.3 and Credential Management v0.2 without changing either. Review is the +founder's, then the service owners named in the inventory. + ## Schema and offline conformance validator ```task