railiance-platform/docs/credential-lane-designs/secrets-engine-task-execution-entitlement.md
codex e50a0d1890
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Record service JWT review defects and scoped execution entitlement design
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 14:04:48 +02:00

165 lines
11 KiB
Markdown

# Secrets Engine task execution entitlement — Platform owner design
Prepared under RPF-WP-0035-T11; implementation belongs to RPF-WP-0035-T12.
This is a proposed source design, not an installed endpoint, approved CCR,
founder disposition or native acceptance. CCR-2026-0038 stays login-self-only.
No service default changes and no standing service administrative ACL.
## Existing machinery and exact first action
Platform INTENT §§ Stable Interfaces, Secure Custody and Operable/Observable
require a stable constrained backend interface. Platform owns OpenBao enforcement
and credential lifecycle; KeyCape owns identity, access-engine decides, approval-engine
consumes, and Secrets Engine owns the lifecycle PEP. Platform is Staff with declared
Tooling gaps, not a replacement identity or authorization engine.
Current `cli._require_lane_approval` consumes before `_open_backend`, which gives
`apply_plan` the login-self token. That token cannot read/write target ACLs or
AppRole roles. Login proof cannot complete functional Lifecycle adoption.
First action: a new explicit **execute-ccr** interface applying exactly one reviewed
`workload-kv-read` CCR's two structure objects: its generated reader ACL and one
OIDC/Kubernetes role on an existing mount. Refuse mount enable/config, AppRole,
KV data/metadata operations, SecretID issuance, token delivery, arbitrary argv,
free-form policy bodies, broader typed CCR dispatch and generic `apply_plan`.
Generic Secrets Engine apply supports mount/AppRole work; silently translating
that action to this owner interface would change its meaning.
Reuse `credential-change.py`'s workload-kv-read validator, generated policy,
`auth_payload`, readiness/binding checks and metadata applier structure. Existing
`service-jwt-login` remains identity only; existing `custody-generate-grant` supplies
patterns for exact generated policy/issuer/token-role TTL/no-default/cleanup, but
its `platform/workloads/keycape/+` create grant cannot perform structure writes.
A new separately reviewed request type **task-execution-grant** is necessary;
it is not yet in `LANE_MODULES`, so presenting it today must be refused.
## Bound request and interface
Proposed version `platform-task-execution/v1`, initially a founder-attended owner
primitive, not an unattended HTTP service. Input is a bounded private pipe containing
a non-secret request envelope; service authentication is a separate memory-only
credential channel. No credential enters command argv, environment, receipt or Git.
The owner launches the exact reviewed primitive through the existing attended
platform-admin envelope only after the scoped founder go is recorded.
Envelope fields: unique `operation_id`; `action=execute-ccr`; platform realm and
stage; service principal `service:secrets-engine` of type service; consumer catalogue
revision/hash; exact CCR id/content SHA256; all authoritative generated source/body
SHA256s; ordered endpoint/method/payload digests; exact canonical CheckRequest and
its digest; served approval id/claim binding; PDP DecisionEnvelope id/package/version,
not-before/expiry; successful consume binding; owner admission/source revision.
The canonical request includes operation id, CCR digest, catalogue digest and the
whole target/body plan, not merely a friendly lane name. Local owner files are the
source; supplied payloads, file paths or HCL are never applied. Secret-bearing
request fields and unknown fields are refused.
Action/PDP spelling is a new consumer contract to publish with access-engine.
Reuse its canonical digest/claim validation, do not invent an interchangeable
`apply` decision. Service identity and caller subject are independently matched;
a caller assertion of `service:secrets-engine`, a decoded JWT or a successful
human OIDC login is insufficient. Authenticate against CCR38's exact native role
and independently verify lookup creation path, role, effective token/identity
policies and service entity provenance. If the existing metadata does not bind
subject adequately, use KeyCape signature/issuer/audience/expiry validation plus
owner-correlated native role login; do not label role-only metadata a trusted subject.
## Admission, consume and execution ordering
1. Secrets Engine and Platform perform only local/static checks and public
unauthenticated discovery before consume: envelope/schema, owner CCR readiness,
exact source hashes, generated policy/role equality, full target/body plan,
owner primitive revision, journal availability and available bounded lifetime.
The founder's independently approved owner envelope is separate bootstrap
authority; it grants no consumer lifecycle action by itself.
2. Platform durably reserves `(approval_id, request_digest, operation_id)` with a
unique constraint and confirms the exact plan. Journal write must be fsynced
and atomic before acknowledgement. No database content is copied to evidence.
3. Secrets Engine obtains validated claim/PDP correspondence and performs the
existing approval-engine CAS consume for that same request. No protected consumer
login, client-secret read or backend write precedes this step. Platform accepts
neither a caller boolean nor an unsigned copy as proof: it independently validates
the served claim/PDP and verifies consume with approval-engine's authenticated
replay/verification contract. If this is unavailable, it refuses execution.
4. Bind the authenticated service identity after successful consume. Authenticate
once; refusal or timeout burns this admission. Platform atomically transitions
the same reservation to `consume-confirmed`, then `applying` before any effect.
A racing second call cannot acquire the state. Consume idempotency is not permission
to execute twice. Repeated calls return the recorded terminal receipt only.
5. Inside the founder owner envelope, mint an **owner-held ephemeral task token**
with only exact target paths. Apply exactly the pinned typed plan, compare
value-free native readback, revoke the task token, remove only this operation's
temporary grant role/policy after revoke, and finalize the durable receipt.
New approval-engine verification/replay semantics and owner journal are required
implementation gates. They are not supplied by current `ConsumedApproval` dataclasses
or caller receipts. If approval-engine offers only idempotent consume, Platform may
make the authenticated same-digest consume call itself after the client call, but
must verify exact original correspondence and keep the independent execution
journal; never interpret idempotent success as a fresh admission.
Journal states: `reserved → consume-confirmed → applying → verified|failed|unknown`.
Consume/auth failures become `failed`; uncertain consume, lost post-write response,
crash after `applying`, or cleanup failure become `unknown`. None automatically
re-executes. A retry requires owner reconciliation, native readback and a fresh
approval/operation id if another effect is needed. No rollback reuses the consumed
approval; retain partial state and prepare a separately approved exact recovery.
## Backend policy, payload and time enforcement
Ephemeral role/policy names derive from the operation hash and are owner-controlled.
Proposed token: service, orphan, nonrenewable, no default/identity policies, TTL and
explicit maximum at most **120s**, and **12 uses maximum** including cleanup.
Actual expiry is the earliest approval/PDP expiry, founder window expiry, authenticated
identity/session expiry and now+120s; refuse insufficient remaining time. Budget
is a reviewed fixed operation plan, never an arbitrary caller num_uses. Reserve
at least one self-revoke use and refuse exhausted/expired executions.
Grant endpoints are exactly `sys/policies/acl/<CCR reader policy>` and
`auth/<CCR existing mount>/role/<CCR role>` with only required read/create/update;
self lookup/capabilities/revoke have explicit minimal grants. Explicit deny applies
to guarded client-secret data/metadata, token create/renew, secret-id and all
unrelated structure targets. Do not publish blanket deny on all ACL paths if it
would also override the two intended target grants. The target-set allowlist and
native denied sibling/admin/data probes define this exact scope.
The founder-held issuer may mint only the unique task role; the service role
never receives issuer/token-create authority. Existing platform-admin bootstrap
remains founder-attended; no persistent administrative credential is introduced
for Secrets Engine. No task token is returned to the caller or reused by generic
`_open_backend`. Returning one would allow direct OpenBao bypass of lifecycle checks.
OpenBao ACL limits endpoint paths/capabilities, but **cannot constrain the HCL or
role JSON body or bind an approval digest by itself**. Independent Platform mediation
validates the exact body hashes against locally generated owner-approved source
immediately before each call; this is part of backend enforcement. A policy-write
token exported to a caller would let it replace the target ACL with broad powers,
and an auth-role-write token could bind a privileged policy. Therefore raw exact-path
ACL is not sufficient execution authority. Native ACL probes prove endpoint scope;
independent mediator counterexamples must prove body/action/approval scope.
## Evidence, acceptance and rollback
Value-free receipt: proposal/source/CCR/request/decision/consume/journal digests,
principal fingerprint and role, reserved/executed timestamps, fixed endpoint/payload
hashes, native request ids or safe hashes, readback equality, task/session accessor
fingerprints, exact granted TTL/uses, revoke and temporary-object cleanup booleans,
terminal state. No raw tokens, JWTs, full accessors, errors, KV responses or secrets.
Receipt return is separate from protected-action success: unknown cleanup never
reports verified. Retain outcome after client disconnect for safe read-only recovery.
Required implementation and native proofs: intended disposable metadata-only action
passes once; wrong identity/type/issuer/audience/claims, altered CCR/catalogue/body,
wrong action/target, stale revision, TTL/use exhaustion, invalid/expired/mismatched
PDP or consume, missing journal, racing/replayed request, direct raw-token sibling
write, policy-body escalation and role-policy substitution all refuse. Crash/timeout
at reserve/consume/mint/write/readback/revoke/temporary cleanup must preserve unknown
and prevent retry. Native ACL denials and mediator refusals must be distinguished
from transport failure. Restore/delete only the disposable objects by their owning
reviewed recovery action; never broaden the service policy to discover gaps.
The initial pilot does not cover provision/rotate/handoff/wrap/exec/deactivate,
KV destruction or the existing generic AppRole apply. Each needs its own typed
operation/body contract and owner evidence. Full steady-state adoption additionally
needs dispatcher integration, real intended result and accounting/reservation
reconciliation, credential-free caller execution and reviewed default migration.
RPF35-T02 and Secrets T06 cannot close from the login verifier or this design.