Fix OpenBao role payload handoff
This commit is contained in:
parent
e3147b7fd5
commit
f630d5135e
3 changed files with 31 additions and 26 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue