Bind secrets-engine approval reader to confirmed operator group
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
codex 2026-09-14 01:30:23 +02:00
parent 817c122d07
commit bbff2bfb93
4 changed files with 200 additions and 121 deletions

View file

@ -3,37 +3,18 @@ kind: credential-change-request
schema_version: 1 schema_version: 1
request_type: workload-kv-read request_type: workload-kv-read
title: secrets-engine client-side read of its approval client secret title: secrets-engine client-side read of its approval client secret
status: in_flight status: applied
created: '2026-09-09' created: '2026-09-09'
updated: '2026-09-10' updated: '2026-09-13'
in_flight:
missing_fields:
- openbao.auth
blocking_reason: >-
The reader is an attended operator running the secrets-engine CLI on a
workstation, not an in-cluster identity. The auth binding is therefore
determined in shape but not bindable: method oidc on mount netkingdom, role
secrets-engine-approval-client-read, the three standard NetKingdom redirect
URIs, scopes openid/profile/email/groups, user_claim sub, groups_claim
groups, policy workload-kv-read-secrets-engine-approval-client, ttl 15m --
with the one missing input being the exact group claim for the authorized
operator, which NetKingdom/KeyCape own. A role cannot be created without it
and no placeholder is recorded here. Live inspection on 2026-09-09 found namespace
secrets-engine holding only ServiceAccount secrets-engine with no workload,
which confirms there is no in-cluster consumer to bind instead.
owner: railiance-platform
requester: requester:
agent: claude agent: claude
reason: >- reason: RPF-WP-0035-T06 residual from the completed verifier custody (CCR-2026-0017).
RPF-WP-0035-T06 residual from the completed verifier custody The verifier copy lets KeyCape authenticate the secrets-engine-approval client;
(CCR-2026-0017). The verifier copy lets KeyCape authenticate the it does not let secrets-engine present that client. secrets-engine implemented
secrets-engine-approval client; it does not let secrets-engine present that the consumer on 2026-09-09 (SECRETS-WP-0009-T03, repo revision 9eb07fd, secrets-engine/docs/approval-service-auth.md)
client. secrets-engine implemented the consumer on 2026-09-09 and reads the value from SECRETS_ENGINE_APPROVAL_CLIENT_SECRET_FILE, an explicit
(SECRETS-WP-0009-T03, repo revision 9eb07fd, protected file. This request establishes the separate exact read that fills that
secrets-engine/docs/approval-service-auth.md) and reads the value from file. It reuses the existing version-1 custody and requests no reseed and no rotation.
SECRETS_ENGINE_APPROVAL_CLIENT_SECRET_FILE, an explicit protected file. This
request establishes the separate exact read that fills that file. It reuses
the existing version-1 custody and requests no reseed and no rotation.
review: review:
required: true required: true
required_approvers: required_approvers:
@ -43,37 +24,44 @@ review:
- at: '2026-09-09' - at: '2026-09-09'
reviewer: railiance-platform (claude) reviewer: railiance-platform (claude)
decision: shape_determined_actor_pending decision: shape_determined_actor_pending
comment: >- comment: 'Reader shape determined from owner source rather than assumed: the consumer
Reader shape determined from owner source rather than assumed: the is an operator-run CLI that exchanges the client secret for a short-lived token
consumer is an operator-run CLI that exchanges the client secret for a immediately before each approval request, holds it only in memory, and has no
short-lived token immediately before each approval request, holds it only refresh token, renewal or fallback identity. That makes this an attended operator-workstation
in memory, and has no refresh token, renewal or fallback identity. That lane on the OIDC mount, not an External Secrets lane. The named operator group
makes this an attended operator-workstation lane on the OIDC mount, not an claim is the one input neither this repo nor secrets-engine can supply; NetKingdom/KeyCape
External Secrets lane. The named operator group claim is the one input own it.'
neither this repo nor secrets-engine can supply; NetKingdom/KeyCape own it.
- at: '2026-09-10' - at: '2026-09-10'
reviewer: codex (consumer source review) reviewer: codex (consumer source review)
decision: consumer_procedure_documented decision: consumer_procedure_documented
comment: >- comment: 'Secrets Engine source 98d72ceb1dbcff2d2a80fb8c3058d76777a0ac93, docs/approval-service-auth.md,
Secrets Engine source 98d72ceb1dbcff2d2a80fb8c3058d76777a0ac93, supplies the requested workstation procedure: operator-owned 0700 runtime session
docs/approval-service-auth.md, supplies the requested workstation directory outside Git, new 0600 file, path-only configuration across one claim/consume
procedure: operator-owned 0700 runtime session directory outside Git, operation, EXIT/INT/TERM cleanup, and explicit residual-file handling after
new 0600 file, path-only configuration across one claim/consume operation, a hard interruption. The consumer checks file permissions/location/non-emptiness;
EXIT/INT/TERM cleanup, and explicit residual-file handling after a hard creation, parent custody and removal remain attended responsibilities. No automatic
interruption. The consumer checks file permissions/location/non-emptiness; cleanup or live read proof is claimed. This documents the consumer return; it
creation, parent custody and removal remain attended responsibilities. is not platform-operator/secrets-engine-owner approval, group confirmation or
No automatic cleanup or live read proof is claimed. This documents the authority to apply the role. Existing activation conditions and in_flight status
consumer return; it is not platform-operator/secrets-engine-owner approval, remain.'
group confirmation or authority to apply the role. Existing activation - at: '2026-09-14'
conditions and in_flight status remain. reviewer: operator (user confirmation in Codex session)
decision: approved
comment: The operator explicitly selected net-kingdom-admins and instructed Codex
to bind the approval-client reader to it. This authorizes the exact CCR-2026-0019
policy and OIDC role apply through attended platform-admin authentication. The
existing group is documented in the platform's live-inspected OpenBao binding.
Consumer file custody was reviewed above. This is the operator's instruction,
not a fabricated separate owner vote or approval-engine human decision. Live
delivery verification remains pending and the credential front door remains
non-resolvable.
target: target:
domain: financials domain: financials
tenant: platform tenant: platform
workload: secrets-engine workload: secrets-engine
environment: production environment: production
purpose: >- purpose: Let the authorized operator running the secrets-engine approval CLI fetch
Let the authorized operator running the secrets-engine approval CLI fetch the the secrets-engine-approval client secret into a protected file, without granting
secrets-engine-approval client secret into a protected file, without granting
any in-cluster reader, any sibling path, or any write on the custody path. any in-cluster reader, any sibling path, or any write on the custody path.
openbao: openbao:
mount: platform mount: platform
@ -82,6 +70,28 @@ openbao:
- CLIENT_SECRET - CLIENT_SECRET
policy_name: workload-kv-read-secrets-engine-approval-client policy_name: workload-kv-read-secrets-engine-approval-client
policy_file: openbao/policies/workload-kv-read-secrets-engine-approval-client.hcl policy_file: openbao/policies/workload-kv-read-secrets-engine-approval-client.hcl
auth:
method: oidc
mount: netkingdom
role: secrets-engine-approval-client-workload-kv-read
allowed_redirect_uris:
- https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
- http://localhost:8250/oidc/callback
- http://127.0.0.1:8250/oidc/callback
oidc_scopes:
- openid
- profile
- email
- groups
user_claim: sub
groups_claim: groups
bound_claims:
groups:
- net-kingdom-admins
bound_claims_confirmed: true
policies:
- workload-kv-read-secrets-engine-approval-client
ttl: 15m
access_frontdoor: access_frontdoor:
type: ops-warden type: ops-warden
catalog_id: secrets-engine-approval-client catalog_id: secrets-engine-approval-client
@ -91,82 +101,73 @@ access_frontdoor:
readiness: pending-review readiness: pending-review
delivery: delivery:
surface: operator-workstation surface: operator-workstation
target: >- target: 'Protected file consumed as SECRETS_ENGINE_APPROVAL_CLIENT_SECRET_FILE by
Protected file consumed as SECRETS_ENGINE_APPROVAL_CLIENT_SECRET_FILE by the the secrets-engine approval CLI: owner-only mode, outside any Git work tree, removed
secrets-engine approval CLI: owner-only mode, outside any Git work tree, at the end of the session. No Kubernetes Secret, no ExternalSecret and no reuse
removed at the end of the session. No Kubernetes Secret, no ExternalSecret of the sso verifier Secret keycape-secrets-engine-approval-client.'
and no reuse of the sso verifier Secret keycape-secrets-engine-approval-client.
risk: risk:
classification: high classification: high
notes: notes:
- >- - This is the presenting copy. Anyone holding it can obtain tokens as service:secrets-engine
This is the presenting copy. Anyone holding it can obtain tokens as for approval:read and approval:consume until KeyCape disables the registration.
service:secrets-engine for approval:read and approval:consume until KeyCape The verifier copy admitted in CCR-2026-0017 does not carry that power; the two
disables the registration. The verifier copy admitted in CCR-2026-0017 does must not be conflated because they hold the same value.
not carry that power; the two must not be conflated because they hold the - A file-delivered credential outlives the process that read it. The lane depends
same value. on operator hygiene for removal, which is weaker than an ESO-delivered Secret
- >- bounded by a pod lifetime. That is a consequence of the consumer being a workstation
A file-delivered credential outlives the process that read it. The lane CLI and is recorded rather than engineered away.
depends on operator hygiene for removal, which is weaker than an - approval:consume is a production-effect scope. This lane grants the ability to
ESO-delivered Secret bounded by a pod lifetime. That is a consequence of the present the client; it does not grant approval decisions, which remain with access-engine,
consumer being a workstation CLI and is recorded rather than engineered away. approval-engine and the existing consume gates.
- >- - No reseed and no rotation. Custody is the existing version 1 from the verifier
approval:consume is a production-effect scope. This lane grants the ability activation; a rotation is a distinct version-guarded operation that also invalidates
to present the client; it does not grant approval decisions, which remain the KeyCape verifier copy and needs both owners.
with access-engine, approval-engine and the existing consume gates.
- >-
No reseed and no rotation. Custody is the existing version 1 from the
verifier activation; a rotation is a distinct version-guarded operation that
also invalidates the KeyCape verifier copy and needs both owners.
verification: verification:
positive: positive:
- >- - The bound operator identity reads CLIENT_SECRET from the exact path into a protected
The bound operator identity reads CLIENT_SECRET from the exact path into a file with no value in chat, argv, shell history or logs.
protected file with no value in chat, argv, shell history or logs. - The secrets-engine approval CLI exchanges it for a token with subject service:secrets-engine,
- >- audience approval-engine, tenant tenant:platform and the single requested scope,
The secrets-engine approval CLI exchanges it for a token with subject verified against live JWKS.
service:secrets-engine, audience approval-engine, tenant tenant:platform and
the single requested scope, verified against live JWKS.
negative: negative:
- >- - An identity outside the confirmed group claim cannot authenticate through role
An identity outside the confirmed group claim cannot authenticate through secrets-engine-approval-client-workload-kv-read.
role secrets-engine-approval-client-read. - The role cannot read the approval-engine operator-client path, cannot list any
- >- parent, and cannot write or patch the custody path.
The role cannot read the approval-engine operator-client path, cannot list - No in-cluster ServiceAccount, including secrets-engine/secrets-engine, gains a
any parent, and cannot write or patch the custody path. read through this lane.
- >- - The KeyCape verifier role cannot be substituted for this reader, and this reader
No in-cluster ServiceAccount, including secrets-engine/secrets-engine, gains cannot read the verifier's delivery Secret.
a read through this lane.
- >-
The KeyCape verifier role cannot be substituted for this reader, and this
reader cannot read the verifier's delivery Secret.
activation_conditions: activation_conditions:
- >- - NetKingdom/KeyCape confirm the exact group claim for the authorized operator and
NetKingdom/KeyCape confirm the exact group claim for the authorized operator it is recorded in openbao.auth.bound_claims with bound_claims_confirmed true.
and it is recorded in openbao.auth.bound_claims with - secrets-engine confirms the workstation procedure, file mode and removal step
bound_claims_confirmed true.
- >-
secrets-engine confirms the workstation procedure, file mode and removal step
for SECRETS_ENGINE_APPROVAL_CLIENT_SECRET_FILE. for SECRETS_ENGINE_APPROVAL_CLIENT_SECRET_FILE.
- >- - Policy and OIDC role applied under attended authority (openbao-platform-admin-login,
Policy and OIDC role applied under attended authority founder_required) with metadata-only receipts.
(openbao-platform-admin-login, founder_required) with metadata-only receipts.
- Positive and negative results recorded with non-secret request ids. - Positive and negative results recorded with non-secret request ids.
evidence: [] evidence:
- at: '2026-09-13T23:27:23+00:00'
actor: operator via attended Warden platform-admin OIDC
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as operator via attended Warden platform-admin
OIDC using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-secrets-engine-approval-client'
- 'Auth role metadata write: auth/netkingdom/role/secrets-engine-approval-client-workload-kv-read'
- No secret values were read, written, printed, or accepted in argv.
lifecycle: lifecycle:
deactivate: >- deactivate: Detach the policy from role secrets-engine-approval-client-workload-kv-read
Detach the policy from role secrets-engine-approval-client-read and disable and disable the ops-warden catalog entry. The KeyCape verifier lane and its delivery
the ops-warden catalog entry. The KeyCape verifier lane and its delivery are are unaffected.
unaffected. rotate: 'Shared with CCR-2026-0017: the value is one client secret. Rotation is
rotate: >- minted by KeyCape and rewritten once under attended authority; both the verifier
Shared with CCR-2026-0017: the value is one client secret. Rotation is minted delivery and this reader then see the new version. No independent rotation exists
by KeyCape and rewritten once under attended authority; both the verifier for this lane alone.'
delivery and this reader then see the new version. No independent rotation compromised: KeyCape disables the secrets-engine-approval registration first, since
exists for this lane alone. that is what stops token issuance, then the value is rotated and this role's policy
compromised: >- detached pending review.
KeyCape disables the secrets-engine-approval registration first, since that
is what stops token issuance, then the value is rotated and this role's
policy detached pending review.
state_hub: state_hub:
workplan_id: RPF-WP-0035 workplan_id: RPF-WP-0035
task_id: RPF-WP-0035-T06 task_id: RPF-WP-0035-T06

