Add response-wrapped operator handoff
secrets-engine wrap writes a single-use OpenBao wrap token to a mode-0600 out-of-repo file and never prints it. KV reads and AppRole secret_ids are wrapped with a 15m TTL cap. Unwrapped secret payloads fail closed. Production wrap remains fail-closed. Assistant: grok Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
This commit is contained in:
parent
2278cefbb3
commit
afd1c8e593
12 changed files with 387 additions and 8 deletions
|
|
@ -9,6 +9,7 @@ Command surface (FR7):
|
|||
provision <catalog-id> --stage <stage> (--from-file F | --generate) --field NAME
|
||||
verify <catalog-id> [--positive] [--negative] [--field NAME] [--negative-token-file F]
|
||||
handoff <catalog-id> --stage <stage> --role-id-file F --secret-id-file F
|
||||
wrap <catalog-id> --out F [--ttl 15m]
|
||||
exec --catalog <catalog-id> [--field NAME] [--mode auto|npm-config|exec-env] -- CMD...
|
||||
route <catalog-id> [--json]
|
||||
revoke <catalog-id>
|
||||
|
|
@ -435,6 +436,37 @@ def cmd_handoff(cfg: Config, args) -> int:
|
|||
return 0
|
||||
|
||||
|
||||
def cmd_wrap(cfg: Config, args) -> int:
|
||||
from secrets_engine.wrap import write_wrapped_handoff
|
||||
|
||||
entry = get_entry(cfg.catalog_dir, args.catalog_id)
|
||||
with _privileged_evidence(
|
||||
cfg, entry, "wrap", detail={"ttl": args.ttl, "out_file": args.out}
|
||||
) as evidence:
|
||||
decision = _require_lane_approval(cfg, entry, "wrap", evidence)
|
||||
evidence.mark_approved(decision)
|
||||
with _open_backend(cfg, args, evidence) as client:
|
||||
result = write_wrapped_handoff(
|
||||
client, entry, out_file=Path(args.out), ttl=args.ttl
|
||||
)
|
||||
print(
|
||||
f"wrote wrap token for lane '{result.catalog_id}' — token not displayed"
|
||||
)
|
||||
print(f" out: {result.out_file}")
|
||||
print(f" ttl: {result.ttl}")
|
||||
print(f" handle: {result.wrap_handle or '-'}")
|
||||
evidence.finish(
|
||||
"wrap-token-written",
|
||||
detail={
|
||||
"out_file": result.out_file,
|
||||
"ttl": result.ttl,
|
||||
"wrap_handle": result.wrap_handle,
|
||||
"creation_path": result.creation_path,
|
||||
},
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_exec(cfg: Config, args) -> int:
|
||||
from secrets_engine.exec_delivery import exec_with_secret
|
||||
entry = get_entry(cfg.catalog_dir, args.catalog)
|
||||
|
|
@ -795,6 +827,16 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
add_token_arg(ha)
|
||||
ha.set_defaults(func=cmd_handoff)
|
||||
|
||||
wr = sub.add_parser(
|
||||
"wrap",
|
||||
help="write a single-use OpenBao wrap token to a file (never printed)",
|
||||
)
|
||||
wr.add_argument("catalog_id")
|
||||
wr.add_argument("--out", required=True, help="mode-0600 wrap-token file outside Git")
|
||||
wr.add_argument("--ttl", default="15m", help="wrap TTL, max 15m (default 15m)")
|
||||
add_token_arg(wr)
|
||||
wr.set_defaults(func=cmd_wrap)
|
||||
|
||||
ex = sub.add_parser("exec", help="run a command with the secret injected for the child only")
|
||||
ex.add_argument("--catalog", required=True)
|
||||
ex.add_argument("--field", default=None)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue