From a3bab9210b7797ae709d492533c3f2fb01ea8553 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 24 Oct 2026 22:57:23 +0200 Subject: [PATCH] NK-WP-0054-T02: agent ceiling is 60 minutes; ld-admin is LLDAP-only and admin stays the human's name Founder clarifications: the SI-14 token ceiling is 60 minutes (3600 s); ld-admin is for LLDAP only and the name admin remains the human platform administrator's wherever it appears. The LLDAP rehearsal is now introducing ld-admin by configuration and demoting admin, not renaming. The validator rejects a service-admin named admin. Co-Authored-By: Claude Sonnet 5.5 Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 328925@bnt-lap001 Assistant-Session: 14b795fd-c632-4c01-8d28-e7e490c7b0ed --- .../service-identity-privilege_v0.1.md | 25 +++++++++++-------- tools/service-identity/README.md | 2 +- .../service-identity/declarations/lldap.yaml | 2 +- tools/service-identity/tests/test_validate.py | 1 + tools/service-identity/validate.py | 2 +- .../NK-WP-0054-service-identity-policy.md | 13 +++++----- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/canon/standards/service-identity-privilege_v0.1.md b/canon/standards/service-identity-privilege_v0.1.md index e7d9d3d..9d6fdd0 100644 --- a/canon/standards/service-identity-privilege_v0.1.md +++ b/canon/standards/service-identity-privilege_v0.1.md @@ -32,7 +32,7 @@ It was started on 2026-10-24 from the founder's direction and from the first inv (`docs/service-identity-inventory.md`, NK-WP-0054-T01). - **draft-1** (2026-10-24): thirteen rules, naming, five initial exceptions, seven open questions. -- **draft-2** (2026-10-24): the founder's review answered four questions (§10). Added the `agent` class and +- **draft-2** (2026-10-24): the founder's review answered four questions and two clarifications (§10). Added the `agent` class and SI-14; SI-8 now requires the assurance claim for admin-policy roles; the human administrator keeps directory admin rights through group membership; declarations start in this repository and move to each service. Building the offline validator (NK-WP-0054-T03) also made custody a list and broadened SI-X1. @@ -144,7 +144,7 @@ subject, and MUST record which identity ran each stage. **SI-14 Agents.** An agent identity MUST present as an agent: its tokens carry `principal_type: agent` and name the agent's mode, autonomous or delegated (IAM Profile v0.3). It MUST NOT use a human's credential, MUST -have short-lived tokens (this draft proposes a ceiling of 3600 seconds) and MUST be bounded by a named +have short-lived tokens (ceiling 3600 seconds, 60 minutes; decided 2026-10-24) and MUST be bounded by a named boundary policy. A delegated agent names the delegating actor in its tokens. ## 4. Naming @@ -152,13 +152,15 @@ boundary policy. A delegated agent names the delegating actor in its tokens. | 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 | +| `service-admin` | `-admin` | A short form belongs to exactly one service (see §9): `pi-admin` for privacyIDEA; `ld-admin` for LLDAP only | | `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 | | `agent` | the agent's own subject | for example `service:codex:railiance-platform` today; the tokens must carry `principal_type: agent` (SI-14) | -Names are reserved for their class. A `svc-` or `-admin` name MUST NOT be used for a human account. A +Names are reserved for their class. The name `admin` is the human platform administrator's name wherever it +appears (decided 2026-10-24); no service-admin account is called `admin`. A `svc-` or `-admin` name MUST NOT +be used for a human account. A federated role that holds no credential (custody `none`), such as an OpenBao role that maps a group to a policy, is not an account and is not bound by this rule (see §10, question 7). @@ -169,7 +171,7 @@ 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` with directory admin rights through the admin group, own password and OTP | +| LLDAP | `ld-admin` (the directory's configured super-admin, for LLDAP only; `admin` stays the human's account) | `svc-authelia`, `svc-keycape`, `svc-privacyidea` read-only; a scoped provisioner identity | `admin` with directory admin rights through the admin group, own password and OTP | | 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 | @@ -239,7 +241,7 @@ declarations carry the exceptions the validator can evaluate (SI-X1, SI-X3, SI-X 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`. + `pi-admin` and `ld-admin`; each form belongs to one service. ## 10. Decisions and open items @@ -249,20 +251,23 @@ declarations carry the exceptions the validator can evaluate (SI-X1, SI-X3, SI-X 2. The human administrator keeps directory admin rights through an administrative group on their own account (SI-5); the service-admin account is a sealed fallback. 3. Declarations start in this repository and move to each service repository as owners confirm them (§6). -4. Agents get their own class, with SI-14. +4. Agents get their own class, with SI-14, and the token-lifetime ceiling is 60 minutes (3600 seconds). +5. `ld-admin` is for LLDAP only. `admin` stays the human's account on every service, including LLDAP, where the + directory's configured super-admin becomes `ld-admin` and `admin` keeps its administrative rights through + an admin group on its own password and OTP. **Settled unless the founder objects:** KeyCape's confidential clients are governed by IAM Profile v0.3 plus a declaration; the reserved-name rule applies to credential-holding accounts, not to federated roles (§4). **To be rehearsed in isolation, not decided:** -- Renaming LLDAP's built-in admin to `ld-admin` on an existing directory (NK-WP-0054-T06). +- Introducing `ld-admin` as the configured super-admin on an existing directory and demoting `admin` to an + administrative-group member with its own password. This replaces the earlier idea of renaming (NK-WP-0054-T06). - The provisioner's narrowest working privilege against the pinned LLDAP image (T05). - Whether a read-only bind account can be prevented from signing in interactively. If not, SI-3's "unable to sign in" becomes "has a long random password that nobody uses and a read-only group". -**Open:** the ceiling of 3600 seconds in SI-14 is a proposal; the right figure comes from the existing -agent roles (15 minutes today). +**Open:** nothing is open beyond the rehearsals and the owners' review. ## 11. Adoption diff --git a/tools/service-identity/README.md b/tools/service-identity/README.md index 101eb08..ff57502 100644 --- a/tools/service-identity/README.md +++ b/tools/service-identity/README.md @@ -34,7 +34,7 @@ and more than one break-glass identity. SI-1, SI-2, SI-3 (including a credential shared by more than one service), SI-6, SI-8 (including an admin policy without a bound assurance claim), SI-9, SI-11 (overdue legacy disposition), SI-12, SI-14 -(an agent must present as an agent, with a token lifetime at or below the proposed 3600-second ceiling) and +(an agent must present as an agent, with a token lifetime at or below the 3600-second, 60-minute ceiling) and the naming rules (`NAMING`). SI-4 (one admin per service) is an advisory. SI-5, SI-7, SI-10 and SI-13 are not decidable from a declaration. Observed state (what is really configured) is the health check, NK-WP-0054-T04. diff --git a/tools/service-identity/declarations/lldap.yaml b/tools/service-identity/declarations/lldap.yaml index f2921f6..99a1311 100644 --- a/tools/service-identity/declarations/lldap.yaml +++ b/tools/service-identity/declarations/lldap.yaml @@ -29,7 +29,7 @@ identities: recovery: {needed: true, works_with_sso_down: false, in_crisis_list: true} - name: ld-admin classes: [service-admin] - purpose: "Proposed: LLDAP's own administrator, separate from every bind and every person (renaming needs a rehearsal)" + purpose: "LLDAP only: the directory's configured super-admin, separate from every bind and every person; introduced by configuration after a rehearsal" privilege_level: admin interactive_login: true second_factor: unknown diff --git a/tools/service-identity/tests/test_validate.py b/tools/service-identity/tests/test_validate.py index 8518493..ab94298 100644 --- a/tools/service-identity/tests/test_validate.py +++ b/tools/service-identity/tests/test_validate.py @@ -131,6 +131,7 @@ class RuleTests(unittest.TestCase): admin = dict(classes=["service-admin"], privilege_level="admin", second_factor="required", interactive_login=True, consumers=[{"name": "p", "kind": "person"}], custody=["personal-safe"]) self.assertIn("NAMING", self.rules(name="administrator", **admin)) + self.assertIn("NAMING", self.rules(name="admin", **admin)) # `admin` is the human's name everywhere self.assertEqual(self.rules(name="ld-admin", **admin), []) def test_overdue_legacy_disposition_is_a_violation(self): diff --git a/tools/service-identity/validate.py b/tools/service-identity/validate.py index d501ac9..61ba19f 100644 --- a/tools/service-identity/validate.py +++ b/tools/service-identity/validate.py @@ -40,7 +40,7 @@ SECRET_VALUE = re.compile( BIND_NAME = re.compile(r"^svc-[a-z0-9]+(-[a-z0-9]+)*$") ADMIN_NAME = re.compile(r"^[a-z]{2,6}-admin$") -AGENT_MAX_TTL_SECONDS = 3600 # draft-2 proposal (standard section 10); the figure is not settled +AGENT_MAX_TTL_SECONDS = 3600 # 60 minutes, decided 2026-10-24 (standard SI-14) ORDER = {"non-conformant": 0, "unknown": 1, "excepted": 2, "planned": 3, "conformant": 4} diff --git a/workplans/NK-WP-0054-service-identity-policy.md b/workplans/NK-WP-0054-service-identity-policy.md index 38037e0..a1afd64 100644 --- a/workplans/NK-WP-0054-service-identity-policy.md +++ b/workplans/NK-WP-0054-service-identity-policy.md @@ -96,7 +96,7 @@ Profile v0.3, Credential Management v0.2 and the Security Zones standard. Draft-2 (2026-10-24, after the founder's review): decisions applied are OpenBao `platform-admin` binds `aal2`; the human administrator keeps directory admin rights through an admin group; declarations start here and move to the services; agents get their own class and rule SI-14. Rehearsals still to do (not decisions): -renaming LLDAP's built-in admin, the provisioner's narrowest scope, and whether a bind account can be kept +introducing `ld-admin` (LLDAP only) as the configured super-admin and demoting `admin`, the provisioner's narrowest scope, and whether a bind account can be kept from signing in interactively. Finding F-1: the coding-agent role in OpenBao presents as a service against IAM Profile v0.3. Draft-1 was delivered earlier the same day: `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 @@ -174,9 +174,10 @@ Create `svc-authelia`, `svc-keycape` and `svc-privacyidea` as read-only director narrowly scoped provisioner account that can create users and groups without being the directory super-admin (the inventory found these four consumers beside the human; the narrowest working privilege for the provisioner must be tested against the pinned LLDAP image -first). Per the founder's decision the human `admin` keeps directory admin rights through an admin group -on its own account; because today's `admin` is also LLDAP's built-in super-admin, the migration must state -which of the two accounts keeps the name `admin` and rehearse it before any change. Switch the Authelia, KeyCape and privacyIDEA resolver configurations and the +first). Per the founder's decisions the human `admin` keeps its name and its administrative rights through an admin +group on its own password and OTP, and `ld-admin` is LLDAP's configured super-admin only. The migration +therefore introduces `ld-admin` by configuration on the existing directory and demotes `admin`; this is +rehearsed in isolation before any change. Switch the Authelia, KeyCape and privacyIDEA resolver configurations and the provisioner 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`, @@ -205,8 +206,8 @@ state_hub_task_id: "8072b5cf-4161-58b9-8dfc-62fa102ccd16" 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. Also rehearse +inventory and the standard. `ld-admin` is for LLDAP only; `admin` stays the human's name on every service. Introducing `ld-admin` as the +configured super-admin on an existing directory is untested here, so it needs a rehearsal first. Also rehearse whether a read-only bind account can be kept from signing in interactively (SI-3). **Done when:** every service has a recorded disposition and a custody location.