View file

@ -0,0 +1,15 @@
{
"observed_at": "2026-09-13T23:27:24.125007+00:00",
"ccr_id": "CCR-2026-0019",
"status": "metadata_applied_and_readback_verified",
"role": "secrets-engine-approval-client-workload-kv-read",
"group": "net-kingdom-admins",
"policy": "workload-kv-read-secrets-engine-approval-client",
"ttl_seconds": 900,
"role_request_id": "cc0fc872-3bff-5af0-5898-886e0ec6741e",
"attended_platform_admin": true,
"secret_values_read": false,
"frontdoor_activated": false,
"positive_negative_delivery_verified": false,
"session_revocation": "enclosing Warden command exited 0 after self-revocation and helper cleanup"
}

View file

@ -0,0 +1,58 @@
"""Silent attended CCR-2026-0019 metadata apply and readback; no KV reads."""
import json
import os
from pathlib import Path
import subprocess
from datetime import datetime, timezone
REPO = Path('/home/worsch/railiance-platform')
ROLE = 'secrets-engine-approval-client-workload-kv-read'
POLICY = 'workload-kv-read-secrets-engine-approval-client'
RECEIPT = REPO / 'docs/evidence/2026-09-14-ccr0019-operator-binding.json'
def run(*args):
result = subprocess.run(args, cwd=REPO, capture_output=True, text=True, timeout=60)
if result.returncode:
raise RuntimeError('command_failed')
return result.stdout
def main():
if Path.home().parent.name != '.warden-attended-login' or os.getenv('BAO_TOKEN') or os.getenv('VAULT_TOKEN'):
raise RuntimeError('attended_envelope_required')
identity = json.loads(run('bao', 'token', 'lookup', '-format=json'))['data']
if 'platform-admin' not in identity['policies'] or 'root' in identity['policies']:
raise RuntimeError('platform_admin_required')
run('python3', 'scripts/credential-change.py', 'applier-apply', 'CCR-2026-0019',
'--actor', 'operator via attended Warden platform-admin OIDC',
'--confirm', 'DELEGATED APPLY CCR-2026-0019', '--quiet')
response = json.loads(run('bao', 'read', '-format=json', 'auth/netkingdom/role/' + ROLE))
role = response['data']
expected = {
'bound_claims': {'groups': ['net-kingdom-admins']},
'role_type': 'oidc', 'user_claim': 'sub', 'groups_claim': 'groups',
}
for key, value in expected.items():
if role.get(key) != value:
raise RuntimeError('role_readback_mismatch')
if role.get('token_policies', role.get('policies')) != [POLICY] or role.get('token_ttl', role.get('ttl')) != 900:
raise RuntimeError('policy_or_ttl_mismatch')
actual = run('bao', 'policy', 'read', POLICY)
source = (REPO / 'openbao/policies' / (POLICY + '.hcl')).read_text()
if actual.split() != source.split():
raise RuntimeError('policy_readback_mismatch')
receipt = {
'observed_at': datetime.now(timezone.utc).isoformat(), 'ccr_id': 'CCR-2026-0019',
'status': 'metadata_applied_and_readback_verified', 'role': ROLE,
'group': 'net-kingdom-admins', 'policy': POLICY, 'ttl_seconds': 900,
'role_request_id': response.get('request_id'),
'attended_platform_admin': True, 'secret_values_read': False,
'frontdoor_activated': False, 'positive_negative_delivery_verified': False,
'session_revocation': 'performed separately by enclosing Warden command',
}
RECEIPT.write_text(json.dumps(receipt, indent=2) + '\n')
if __name__ == '__main__':
try:
main()
except Exception:
raise SystemExit(1) from None

