Reviewable governance scaffolding — NOTHING applied to live OpenBAO yet: - policies/: materialize all existing policies as code (faithfully fetched from live) + new `operator` (use engines, no admin) and `auditor` (read-only governance visibility, no secret material) - scripts/apply-policies.sh: idempotent policy-as-code apply, with a read-only --dry-run that diffs files vs live (ignores comments) - scripts/setup-identity.sh: identity-as-code — policy-bound groups (g-admins/operators/auditors/personal) + a human entity/alias; DEFAULT DRY-RUN, --apply to execute - GOVERNANCE.md: the layered model, policy catalog, naming, apply order, and cross-cutting controls (audit device, root offline, AppRole migration) Dry-runs verified read-only: apply-policies shows operator/auditor as NEW, all others unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.8 KiB
OpenBAO Governance Blueprint
A layered access model for this instance. Everything here is policy-as-code: edit files, review in git, then apply with the scripts. Nothing is live until you run the apply scripts.
Model
auth method → identity (entity / group) → policy → paths
(how you log in) (who you are) (what's allowed) (where)
Rule of thumb: bind policies to GROUPS (humans) and ROLES (machines) — not to individuals or scattered standalone tokens. Access then = "which groups/roles am I in".
Policy catalog (policies/*.hcl)
| Policy | Kind | Grants |
|---|---|---|
admin |
human | platform administration (auth, mounts, policies, engines) — not root-only ops |
operator |
human | day-to-day use of engines (KV, issue certs, sign SSH, transit) — no administration |
auditor |
human | read-only governance visibility — config + policies, no secret material |
personal-rw |
human | the isolated personal/ KV vault |
demo-app |
machine | read secret/demo-app/* (AppRole example) |
gitea-push-ro |
machine | read secret/gitea/push (git credential helper) |
cert-renew |
machine | pki_int/issue/famfi-home (cert renewal timer) |
backup |
machine | sys/storage/raft/snapshot (snapshot timer) |
ssh-sign-user |
machine | ssh/sign/user (ssh-login helper) |
personal-transit |
machine | encrypt/decrypt with transit-personal/personal-backup |
default and root are built-in and not managed here.
Humans — via identity groups
| Group | Policy | Who |
|---|---|---|
g-admins |
admin |
you (platform changes; not daily use) |
g-operators |
operator |
routine work |
g-auditors |
auditor |
the governance lens (read-only) |
g-personal |
personal-rw |
personal vault access |
A person = an entity; each login (e.g. admin@userpass) is an alias to that
entity; the entity's group membership confers policies. Compose access by adding
an entity to more groups — no token edits.
Machines — AppRole, one role + least-privilege policy per service
Target: migrate the static-token helpers to AppRole roles svc-backup,
svc-cert-renew, svc-ssh-sign, app-gitea (each bound to the matching policy
above). demo-app already demonstrates the pattern.
Naming conventions
- Policies:
<domain>-<resource>-<access>(kv-personal-rw,app-gitea-ro) - AppRoles:
app-<name>(apps) /svc-<name>(infra jobs) - Groups:
g-<persona>
How to apply
# 1. Review what would change (read-only):
scripts/apply-policies.sh --dry-run
# 2. Apply the policies (NEW/CHANGED only):
scripts/apply-policies.sh
# 3. Create groups + a human entity (dry-run first, then --apply):
scripts/setup-identity.sh
scripts/setup-identity.sh --apply # aliases to the 'admin' userpass user
Cross-cutting controls
- Audit device (currently OFF — top gap). Enable a file audit log:
(logs to the container's
docker compose exec openbao bao audit enable file file_path=/openbao/logs/audit.log/openbao/logs; mount/ship it for retention). - Root sealed — move
init-output.json(recovery keys + root token) offline; root is break-glass only. - Least privilege + short TTLs — services use AppRole short tokens; humans get group-derived policies.
- Trust-domain isolation by mount —
personal/andtransit-personal/are excluded fromadmin/operator; onlyg-personalreaches them. - Policy-as-code — all rules live in
policies/, applied byapply-policies.sh, reviewed in git.
Apply order (when you're ready)
apply-policies.sh(addsoperator,auditor)setup-identity.sh --apply(groups + entity)- Enable the audit device
- (Optional) give
adminidentity/*; migrate services to AppRole; move root offline