NK-WP-0056-T02 owner answer: bounded executor credential lanes; SECRETS-WP-0017 proposed
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 994874@bnt-lap001
Assistant-Session: 6017c416-d619-47eb-bd1a-f670a02d7d87
This commit is contained in:
tegwick 2026-10-31 14:19:34 +01:00
parent 65a3dbe103
commit 8ec5aff1a6
3 changed files with 195 additions and 0 deletions

View file

@ -25,6 +25,7 @@
| workplan | SECRETS-WP-0014 | active | — | workplans/SECRETS-WP-0014-pdp-transport-and-responder-authentication.md |
| workplan | SECRETS-WP-0015 | active | — | workplans/SECRETS-WP-0015-original-keycape-recovery-selector.md |
| workplan | SECRETS-WP-0016 | finished | — | workplans/SECRETS-WP-0016-engine-manifestations.md |
| workplan | SECRETS-WP-0017 | proposed | — | workplans/SECRETS-WP-0017-bounded-executor-lanes.md |
| task | SECRETS-WP-ADHOC-2026-08-21-T01 | done | — | workplans/ADHOC-2026-08-21.md |
| task | SECRETS-WP-ADHOC-2026-08-23-T01 | done | — | workplans/ADHOC-2026-08-23.md |
| task | SECRETS-WP-0001-T01 | done | — | workplans/SECRETS-WP-0001-statehub-bootstrap.md |
@ -103,6 +104,9 @@
| task | SECRETS-WP-0016-T03 | done | — | workplans/SECRETS-WP-0016-engine-manifestations.md |
| task | SECRETS-WP-0016-T04 | done | — | workplans/SECRETS-WP-0016-engine-manifestations.md |
| task | SECRETS-WP-0016-T05 | done | — | workplans/SECRETS-WP-0016-engine-manifestations.md |
| task | SECRETS-WP-0017-T01 | wait | — | workplans/SECRETS-WP-0017-bounded-executor-lanes.md |
| task | SECRETS-WP-0017-T02 | todo | — | workplans/SECRETS-WP-0017-bounded-executor-lanes.md |
| task | SECRETS-WP-0017-T03 | wait | — | workplans/SECRETS-WP-0017-bounded-executor-lanes.md |
| intake | SECRETS-IN-0001 | closed | — | intakes/intakes.md |
| intake | SECRETS-IN-0002 | closed | — | intakes/intakes.md |
| intake | SECRETS-IN-0003 | closed | — | intakes/intakes.md |

View file

