Schema, validator, fixtures, declarations and tests (40 pass) now use the imported principal_type and authority grants MODE @ SCOPE with activation and environments, estate conventions bind and service-admin, unknown placeholders, and an elimination path on every exception. SI-4 and SI-7 became decidable. Before/after over the same declarations: 1/11/2/5/6 to 1/10/2/5/7 (non-conformant/unknown/excepted/planned/ conformant); the only move is KeyCape's secrets-engine-openbao client, whose scope is established. lldap/admin is now also excepted under SI-4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 328925@bnt-lap001 Assistant-Session: 14b795fd-c632-4c01-8d28-e7e490c7b0ed
135 lines
12 KiB
Markdown
135 lines
12 KiB
Markdown
# 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
|
|
|
|
These are draft-1 classes. Draft-3 of the standard replaces them with the canon's `principal_type` and grants
|
|
`MODE @ SCOPE` (standard §2 has the mapping); the declarations in `tools/service-identity/declarations/` use draft-3.
|
|
|
|
|
|
| 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 | agent (filed as workload in the first draft) | 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 coding agent presents as a service.** The OpenBao role for the coding agent binds `principal_type:
|
|
service`, while IAM Profile v0.3 requires agent tokens to carry `principal_type: agent` and to name the
|
|
agent's mode. This is finding F-1 in the standard (§8). The role's fifteen-minute token and its boundary
|
|
policy already fit the intent of SI-14.
|
|
7. **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 |
|