Fix OpenBao role payload handoff

This commit is contained in:
tegwick 2026-06-28 02:33:42 +02:00
parent e3147b7fd5
commit f630d5135e
3 changed files with 31 additions and 26 deletions

View file

@ -104,18 +104,31 @@ Role payload:
} }
``` ```
Equivalent CLI command from an approved OpenBao operator context: Equivalent CLI command from an approved OpenBao operator shell:
```bash ```bash
bao write auth/netkingdom/role/whynot-design-workload-kv-read \ role_payload_file="$(mktemp)"
'bound_claims={"groups":["whynot-design"]}' \ trap 'rm -f "$role_payload_file"' EXIT
groups_claim=groups \ cat >"$role_payload_file" <<'JSON'
policies=workload-kv-read-whynot-design-npm-publish \ {
role_type=oidc \ "bound_claims": {
ttl=15m \ "groups": [
user_claim=sub "whynot-design"
]
},
"groups_claim": "groups",
"policies": "workload-kv-read-whynot-design-npm-publish",
"role_type": "oidc",
"ttl": "15m",
"user_claim": "sub"
}
JSON
bao write auth/netkingdom/role/whynot-design-workload-kv-read @"$role_payload_file"
``` ```
The OpenBao Web UI console may treat `bound_claims={...}` as a string. Use a
raw JSON/API role editor when staying in the UI, or use the shell form above.
## Non-Secret Reads ## Non-Secret Reads
These commands should succeed from an operator-capable identity and do not print These commands should succeed from an operator-capable identity and do not print

View file

@ -375,26 +375,12 @@ def render_plan(ccr: dict[str, Any]) -> str:
return "\n".join(lines) return "\n".join(lines)
def shell_kv_arg(key: str, value: Any) -> str:
if isinstance(value, (dict, list)):
encoded = json.dumps(value, sort_keys=True, separators=(",", ":"))
elif isinstance(value, bool):
encoded = "true" if value else "false"
elif value is None:
encoded = ""
else:
encoded = str(value)
return shlex.quote(f"{key}={encoded}")
def render_operator_commands(ccr: dict[str, Any]) -> str: def render_operator_commands(ccr: dict[str, Any]) -> str:
openbao = ccr["openbao"] openbao = ccr["openbao"]
auth = openbao["auth"] auth = openbao["auth"]
auth_path = f"auth/{auth['mount']}/role/{auth['role']}" auth_path = f"auth/{auth['mount']}/role/{auth['role']}"
payload = auth_payload(ccr) payload = auth_payload(ccr)
role_args = " ".join( role_payload = json.dumps(payload, indent=2, sort_keys=True)
shell_kv_arg(key, payload[key]) for key in sorted(payload)
)
secret_args = " ".join( secret_args = " ".join(
shlex.quote(f"{field}=<enter-through-approved-custody>") shlex.quote(f"{field}=<enter-through-approved-custody>")
for field in openbao["fields"] for field in openbao["fields"]
@ -404,7 +390,12 @@ def render_operator_commands(ccr: dict[str, Any]) -> str:
"# Run from the railiance-platform repo with an approved OpenBao operator token.", "# Run from the railiance-platform repo with an approved OpenBao operator token.",
"set -euo pipefail", "set -euo pipefail",
f"bao policy write {shlex.quote(openbao['policy_name'])} {shlex.quote(openbao['policy_file'])}", f"bao policy write {shlex.quote(openbao['policy_name'])} {shlex.quote(openbao['policy_file'])}",
f"bao write {shlex.quote(auth_path)} {role_args}", 'role_payload_file="$(mktemp)"',
'trap \'rm -f "$role_payload_file"\' EXIT',
'cat >"$role_payload_file" <<\'JSON\'',
role_payload,
"JSON",
f"bao write {shlex.quote(auth_path)} @\"$role_payload_file\"",
"", "",
"# Secret provisioning remains under approved OpenBao/operator custody.", "# Secret provisioning remains under approved OpenBao/operator custody.",
"# Do not paste secret values into Git, State Hub, workplans, logs, or chat.", "# Do not paste secret values into Git, State Hub, workplans, logs, or chat.",
@ -414,7 +405,6 @@ def render_operator_commands(ccr: dict[str, Any]) -> str:
] ]
return "\n".join(lines) return "\n".join(lines)
def validate_or_exit(path: Path) -> tuple[dict[str, Any], list[str]]: def validate_or_exit(path: Path) -> tuple[dict[str, Any], list[str]]:
ccr, errors, warnings = validate_ccr(path) ccr, errors, warnings = validate_ccr(path)
for warning in warnings: for warning in warnings:

View file

@ -144,8 +144,10 @@ class CredentialChangeTests(unittest.TestCase):
"bao write auth/netkingdom/role/whynot-design-workload-kv-read", "bao write auth/netkingdom/role/whynot-design-workload-kv-read",
rendered, rendered,
) )
self.assertIn('role_payload_file="$(mktemp)"', rendered)
self.assertIn('"bound_claims": {', rendered)
self.assertIn( self.assertIn(
'bound_claims={"groups":["whynot-design"]}', 'bao write auth/netkingdom/role/whynot-design-workload-kv-read @"$role_payload_file"',
rendered, rendered,
) )
self.assertIn( self.assertIn(