Files
OpenBAO/GOVERNANCE.md
Lutz Finsterle 5e5e7f779f Add governance blueprint: policy-as-code, operator/auditor, identity setup
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>
2026-06-30 21:05:38 +02:00

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

  1. Audit device (currently OFF — top gap). Enable a file audit log:
    docker compose exec openbao bao audit enable file file_path=/openbao/logs/audit.log
    
    (logs to the container's /openbao/logs; mount/ship it for retention).
  2. Root sealed — move init-output.json (recovery keys + root token) offline; root is break-glass only.
  3. Least privilege + short TTLs — services use AppRole short tokens; humans get group-derived policies.
  4. Trust-domain isolation by mountpersonal/ and transit-personal/ are excluded from admin/operator; only g-personal reaches them.
  5. Policy-as-code — all rules live in policies/, applied by apply-policies.sh, reviewed in git.

Apply order (when you're ready)

  1. apply-policies.sh (adds operator, auditor)
  2. setup-identity.sh --apply (groups + entity)
  3. Enable the audit device
  4. (Optional) give admin identity/*; migrate services to AppRole; move root offline