@ -0,0 +1,123 @@
# Bounded executor credential lanes: owner answer to NK-WP-0056-T02
Status: **proposal** (secrets-engine owner answer, 2026-10-31). Nothing here is
an approval, a grant, or a live change. Founder and platform-owner decisions are
marked as such.
Request: `net-kingdom/docs/proposals/2026-10-31-nk56-t02-owner-requests.md`
(section "secrets-engine"), hub message `16adf344-92da-443d-a25b-19dc29c5952a`.
Standard under review: NetKingdom Bounded Execution Admission v0.1 (proposed),
example `examples/bounded-execution-admission/bind-rotation-svc-authelia.yaml`.
Pilot executor: `service:nk-bind-executor`, Kubernetes ServiceAccount
`nk-bind-executor` in namespace `sso`. Follow-on work: `SECRETS-WP-0017`.
## What the engine does today (source facts)
| Fact | Source |
| --- | --- |
| Consumer lane policies are **read-only**: `{data_path: ["read"]}` plus optional metadata read. No consumer lane can write. | `src/secrets_engine/roles.py` `lane_policy_paths` |
| Engine-operated delivery auth is AppRole only (`VALID_DELIVERY_AUTH_METHODS = ("approle", "none")`). Kubernetes/ESO/OIDC delivery is recorded as `workload_delivery` metadata and not operated. | `src/secrets_engine/catalog.py`; `SCOPE.md` "Catalog and validation" |
| `auth-capability` lanes grant only `update`, and only on exact `ssh/sign/<role>`. | `roles.py` `AUTH_CAPABILITY_ALLOWED_CAPABILITIES` and path check |
| The engine's own CAS write (`provision`) runs under the engine's stage credential from a mode-0600 file. It is not a capability handed to a workload. | `SCOPE.md` "KV provisioning"; `src/secrets_engine/provision.py` |
| Every production live command currently fails closed (approval-claim endpoint not served). Task-scoped protected execution is designed, not shipped; the service-jwt identity (CCR-2026-0038) is login-only. | `pep-stance.yaml`; `docs/task-scoped-openbao-execution-contract.md`; `SECRETS-WP-0013` |
| The existing NetKingdom bind lanes (CCR-2026-0052..0057) are platform-typed `workload-kv-read` / attended-delivery CCRs with Kubernetes auth roles applied by railiance-platform, not secrets-engine catalog lanes. | `railiance-platform/credential-change-requests/CCR-2026-0052..0057` |
## Question 1: executor OpenBao capabilities (CAS write of svc-authelia, reads for checks)
**Answer: not supported today; needs a change, split between two owners.**
secrets-engine does not orchestrate this lane the way it orchestrates its
AppRole lanes, for three reasons: the lane needs a write, the workload logs in
with Kubernetes auth, and live production commands are gated. Routing the
executor's write through `secrets-engine serve` was considered and rejected for
the pilot. It would put T06 behind two unfinished gates (approval-claim and
`SECRETS-WP-0013`), and the engine would then hold a write capability on behalf
of the executor. The executor should hold only its own 15-minute token.
Proposed split:
- **railiance-platform** (no CCR is created here) owns the Kubernetes auth role
and the ACL policy. It applies them with a new CCR request type, for example
`workload-kv-rotate-cas`, following the CCR-0052/0053 shape. It also sets
`cas_required=true` on the svc-authelia secret's metadata. This is a one-time
attended step. The executor has no metadata capability and cannot undo it.
- **secrets-engine** catalogs the lane as non-secret metadata
(`delivery_auth.management: existing`). It renders the expected policy from
the catalog and refuses any platform policy file that differs from it. That
check covers wildcards, metadata, delete/destroy and `sys/` paths, and token
creation. secrets-engine also gives a read-only `route` pointer. This is
`SECRETS-WP-0017`.
### Proposed lane shape (for founder and platform approval)
| Item | Proposal |
| --- | --- |
| Catalog id | `nk-bind-executor-svc-authelia` (kind `kv`, stage `prod`, risk `high`) |
| Policy name | `net-kingdom-nk-bind-executor` (must not use `admin`) |
| Write path | `platform/data/workloads/net-kingdom/directory-binds/svc-authelia`: `["read", "update"]`. There is no `create`: the path already exists (CAS=0 written in T04), so a deleted path cannot be recreated silently. |
| CAS | `cas_required=true` on that secret's metadata (set by platform). The executor reads the current version from the data response, so it needs no metadata read. |
| Check read | `platform/data/workloads/net-kingdom/synthetic-journey/syn-journey`: `["read"]` |
| Denied (verified as denials) | `platform/metadata/...`, `platform/delete/...`, `platform/destroy/...`, `platform/undelete/...` for both paths; sibling `directory-binds/*`; `auth/token/create`; `sys/` |
| Token self-lifecycle | `auth/token/lookup-self` `["read"]` and `auth/token/revoke-self` `["update"]`, with self-revoke in `finally` (same as the bind-lane pattern) |
| Auth | Kubernetes mount `kubernetes`, role `net-kingdom-nk-bind-executor`, bound SA `nk-bind-executor`, namespace `sso`, audience `openbao`, `bound_claims_confirmed: true` |
| TTL | `ttl`, `token_max_ttl` and `token_explicit_max_ttl` all `15m`; `token_no_default_policy: true`; `token_num_uses` bounded (proposal: 16) |
| Rotation | Nothing long-lived to rotate. The credential is a projected SA token (minted for at most 10m) exchanged for a 15m OpenBao token. |
| Revocation | Platform removes the role binding or the policy (CCR deactivation). Deleting the SA also revokes. Revoke outstanding tokens by accessor. The admission expiry is enforced by the executor runtime and is not OpenBao authority. |
| Dormancy (**founder decision**) | (a) The role exists only during an admitted window: platform applies it at admission and removes it after the receipt. Recommended for the pilot. (b) A standing role whose use is gated only by the admission record. |
| Evidence | Lane id, admission id, actor (SA), exact paths, KV version before and after, accessor fingerprint, pass/fail booleans. Never values. |
The svc-authelia ESO reader role (`net-kingdom-svc-authelia-bind-eso`, CCR-0053)
stays unchanged and separate.
## Question 2: directory credential to set svc-authelia's password in LLDAP
**Answer: secrets-engine can catalog either shape. It recommends a shape that
needs no new privileged account and keeps the dedicated account as a fallback.**
### Preferred: self-rotation with the current bind credential (no new LLDAP account)
The executor already reads the current svc-authelia value (version *n*) under
Q1. It can log in to LLDAP as `svc-authelia` with that value and change that
account's own password. It then writes the new value as version *n+1* (CAS).
Revert follows the same path in reverse: change the password back using the new
value, and write the old value as *n+2*.
- No `lldap_password_manager` membership and no new standing credential. The
blast radius is the one account being rotated.
- No extra secrets-engine lane. Q1's read+update policy covers the whole flow.
- **Must be proven first (net-kingdom / LLDAP deployment owner):** on a
synthetic LLDAP fixture, a bind user such as svc-authelia must be able to log
in and change its own password, for example when it is in
`lldap_strict_readonly`. Account lockout and rate limits must not break
revert. Until this is proven, the shape is a hypothesis.
### Fallback: dedicated LLDAP executor account as a cataloged credential
Use this if self-rotation is not possible.
| Item | Proposal |
| --- | --- |
| LLDAP account | `svc-nk-bind-executor`, member of `lldap_password_manager` only. This can reset passwords of non-admin users, which includes every service bind, so the risk is `high`. |
| Catalog id | `nk-bind-executor-lldap-credential` (kind `kv`, stage `prod`, risk `high`, approval `ccr` plus founder) |
| KV path | `platform/workloads/net-kingdom/directory-binds/svc-nk-bind-executor`, field `LDAP_BIND_PASSWORD` |
| Access | Added to the Q1 executor policy as `["read"]` on that data path. There is no metadata read and no other reader. ESO does not project it, and it lives only in executor memory. |
| Rotation | After each run, the executor changes its own LLDAP password and writes it with CAS. This needs `update` on its own path too. Otherwise the founder rotates it attended with the existing bind-lane setup pattern. The **founder chooses**. |
| Revocation | Remove the account from `lldap_password_manager` or delete the account, revoke the KV read in the policy, and destroy the KV versions (platform). |
| Residual risk | A compromised executor token within 15m can reset any non-admin LLDAP user. This is the main reason the self-rotation shape is preferred. |
## Items needing the founder
1. Approve the Q1 lane shape and the dormancy option (a) or (b).
2. Choose the Q2 shape: self-rotation, after the fixture proof, or the fallback
account with its rotation mode.
3. Approve `SECRETS-WP-0017`. Its catalog entries stay unapplied until the
platform CCR exists and is approved.
## Items for other owners
- **railiance-platform:** a CCR request type for a Kubernetes-auth CAS
read+update lane, `cas_required=true` on the svc-authelia metadata, the role
and policy above, and the deactivation procedure.
- **net-kingdom:** the LLDAP self-password-change proof on a synthetic fixture.
The executor runtime must self-revoke and must refuse to run outside the
admission window.

