Add credential-change delegated applier flow
This commit is contained in:
@@ -192,16 +192,22 @@ The GitOps contract uses:
|
||||
`ClusterSecretStore`.
|
||||
- OpenBao Kubernetes auth role `external-secrets-issue-core` for the
|
||||
issue-core pilot.
|
||||
- OpenBao Kubernetes auth role `external-secrets-activity-core` for the
|
||||
activity-core/llm-connect provider-secret lane once approved.
|
||||
|
||||
The initial `ClusterSecretStore/openbao` is intentionally limited to the
|
||||
`issue-core` namespace. Broaden it only with a new platform review when another
|
||||
tenant is ready to consume OpenBao through ESO.
|
||||
`ClusterSecretStore/openbao` is limited to the `issue-core` namespace.
|
||||
`ClusterSecretStore/openbao-activity-core` is limited to the `activity-core`
|
||||
namespace and is intended for the llm-connect provider-secret lane. Broaden or
|
||||
add stores only with platform review.
|
||||
|
||||
Configure the OpenBao side without printing token values:
|
||||
|
||||
```bash
|
||||
OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token \
|
||||
make openbao-configure-external-secrets-issue-core
|
||||
|
||||
OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token \
|
||||
make openbao-configure-external-secrets-activity-core
|
||||
```
|
||||
|
||||
The helper keeps Kubernetes auth in local-reviewer mode: OpenBao rereads its
|
||||
|
||||
@@ -156,6 +156,12 @@ scripts/credential-change.py needs-changes CCR-2026-0001 --reviewer <name> --com
|
||||
make credential-change-sync-decision CREDENTIAL_CHANGE=CCR-2026-0001
|
||||
make credential-change-apply-plan CREDENTIAL_CHANGE=CCR-2026-0001
|
||||
make credential-change-operator-commands CREDENTIAL_CHANGE=CCR-2026-0001
|
||||
make credential-change-runbook CREDENTIAL_CHANGE=CCR-2026-0001
|
||||
scripts/credential-change.py runbook CCR-2026-0001 --execute-metadata --actor <operator> --confirm "APPLY CCR-2026-0001"
|
||||
scripts/credential-change.py record-evidence CCR-2026-0001 --actor <operator> --kind positive_verification --result passed --detail "<non-secret audit reference>" --record-state-hub
|
||||
make credential-change-lifecycle-plan CREDENTIAL_CHANGE=CCR-2026-0001 CREDENTIAL_CHANGE_LIFECYCLE_ACTION=deactivate
|
||||
scripts/credential-change.py lifecycle-event CCR-2026-0001 --action compromise --actor <operator> --reason "<non-secret reason>" --detail "<non-secret evidence>" --blast-radius "<non-secret scope>" --follow-up "<task/ref>" --record-state-hub
|
||||
scripts/credential-change.py import-inventory CCR-YYYY-NNNN --title "existing lane" --tenant <tenant> --workload <workload> --environment production --purpose "<purpose>" --kv-path platform/workloads/<tenant>/<workload>/<purpose> --field <FIELD_NAME> --auth-method oidc --auth-mount netkingdom --auth-role <role> --bound-claim groups=<group> --bound-claims-confirmed --frontdoor-type ops-warden --catalog-id <catalog-id> --reason "Imported existing lane without secret values"
|
||||
```
|
||||
|
||||
`apply-plan` and `operator-commands` are intentionally guarded: they refuse
|
||||
@@ -229,6 +235,15 @@ The interactive runbook is the operator bridge:
|
||||
8. record non-secret evidence;
|
||||
9. notify downstream front doors such as ops-warden.
|
||||
|
||||
`credential-change.py runbook <CCR>` renders the checklist and exact final
|
||||
confirmation phrase. `--execute-metadata` is intentionally opt-in and requires
|
||||
that phrase; it uses the local `bao` CLI with ambient approved operator
|
||||
authority, writes only policy/auth metadata, and records a non-secret
|
||||
`metadata_apply` evidence entry. Secret value provisioning stays outside the
|
||||
script through approved OpenBao/operator custody. Verification, activation, and
|
||||
manual custody events are recorded with `record-evidence`, whose comments are
|
||||
scanned for known secret markers before the CCR file or State Hub is updated.
|
||||
|
||||
This lets operators safely drive privileged work without needing to remember
|
||||
every OpenBao command.
|
||||
|
||||
@@ -241,6 +256,16 @@ Every active CCR needs a deactivate and rotate path:
|
||||
- `compromised`: emergency state requiring immediate disablement, rotation,
|
||||
blast-radius notes, and incident follow-up.
|
||||
|
||||
`lifecycle-plan` renders the attended checklist for each case, including the
|
||||
front-door state change and OpenBao metadata disable commands for deactivation
|
||||
or compromise. `lifecycle-event` records the non-secret lifecycle event in the
|
||||
CCR, sets the CCR status, and marks the access front door disabled, pending
|
||||
verification, or compromised as appropriate. For compromise events it accepts
|
||||
non-secret blast-radius notes and follow-up task references. Existing lanes that
|
||||
predate CCRs can be imported with `import-inventory`, which writes a CCR and
|
||||
matching read-policy artifact from metadata only; it never asks for or stores
|
||||
the secret value.
|
||||
|
||||
The workflow must support marking an existing credential or lane as compromised
|
||||
even when the original request predates this system.
|
||||
|
||||
|
||||
127
docs/openbao-approved-automation-delegation.md
Normal file
127
docs/openbao-approved-automation-delegation.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# OpenBao Approved Automation Delegation
|
||||
|
||||
This document specifies the narrow OpenBao metadata surface that approved
|
||||
credential-change automation may mutate. It exists to avoid routine use of broad
|
||||
`platform-admin` while keeping secret values under operator custody.
|
||||
|
||||
## Scope
|
||||
|
||||
The delegated applier is for reviewed metadata only:
|
||||
|
||||
- ACL policies generated from approved CCRs;
|
||||
- auth roles bound to reviewed OIDC claims or Kubernetes service accounts;
|
||||
- credential-broker issuer policies and token roles generated from reviewed
|
||||
grant catalog entries;
|
||||
- readback and capability checks needed to prove the mutation landed.
|
||||
|
||||
It must not read, write, print, wrap, unwrap, or proxy managed secret values.
|
||||
Production secret provisioning remains an attended OpenBao/operator custody
|
||||
step unless a later workplan approves a stronger dual-control flow.
|
||||
|
||||
## Environment Boundaries
|
||||
|
||||
Build and development may use sandbox metadata once a non-production OpenBao
|
||||
mount or namespace is declared. Generated test secrets must stay in the sandbox
|
||||
and must never be copied into State Hub, prompts, Git, or chat.
|
||||
|
||||
The non-production applier policy candidate is
|
||||
`openbao/policies/credential-change-nonprod-applier.hcl`. It currently grants
|
||||
only metadata writes, matching the no-secret-value rule used in production.
|
||||
Any future generated test-secret path needs a separate CCR-backed approval so
|
||||
it cannot silently expand this delegation.
|
||||
|
||||
Test and staging may apply reviewed metadata after owner review. Verification
|
||||
must include positive and negative access checks, and evidence must be
|
||||
non-secret.
|
||||
|
||||
Production may apply only reviewed non-secret metadata. The production applier
|
||||
policy is `openbao/policies/credential-change-prod-applier.hcl`, and every live
|
||||
run must be preceded by `scripts/credential-change.py applier-dry-run <CCR>`.
|
||||
Unapproved CCRs fail closed before any OpenBao mutation is rendered. Live
|
||||
metadata mutation uses `scripts/credential-change.py applier-apply <CCR>` with
|
||||
an exact `DELEGATED APPLY <CCR-ID>` confirmation phrase and the local `bao` CLI
|
||||
under ambient delegated applier authority; the command does not accept OpenBao
|
||||
tokens in argv.
|
||||
|
||||
## Production Mutation Surface
|
||||
|
||||
| Change class | Allowed OpenBao path | Notes |
|
||||
| --- | --- | --- |
|
||||
| Workload KV read policies | `sys/policies/acl/workload-kv-read-*` | Generated from CCR mount/path/field metadata. |
|
||||
| Credential broker issuer policies | `sys/policies/acl/credential-broker-*-issuer` | Generated from grant catalog metadata. |
|
||||
| OIDC workload roles | `auth/netkingdom/role/*-workload-kv-read` | Bound claims must be confirmed before apply. |
|
||||
| Kubernetes workload roles | `auth/kubernetes/role/*` | Bound service accounts/namespaces must be confirmed before apply. |
|
||||
| Credential broker token roles | `auth/token/roles/credential-broker-*` | Child-token roles only; no root or platform-admin policies. |
|
||||
| Self checks | `auth/token/lookup-self`, `sys/capabilities-self` | Read/update only as required by OpenBao. |
|
||||
|
||||
Denied by omission:
|
||||
|
||||
- `platform/data/*`, `platform/metadata/*`, or any other secret value path;
|
||||
- `sys/*` outside the approved ACL policy prefixes;
|
||||
- `auth/*` outside the approved role prefixes;
|
||||
- `identity/*`, unseal/recovery material, audit devices, mounts, and root/admin
|
||||
policy assignment;
|
||||
- wildcard, parent-directory, or mismatched policy and role names.
|
||||
|
||||
## Local Dry-Run Guardrails
|
||||
|
||||
The CCR dry-run is deliberately stricter than the OpenBao ACL policy. It must:
|
||||
|
||||
1. validate the CCR schema and secret-marker scan;
|
||||
2. require CCR status `approved`, `applied`, `verified`, or `active`;
|
||||
3. require `openbao.auth.bound_claims_confirmed=true`;
|
||||
4. require mount `platform` and path `platform/workloads/...` for workload KV
|
||||
requests;
|
||||
5. require policy names to start with `workload-kv-read-` and remain under
|
||||
`openbao/policies/<policy-name>.hcl`;
|
||||
6. require OIDC roles to stay under `auth/netkingdom/role/*-workload-kv-read`;
|
||||
7. require Kubernetes roles to stay under `auth/kubernetes/role/*-workload-kv-read`
|
||||
or `auth/kubernetes/role/*-secrets-read`;
|
||||
8. render only exact policy and auth-role metadata mutations;
|
||||
9. leave secret value writes and front-door activation out of scope.
|
||||
|
||||
`applier-apply` reuses the same guardrails, renders the dry-run payload before
|
||||
mutation, requires exact confirmation, writes only policy/auth-role metadata,
|
||||
and appends non-secret `delegated_metadata_apply` evidence. For approved CCRs it
|
||||
can advance file-backed status to `applied`; for already applied/verified/active
|
||||
CCRs it records idempotent evidence without moving the lifecycle backward.
|
||||
|
||||
## Required Evidence
|
||||
|
||||
Record only non-secret evidence:
|
||||
|
||||
- CCR id and approval/decision reference;
|
||||
- applier identity and timestamp;
|
||||
- policy name and auth role path;
|
||||
- OpenBao request id or audit timestamp;
|
||||
- positive and negative verification references;
|
||||
- front-door activation confirmation after verification.
|
||||
|
||||
## Applier Identity Setup
|
||||
|
||||
`openbao-apply-credential-change-appliers.py` configures the source-owned
|
||||
metadata applier policies and matching OpenBao token roles:
|
||||
|
||||
- `credential-change-nonprod-applier` uses
|
||||
`openbao/policies/credential-change-nonprod-applier.hcl`;
|
||||
- `credential-change-prod-applier` uses
|
||||
`openbao/policies/credential-change-prod-applier.hcl`.
|
||||
|
||||
The token roles allow only their matching applier policy, explicitly disallow
|
||||
`root` and `platform-admin`, disable the default policy, use service tokens,
|
||||
and do not issue tokens by themselves. Token issuance remains an approved
|
||||
custody path outside this setup script. Use
|
||||
`make openbao-credential-change-appliers-dry-run` before any live apply.
|
||||
|
||||
## Current Production Policy Candidate
|
||||
|
||||
`openbao/policies/credential-change-prod-applier.hcl` is the source candidate
|
||||
for a future production applier identity. It is not a substitute for CCR review;
|
||||
it is the OpenBao-side capability envelope used after local dry-run validation.
|
||||
|
||||
## Current Non-Production Policy Candidate
|
||||
|
||||
`openbao/policies/credential-change-nonprod-applier.hcl` is the source
|
||||
candidate for a build/test/staging applier identity. It is intentionally
|
||||
metadata-only until this repo declares a non-production OpenBao mount or
|
||||
namespace and records live positive/negative evidence for that lane.
|
||||
@@ -30,7 +30,7 @@ Ops-warden batch follow-up:
|
||||
| KV mount | `platform` |
|
||||
| OpenBao CLI path | `platform/workloads/coulomb/whynot-design/npm-publish` |
|
||||
| Secret field | `NPM_AUTH_TOKEN` |
|
||||
| Front-door readiness | `applied-pending-verify`, `resolvable=false` until caller verification |
|
||||
| Front-door readiness | `active`, `resolvable=true` in ops-warden |
|
||||
| Read policy | `workload-kv-read-whynot-design-npm-publish` |
|
||||
| Policy file | `openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl` |
|
||||
| OIDC auth mount | `netkingdom` |
|
||||
@@ -57,6 +57,13 @@ Expected ops-warden exec shape after activation:
|
||||
warden access whynot-design-npm-publish --exec -- npm publish
|
||||
```
|
||||
|
||||
Ops-warden confirmed activation in State Hub message
|
||||
`f76d3a9e-a98f-4081-885d-b79d94312699`: selector
|
||||
`whynot-design-npm-publish` is active, resolvable, and wired to this
|
||||
caller-scoped lane. The sibling lanes `issue-core-ingestion-api-key` and
|
||||
`openrouter-llm-connect` remain draft and are tracked separately by
|
||||
`RAILIANCE-WP-0009` and `RAILIANCE-WP-0010`.
|
||||
|
||||
The fetch command returns the secret value to the authenticated caller. Run it
|
||||
only in an attended shell or through a process that consumes the value without
|
||||
logging it.
|
||||
|
||||
Reference in New Issue
Block a user