Some checks failed
CI/CD / typecheck (pull_request) Successful in 55s
CI/CD / test (pull_request) Successful in 1m4s
CI/CD / lint (pull_request) Successful in 2m2s
CI/CD / smoke (pull_request) Failing after 1m36s
CI/CD / build (pull_request) Successful in 4m13s
CI/CD / publish (pull_request) Has been skipped
One-command setup replaces the 6-step manual flow — `mcpctl create
secretbackend bao --type openbao --wizard` takes the OpenBao admin token
once, provisions a narrow policy + token role, mints the first periodic
token, stores it on mcpd, verifies end-to-end, and prints the migration
command. The admin token is NEVER persisted.
The stored credential auto-rotates daily: mcpd mints a successor via the
token role (self-rotation capability is part of the policy it was issued
with), verifies the successor, writes it over the backing Secret, then
revokes the predecessor by accessor. TTL 720h means a week of rotation
failures still leaves 20+ days of runway.
Shared:
- New `@mcpctl/shared/vault` — pure HTTP wrappers (verifyHealth,
ensureKvV2, writePolicy, ensureTokenRole, mintRoleToken, revokeAccessor,
lookupSelf, testWriteReadDelete) and policy HCL builder.
mcpd:
- `tokenMeta Json @default("{}")` on SecretBackend. Self-healing schema
migration — empty default lets `prisma db push` add the column cleanly.
- SecretBackendRotator.rotateOne: mint → verify → persist → revoke-old →
update tokenMeta. Failures surface via `lastRotationError` on the row;
the old token keeps working.
- SecretBackendRotatorLoop: on startup rotates overdue backends, schedules
per-backend timers with ±10min jitter. Stops cleanly on shutdown.
- New `POST /api/v1/secretbackends/:id/rotate` (operation
`rotate-secretbackend` — added to bootstrap-admin's auto-migrated ops
alongside migrate-secrets, which was previously missing too).
CLI:
- `--wizard` on `create secretbackend` delegates to the interactive flow.
All prompts can be pre-answered via flags (--url, --admin-token,
--mount, --path-prefix, --policy-name, --token-role,
--no-promote-default) for CI.
- `mcpctl rotate secretbackend <name>` — convenience verb; hits the new
rotate endpoint.
- `describe secretbackend` renders a Token health section (healthy /
STALE / WARNING / ERROR) with generated/renewal/expiry timestamps and
last rotation error. Only shown when tokenMeta.rotatable is true — the
existing k8s-auth + static-token backends don't surface it.
Tests: 15 vault-client unit tests (shared), 8 rotator unit tests (mcpd),
3 wizard flow tests (cli, including a regression test that the admin
token never appears in stdout). Full suite 1885/1885 (+32). Completions
regenerated for the new flags.
Out of scope (explicit): kubernetes-auth wizard, Vault Enterprise
namespaces in the wizard path, rotation for non-wizard static-token
backends. See plan file for details.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
36 lines
1.5 KiB
TypeScript
36 lines
1.5 KiB
TypeScript
/**
|
|
* OpenBao / Vault policy template for mcpd's wizard-provisioned backend.
|
|
*
|
|
* The policy is deliberately narrow:
|
|
* - Read/write/list/delete under `<mount>/{data,metadata}/<pathPrefix>/*`
|
|
* - Self-rotation: mcpd can mint its successor via the dedicated token role
|
|
* and revoke its predecessor by accessor.
|
|
*
|
|
* Keeping the paths in one place lets the wizard and the rotator agree on
|
|
* exactly which capabilities the stored token has, and lets tests assert the
|
|
* generated HCL is stable.
|
|
*/
|
|
|
|
export interface AppMcpdPolicyConfig {
|
|
/** KV v2 mount name. Default: 'secret'. */
|
|
mount: string;
|
|
/** Path prefix under the mount (the directory mcpd is confined to). Default: 'mcpd'. */
|
|
pathPrefix: string;
|
|
/** Token role name the policy allows self-rotation against. Default: 'app-mcpd-role'. */
|
|
tokenRole: string;
|
|
}
|
|
|
|
export function buildAppMcpdPolicyHcl(cfg: AppMcpdPolicyConfig): string {
|
|
const { mount, pathPrefix, tokenRole } = cfg;
|
|
const prefix = pathPrefix.replace(/^\/|\/$/g, '');
|
|
return [
|
|
`path "${mount}/data/${prefix}/*" { capabilities = ["create", "read", "update"] }`,
|
|
`path "${mount}/metadata/${prefix}/*" { capabilities = ["list", "delete"] }`,
|
|
`path "${mount}/metadata/${prefix}/" { capabilities = ["list"] }`,
|
|
`path "auth/token/create/${tokenRole}" { capabilities = ["create", "update"] }`,
|
|
`path "auth/token/revoke-accessor" { capabilities = ["update"] }`,
|
|
`path "auth/token/lookup-self" { capabilities = ["read"] }`,
|
|
'',
|
|
].join('\n');
|
|
}
|