Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
165 lines
11 KiB
Markdown
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.
|