View file

@ -303,10 +303,15 @@ remains refused. No live identity, policy, role, custody or verifier was changed
Human approval follows existing INFD-WP-0001-T07/T08; future service requesters Human approval follows existing INFD-WP-0001-T07/T08; future service requesters
need a separate narrow registration, not this withdrawn bundle. need a separate narrow registration, not this withdrawn bundle.
**Remaining unblock:** CCR-2026-0019 needs the exact authorized operator group **Operator binding confirmed, 2026-09-14:** The user explicitly selected
from NetKingdom/KeyCape, then reviewed attended file delivery and its scoped `net-kingdom-admins` and authorized applying CCR-2026-0019. Its OIDC role is
positive/negative proof. Completed CCR-2026-0017/0018 remain closed. T06 stays `secrets-engine-approval-client-workload-kv-read`, aligned with the platform
`wait`; its historical two-reader notes below are superseded for reader 2. applier naming rule; policy and exact KV scope are unchanged. The metadata dry
run passes with no warnings. Attended apply and exact role/policy readback passed; the enclosing Warden
command exited successfully after its self-revocation/cleanup. Receipt:
`docs/evidence/2026-09-14-ccr0019-operator-binding.json`. Scoped delivery proof
remains pending; the front door stays non-resolvable. Completed CCR-2026-0017/0018
remain closed and T06 stays `wait`.
Consumer procedure returned in source, 2026-09-10: Consumer procedure returned in source, 2026-09-10:
`secrets-engine@98d72ceb1dbcff2d2a80fb8c3058d76777a0ac93:docs/approval-service-auth.md` `secrets-engine@98d72ceb1dbcff2d2a80fb8c3058d76777a0ac93:docs/approval-service-auth.md`