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>
91 lines
3.8 KiB
Markdown
91 lines
3.8 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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:
|
|
```bash
|
|
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 mount** — `personal/` 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
|