View file

@ -0,0 +1,68 @@
---
id: SECRETS-WP-0017
type: workplan
title: "Catalog and guard bounded-executor CAS rotation lanes (NK-WP-0056 pilot)"
domain: infotech
repo: secrets-engine
status: proposed
flavor: implementation
owner: secrets-engine
topic_slug: netkingdom
created: "2026-10-31"
updated: "2026-10-31"
depends_on: []
related: [SECRETS-WP-0013]
---
Owner answer to NK-WP-0056-T02 (hub message 16adf344). Design and proposed lane
shapes: `docs/bounded-executor-credential-lanes.md`. The engine catalogs and
statically guards the executor lane. railiance-platform applies the Kubernetes
auth role and policy through its own CCR, and the executor holds only its own
15-minute token. Nothing here is a grant or a live change.
## Model an existing-auth CAS rotation lane in the catalog
```task
id: SECRETS-WP-0017-T01
status: wait
needs_human: true
blocked_on: founder approval of the Q1 lane shape (docs/bounded-executor-credential-lanes.md)
priority: high
```
Add an explicit lane access mode, for example `access: rotate-cas`, for `kv`
lanes with `delivery_auth.management: existing`. It renders `["read",
"update"]` on the exact data path and no `create`, metadata, delete, destroy or
undelete paths. It requires the `cas_required` declaration, a 15m maximum TTL,
risk `high`, and rotation and deactivation owners. Kubernetes auth is recorded
as existing-auth metadata only. Keep the default read-only `lane_policy_paths`
unchanged. Add tests for the rendered policy and for refusal of wildcard, extra
capability and metadata variants.
## Check a platform policy file against the catalog-rendered policy
```task
id: SECRETS-WP-0017-T02
status: todo
priority: medium
```
Add a static, offline comparison of a railiance-platform policy HCL with the
engine-rendered policy for a lane, reported as non-secret pass/fail. Platform
can run it on the CCR policy file before an attended apply. No OpenBao call.
## Add catalog entries for the pilot executor
```task
id: SECRETS-WP-0017-T03
status: wait
needs_human: true
blocked_on: platform CCR for the executor role/policy, and founder choice of LLDAP credential shape
priority: medium
```
Add `nk-bind-executor-svc-authelia` (rotate-cas) and the syn-journey check read
as non-secret entries that reference the platform CCR and NK-WP-0056. If the
founder chooses the fallback LLDAP account, also add
`nk-bind-executor-lldap-credential` (high risk). Use `route` for readiness
pointers only.