NK-WP-0054-T01: service identity inventory; T05 scope corrected to four accounts
Read-only inventory from manifests and files, no Secrets read. The admin credential has five consumers (human, Authelia, KeyCape, the privacyIDEA resolver and the identity-provisioner), so the bind split needs svc-authelia, svc-keycape, svc-privacyidea and a scoped provisioner account. Unknowns and who confirms them are listed. trigger-admin recorded as an uninventoried legacy admin. 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
73870c5d37
commit
597ffbf111
3 changed files with 144 additions and 4 deletions
|
|
@ -308,7 +308,7 @@
|
|||
| 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-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-T03 | wait | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
| task | NK-WP-0054-T04 | wait | — | workplans/NK-WP-0054-service-identity-policy.md |
|
||||
|
|
|
|||
127
docs/service-identity-inventory.md
Normal file
127
docs/service-identity-inventory.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Service identity inventory
|
||||
|
||||
NK-WP-0054-T01. First inventory, 2026-10-24. **Read-only.** It is built from repository
|
||||
manifests, scripts, ConfigMaps, templates and documents, plus the sibling repository
|
||||
`railiance-platform` read as files. No Kubernetes Secret, live service or archive was
|
||||
read, and no credential value appears here. Where the repository cannot establish a
|
||||
fact, the row says **unknown** and names who must confirm it.
|
||||
|
||||
"Established" means the cited file says it. It does not mean the live estate matches;
|
||||
live state is T04's job.
|
||||
|
||||
## Classes used
|
||||
|
||||
| Class | Meaning |
|
||||
| --- | --- |
|
||||
| human | A person's login |
|
||||
| break-glass | Final authority, kept offline, not routine |
|
||||
| bind | A service's directory lookup credential |
|
||||
| service-admin | An administrative account for one service |
|
||||
| workload | A service or agent identity obtaining tokens or secrets |
|
||||
| infrastructure | A database or platform role below the application |
|
||||
|
||||
## People
|
||||
|
||||
| Identity | Class | Established facts | Source | Status |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `platform-root` (king credential) | break-glass | Dedicated final-custody credential, independent of day-to-day accounts; held in an independently held password safe or offline packet. Not for production use (founder, 2026-10-24) | `docs/platform-root-custody.md`, Standard §4 | Established |
|
||||
| `tegwick` | human | Setup operator, Git/Gitea, tooling; "not the desired long-term platform root of trust" | `docs/platform-root-custody.md` | Established |
|
||||
| `admin` | human **and** bind **and** directory super-admin | The human platform admin is the same account as LLDAP `uid=admin` (founder, 2026-10-24); see LLDAP | founder statement; LLDAP rows | Established |
|
||||
| Founder admission key, principal `bernd` | break-glass | SSH signing key for attempt admissions; verifier anchor on the workstation | secrets-engine `docs/founder-admission.md` | Which identity owns it is **unknown**; founder confirms |
|
||||
|
||||
## LLDAP (directory)
|
||||
|
||||
| Identity | Class | Privilege | Credential custody | Consumers | Source | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `uid=admin,ou=people,dc=netkingdom,dc=local` | bind + service-admin + human | Full directory administration, web UI at the IP-restricted admin host | `lldap-secrets` key `LLDAP_LDAP_USER_PASS`; password from KeePassXC or custody for the web UI; present in every identity backup bundle | the human; **Authelia** (LDAP bind); **KeyCape** (LDAP lookups); **privacyIDEA** resolver `lldap-coulomb`; **identity-provisioner** (admin API, as `LLDAP_ADMIN_PASSWORD`) | `authelia/configmap.yaml`, `keycape/bootstrap-config.json` (template), `privacyidea/realm_bootstrap.py`, `identity-provisioner/deployment.yaml`, `lldap/OPERATOR-GROUPS.md` | Established from manifests. KeyCape's live bind is in the Secret `keycape-config`, **not read** |
|
||||
| Other users | human | Seven users existed in the 2026-10-16 backup (count only) | LLDAP database | n/a | restore receipt `actual-isolated-lldap-recovery.json` | **Unknown** names; directory owner confirms |
|
||||
| Groups | policy | Seventeen groups in the same backup (count). Named in the repository: `net-kingdom-admins`, `net-kingdom-users`, `activity-core-operators`, `fluid-telegram-operators` | `lldap/OPERATOR-GROUPS.md`, OpenBao role files | gate Authelia domain rules and OpenBao roles | as cited | Names beyond these **unknown** |
|
||||
|
||||
## Authelia
|
||||
|
||||
| Identity | Class | Privilege | Custody | Consumers | Source | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| LDAP bind as `uid=admin` | bind | Directory super-admin (more than a lookup needs) | `authelia-secrets` `ldap_password` | Authelia | `authelia/configmap.yaml` | Established |
|
||||
| OIDC client `keycape` | workload | Only registered client; `one_factor`; MFA is done by KeyCape and privacyIDEA | verifier in `authelia-secrets`; plaintext in `keycape-config` | KeyCape | `authelia/configmap.yaml` | Established |
|
||||
| Notifier credential | workload | SMTP submission as `platform@coulomb.social` | `authelia-smtp` | Authelia | `authelia/configmap.yaml`, NK-WP-0047 | Gated; activation state **unknown** |
|
||||
|
||||
Authelia has no local user store or admin UI in this configuration (LDAP backend only).
|
||||
|
||||
## KeyCape
|
||||
|
||||
| Identity | Class | Privilege | Custody | Consumers | Source | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| LDAP bind as `uid=admin` | bind | Directory super-admin | `keycape-config` | KeyCape | `keycape/bootstrap-config.json` (template) | Template only; live value **unknown** |
|
||||
| Authelia client `keycape` | workload | Upstream sign-in | `keycape-config` | KeyCape | same | Established |
|
||||
| privacyIDEA factor-reader token | workload | Reads factor state; exact rights **unknown** | `keycape-factor-read`, delivered by railiance-platform (RPF-WP-0040); OpenBao policy `keycape-factor-renewer` | KeyCape | `keycape/deployment.yaml`, `openbao/policies/` | Rights **unknown**; railiance-platform and key-cape confirm |
|
||||
| Confidential clients with secrets in the backup profile: `secrets-engine-openbao`, `secrets-engine-approval`, `secrets-engine-requester`, `approval-engine-operator`, `informed-decision-sitting-requester`, `rapp-qonto` | workload | Per client scopes and audiences | one Kubernetes Secret each | their consuming services | `identity-backup-profile.json`, `keycape/deployment.yaml` | Names established; scopes **unknown** except `secrets-engine-openbao` |
|
||||
| Template clients: `demo-app`, `user-engine-portal`, `netkingdom-bootstrap-console`, `openbao-admin`, `rapp-qonto-client` | workload | Unknown | registry in `keycape-config` | various | `keycape/bootstrap-config.json` | Production registry **not read**; key-cape confirms which exist |
|
||||
|
||||
## privacyIDEA
|
||||
|
||||
| Identity | Class | Privilege | Custody | Consumers | Source | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `pi-admin` | service-admin | Local administrator; policy `pi-admin-all-rights` restricted to `adminuser=pi-admin` | founder's personal password safe (founder, 2026-10-24) | the founder, for token repair | `privacyidea/README.md`, `bootstrap-admin.py` | Established. MFA enrolment for `pi-admin` is **unknown** (README requires attended acceptance) |
|
||||
| `trigger-admin` (legacy) | service-admin | Legacy Keycloak-era account, policy and Secret | Kubernetes Secret | none known | `privacyidea/README.md` | Not deleted; consumers uninventoried; revocation is NK-WP-0049-T22 |
|
||||
| Resolver `lldap-coulomb` bind as `uid=admin` | bind | Directory super-admin | resolver config in the database, encrypted with the enckey | privacyIDEA | `privacyidea/realm_bootstrap.py` | Established |
|
||||
| Database role `privacyidea` | infrastructure | Owner of `privacyidea_db` | `net-kingdom-pg-privacyidea-app` | privacyIDEA | `postgresql/cluster.yaml` | Established |
|
||||
| End-user tokens | human factor | Five tokens in the 2026-10-16 backup (count) | database, encrypted | KeyCape MFA check | restore receipt | `admin@coulomb` token added 2026-10-24, not yet in any backup |
|
||||
|
||||
## identity-provisioner, user-engine, tenant-engine
|
||||
|
||||
| Identity | Class | Privilege | Custody | Consumers | Source | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| LLDAP admin password as `LLDAP_ADMIN_PASSWORD` | bind + service-admin | Full admin API, used to create users and groups | `lldap-secrets` | identity-provisioner | `identity-provisioner/deployment.yaml` | Established |
|
||||
| `identity-provisioner-token` | workload | Service token; only user-engine pods may reach the provisioner | Kubernetes Secret | user-engine | same | Established |
|
||||
| user-engine ServiceAccount and database role `user-engine-pg-app` | workload, infrastructure | Token automount off; database owner | Secrets | user-engine | `user-engine/runtime.yaml`, `backup.yaml` | Established |
|
||||
| tenant-engine | workload | Owner pointers only in this repository | n/a | n/a | `tenant-engine/runtime.yaml` | Owner repository holds it; **unknown** here |
|
||||
|
||||
## OpenBao (owner: railiance-platform; read as files)
|
||||
|
||||
| Identity or role | Class | Privilege | Source | Status |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| OIDC role `netkingdom-platform-admin` | human | Group `net-kingdom-admins` receives policies `platform-admin` and `operator-custody`, one-hour token | `openbao/auth/netkingdom-platform-admin-role.json` | Established. The role file binds on the group only; it has no `aal2` claim bound |
|
||||
| OIDC role for credential-broker | human | Group `net-kingdom-admins` **and** `aal2` receive the credential-broker policy | `credential-broker-keycape-client-secret-generate-oidc-role.json` | Established |
|
||||
| OIDC role for the fluid-telegram operators | human | Group `fluid-telegram-operators` and `aal2` | `fluid-telegram-hall-of-helix-oidc-role.json` | Established |
|
||||
| JWT role `secrets-engine-openbao` | workload | Subject `service:secrets-engine`, scope `openbao:login`, policy `secrets-engine-login-self`, five-minute token | `secrets-engine-jwt-role.json` | Established |
|
||||
| JWT role and AppRole for the coding agent | workload | Subject `service:codex:railiance-platform`; policies `agent-high-risk-boundary`, `workload-kv-read-issue-core-runtime`; fifteen minutes | `coding-agent-jwt-role.json`, `coding-agent-approle.json` | Established |
|
||||
| ESO roles, backup publisher and retention policies | workload | One policy per delivery, for example `net-kingdom-backup-publisher-eso` and `net-kingdom-backup-retention-eso` | `openbao/policies/` | Established as files; live roles **unknown** |
|
||||
| Root grant, unseal shares | break-glass | Final authority | `tools/custody-record`, custody docs | Holders **unknown**; see the crisis custody list |
|
||||
|
||||
## Other consumers
|
||||
|
||||
flex-auth (six consumers enforcing caller authentication, per `SCOPE.md`), the secrets-engine
|
||||
runtime and the platform `rapp-*` packages hold their own identities in their own
|
||||
repositories. They are **unknown** here and each owner must supply a declaration (T03).
|
||||
|
||||
## Observations that change the plan
|
||||
|
||||
1. **One credential, five consumers.** The `admin` password is used by the human, Authelia,
|
||||
KeyCape, the privacyIDEA resolver and the identity-provisioner. The bind split in T05
|
||||
therefore needs four service accounts, not two: `svc-authelia`, `svc-keycape` and
|
||||
`svc-privacyidea` as read-only lookups, and a provisioner account that can create users and
|
||||
groups but is not the directory super-admin.
|
||||
2. **The provisioner is the hard case.** It uses the admin API, so a read-only group is not
|
||||
enough. What the narrowest working privilege is must be tested against the pinned LLDAP
|
||||
image before T05.
|
||||
3. **Three bind consumers need only read access** but hold super-admin today.
|
||||
4. **`trigger-admin` is an uninventoried legacy admin.** It is outside this inventory until its
|
||||
consumers are established (NK-WP-0049-T22).
|
||||
5. **OpenBao `platform-admin` binds on a group claim alone** in the repository file, while the
|
||||
credential-broker and fluid-telegram roles also require `aal2`. MFA is enforced earlier by
|
||||
KeyCape (`requireForAll: true`), so this may be intended, but the policy should say whether
|
||||
OpenBao must assert the assurance level itself. This is a question for T02, not a finding of a defect.
|
||||
6. **The backup contains the admin password** (the `lldap-secrets` Secret). That is by design
|
||||
for recovery, and it is another reason the human and service uses must be separated.
|
||||
|
||||
## What must be confirmed
|
||||
|
||||
| Unknown | Who confirms |
|
||||
| --- | --- |
|
||||
| Names of the seven users and seventeen groups; which are in `net-kingdom-admins` | LLDAP and User Engine owners |
|
||||
| Live KeyCape registry, live LDAP bind, factor-reader rights | key-cape and railiance-platform |
|
||||
| Scopes of the confidential clients other than `secrets-engine-openbao` | key-cape |
|
||||
| Whether `trigger-admin` still exists and who uses it | founder (NK-WP-0049-T22) |
|
||||
| Live OpenBao roles versus these files | railiance-platform |
|
||||
| flex-auth and `rapp-*` identities | their owners |
|
||||
| MFA state of `pi-admin` | founder |
|
||||
|
|
@ -45,7 +45,7 @@ not contradict, that profile and Credential Management Standard v0.2.
|
|||
|
||||
```task
|
||||
id: NK-WP-0054-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
assignee: claude-code
|
||||
state_hub_task_id: "422918a5-c695-5fe3-ba3c-2b370b2ef4dc"
|
||||
|
|
@ -60,6 +60,15 @@ 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.
|
||||
|
||||
Completed 2026-10-24. `docs/service-identity-inventory.md` covers people, LLDAP, Authelia, KeyCape,
|
||||
privacyIDEA, the identity-provisioner and engines, and OpenBao (railiance-platform files read
|
||||
read-only). No Secret or live service was read. It ends with a table of what is unknown and who
|
||||
confirms it. Main finding: the `admin` credential has **five** consumers (the human, Authelia,
|
||||
KeyCape, the privacyIDEA resolver and the identity-provisioner), so T05 needs four service
|
||||
accounts, not two. Also recorded: the provisioner needs more than read access, `trigger-admin`
|
||||
is an uninventoried legacy admin, and OpenBao `platform-admin` binds on a group claim alone
|
||||
(a question for T02, not a defect).
|
||||
|
||||
## Draft the Service Identity and Privilege standard
|
||||
|
||||
```task
|
||||
|
|
@ -136,8 +145,12 @@ blocking_reason: "Production change across LLDAP, Authelia, privacyIDEA and thei
|
|||
state_hub_task_id: "c43d339a-fc8a-5526-885a-64693c930005"
|
||||
```
|
||||
|
||||
Create `svc-authelia` and `svc-privacyidea` as read-only directory binds, switch the
|
||||
Authelia and privacyIDEA resolver configurations to them, make `admin`'s password
|
||||
Create `svc-authelia`, `svc-keycape` and `svc-privacyidea` as read-only directory binds and a
|
||||
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). 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`,
|
||||
`isolated-privacyidea-mfa.py`, which read the directory password from `lldap-secrets` and
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue