2026-06-29 16:58:16 +02:00
|
|
|
# warden-sign auth-capability lane
|
|
|
|
|
|
|
|
|
|
`warden-sign` is a non-KV secrets-engine lane for SECRETS-WP-0004. It creates an
|
|
|
|
|
OpenBao ACL policy plus AppRole that lets ops-warden run `warden sign` over the
|
|
|
|
|
HTTP API for the FLEX-WP-0007 T4 production smoke.
|
|
|
|
|
|
|
|
|
|
No token value, AppRole `secret_id`, or SSH private material belongs in Git,
|
|
|
|
|
State Hub, chat, prompts, workplans, or normal logs. State Hub gets pointers
|
|
|
|
|
only; the operator receives `role_id` and `secret_id` out-of-band.
|
|
|
|
|
|
|
|
|
|
## Non-secret pointers
|
|
|
|
|
|
|
|
|
|
| Pointer | Value |
|
|
|
|
|
| --- | --- |
|
|
|
|
|
| OpenBao address | `https://bao.coulomb.social` |
|
|
|
|
|
| SSH mount | `ssh` |
|
|
|
|
|
| Policy | `warden-sign` |
|
|
|
|
|
| AppRole | `warden-sign` |
|
2026-06-30 00:52:05 +02:00
|
|
|
| OIDC auth mount | `netkingdom` |
|
|
|
|
|
| OIDC role | `warden-sign` |
|
|
|
|
|
| OIDC role path | `auth/netkingdom/role/warden-sign` |
|
|
|
|
|
| OIDC bound group | `net-kingdom-admins` |
|
2026-06-29 16:58:16 +02:00
|
|
|
| Token TTL | `15m` |
|
|
|
|
|
| Secret ID TTL | `30m` |
|
|
|
|
|
| Secret ID uses | `1` |
|
|
|
|
|
| Allowed paths | `ssh/sign/agt-role`, `ssh/sign/adm-role`, `ssh/sign/atm-role` |
|
|
|
|
|
| Denied probes | `ssh/sign/unlisted-role`, `ssh/roles/agt-role`, `ssh/config/ca`, `sys/policies/acl/warden-sign`, `auth/token/create`, `identity/entity/id` |
|
|
|
|
|
|
|
|
|
|
## Plan and apply
|
|
|
|
|
|
|
|
|
|
Preview without mutation:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
SECRETS_ENGINE_HUB_URL="" secrets-engine apply warden-sign --stage prod --dry-run
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Live apply requires an approved decision/workplan and a short-lived bootstrap
|
|
|
|
|
token file outside any repo:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
BAO_ADDR=https://bao.coulomb.social \
|
|
|
|
|
secrets-engine apply warden-sign --stage prod \
|
|
|
|
|
--bootstrap-token-file ~/.secrets-engine/bootstrap/prod-warden-sign.token
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The bootstrap token file must be mode `0600`, revocable, and tracked in
|
|
|
|
|
[hardening-backlog.md](hardening-backlog.md) H0 until revoked and shredded.
|
|
|
|
|
|
2026-06-30 00:52:05 +02:00
|
|
|
## OIDC operator login pointer
|
|
|
|
|
|
|
|
|
|
For an attended human operator who has `bao` on the workstation, the intended
|
|
|
|
|
narrow login is:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bao login -method=oidc -path=netkingdom role=warden-sign
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
That role lives at `auth/netkingdom/role/warden-sign`, is bound through the
|
|
|
|
|
`groups` claim to `net-kingdom-admins`, and attaches only the `warden-sign`
|
|
|
|
|
policy. It is not the `platform-admin` role and not the whynot-design KV-read
|
|
|
|
|
role.
|
|
|
|
|
|
|
|
|
|
If the role has not been applied yet, create/update it from an approved OpenBao
|
|
|
|
|
operator context with this non-secret payload:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"role_type": "oidc",
|
|
|
|
|
"allowed_redirect_uris": [
|
|
|
|
|
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
|
|
|
|
|
"http://localhost:8250/oidc/callback",
|
|
|
|
|
"http://127.0.0.1:8250/oidc/callback"
|
|
|
|
|
],
|
|
|
|
|
"oidc_scopes": ["openid", "profile", "email", "groups"],
|
|
|
|
|
"user_claim": "sub",
|
|
|
|
|
"groups_claim": "groups",
|
|
|
|
|
"bound_claims": {
|
|
|
|
|
"groups": ["net-kingdom-admins"]
|
|
|
|
|
},
|
|
|
|
|
"policies": "warden-sign",
|
|
|
|
|
"ttl": "15m"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Equivalent CLI path:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bao write auth/netkingdom/role/warden-sign @warden-sign-oidc-role.json
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
After login, verify the token shape without printing the token value:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bao token capabilities ssh/sign/agt-role
|
|
|
|
|
bao token capabilities ssh/sign/adm-role
|
|
|
|
|
bao token capabilities ssh/sign/atm-role
|
|
|
|
|
bao token capabilities auth/token/create
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The first three should include `update`; the last one should not grant token
|
|
|
|
|
creation/admin capability.
|
|
|
|
|
|
2026-06-29 16:58:16 +02:00
|
|
|
## Handoff
|
|
|
|
|
|
|
|
|
|
Mint a fresh single-use AppRole `secret_id` and write both handoff values to
|
|
|
|
|
mode-0600 files outside any Git worktree:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
install -m 700 -d ~/.secrets-engine/handoff
|
|
|
|
|
BAO_ADDR=https://bao.coulomb.social \
|
|
|
|
|
secrets-engine handoff warden-sign --stage prod \
|
|
|
|
|
--bootstrap-token-file ~/.secrets-engine/bootstrap/prod-warden-sign.token \
|
|
|
|
|
--role-id-file ~/.secrets-engine/handoff/warden-sign.role_id \
|
|
|
|
|
--secret-id-file ~/.secrets-engine/handoff/warden-sign.secret_id
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The command prints only file paths and TTL metadata. It does not print the
|
|
|
|
|
`secret_id`.
|
|
|
|
|
|
|
|
|
|
On CoulombCore, the operator can authenticate without the `bao` CLI by posting
|
|
|
|
|
the file contents to AppRole login and capturing the returned token into process
|
|
|
|
|
state only:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
ROLE_ID="$(cat ~/.secrets-engine/handoff/warden-sign.role_id)"
|
|
|
|
|
SECRET_ID="$(cat ~/.secrets-engine/handoff/warden-sign.secret_id)"
|
|
|
|
|
VAULT_TOKEN="$(
|
|
|
|
|
printf '{"role_id":"%s","secret_id":"%s"}' "${ROLE_ID}" "${SECRET_ID}" \
|
|
|
|
|
| curl -fsS \
|
|
|
|
|
--request POST \
|
|
|
|
|
--header 'Content-Type: application/json' \
|
|
|
|
|
--data @- \
|
|
|
|
|
https://bao.coulomb.social/v1/auth/approle/login \
|
|
|
|
|
| jq -r '.auth.client_token'
|
|
|
|
|
)"
|
|
|
|
|
unset ROLE_ID SECRET_ID
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Run the smoke with the scoped token:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
FLEX_AUTH_EXTERNAL=1 SMOKE_VAULT=1 VAULT_TOKEN="${VAULT_TOKEN}" \
|
|
|
|
|
~/ops-warden/scripts/policy_gate_production_smoke.sh
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Verification
|
|
|
|
|
|
|
|
|
|
After live apply and handoff, verify the scoped AppRole token shape without
|
|
|
|
|
printing the token:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
BAO_ADDR=https://bao.coulomb.social \
|
|
|
|
|
secrets-engine verify warden-sign --positive --negative \
|
|
|
|
|
--bootstrap-token-file ~/.secrets-engine/bootstrap/prod-warden-sign.token
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Positive verification checks `update` on the three allowlisted `ssh/sign` paths.
|
|
|
|
|
Negative verification checks the denial probes lack `update`, `sudo`, and `root`.
|
|
|
|
|
|
|
|
|
|
## State Hub pointer payload
|
|
|
|
|
|
|
|
|
|
Post only non-secret pointers, for example:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"catalog_id": "warden-sign",
|
|
|
|
|
"kind": "auth-capability",
|
|
|
|
|
"addr": "https://bao.coulomb.social",
|
|
|
|
|
"mount": "ssh",
|
|
|
|
|
"policy": "warden-sign",
|
|
|
|
|
"approle": "warden-sign",
|
|
|
|
|
"token_ttl": "15m",
|
|
|
|
|
"secret_id_ttl": "30m",
|
|
|
|
|
"secret_id_num_uses": 1,
|
|
|
|
|
"allowed_paths": ["ssh/sign/agt-role", "ssh/sign/adm-role", "ssh/sign/atm-role"],
|
|
|
|
|
"status": "applied-and-handoff-ready"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Do not include `role_id`, `secret_id`, `VAULT_TOKEN`, token accessors, raw curl
|
|
|
|
|
responses, or smoke logs containing token material.
|
|
|
|
|
|
|
|
|
|
## Revocation and cleanup
|
|
|
|
|
|
|
|
|
|
After the smoke, revoke the scoped token by accessor if available, then remove
|
|
|
|
|
handoff files:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bao token revoke -accessor <accessor>
|
|
|
|
|
shred -u ~/.secrets-engine/handoff/warden-sign.role_id
|
|
|
|
|
shred -u ~/.secrets-engine/handoff/warden-sign.secret_id
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To retire the lane metadata itself:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
BAO_ADDR=https://bao.coulomb.social \
|
|
|
|
|
secrets-engine revoke warden-sign \
|
|
|
|
|
--bootstrap-token-file ~/.secrets-engine/bootstrap/prod-warden-sign.token
|
|
|
|
|
```
|