diff --git a/docs/session-ledger.md b/docs/session-ledger.md index b17bdf1..bf0d466 100644 --- a/docs/session-ledger.md +++ b/docs/session-ledger.md @@ -31,8 +31,8 @@ - **OPEN security rows:** SEC-001 (rotate at v1 close, re-ruled 2026-07-06), SEC-003 (custodians + second-person unseal -- DEFERRED; keeps d011-06 MANUAL), SEC-004 (flip repo to private at v1 close -- deferral reaffirmed). -- **Next-free numbers:** D = 074, DOCFIX = 091, BUNDLEFIX = 012. - (Scan and the addendum-20 changelog next-free pointer agree.) +- **Next-free numbers:** D = 074, DOCFIX = 092, BUNDLEFIX = 012. + (Scan and the addendum-21 changelog next-free pointer agree.) --- @@ -197,6 +197,11 @@ responsibility split). A2 (d011-04 disruptive gating) held behind A1. - IN DISCUSSION (B2): OpenBao evaluation; D-068 item 2 (vault_url cleartext http); D-068 item 3 (AppRole TTL audit + proactive probe) scoping. +- DELIVERED (addendum 21): DOCFIX-091 docs/tenant-onboarding-contract.md per A1 + rulings 1-4 (boundary, roles, intake, dangers, hardening register H1-H5 -- + proposals logged, none implemented). A1.4 interpretation pending operator + confirm: "SSH access into their domain" read as canary access-proof (H3). + Verify-live additions: Horizon manager-role GUI identity probe (contract 5.3). diff --git a/docs/tenant-onboarding-contract.md b/docs/tenant-onboarding-contract.md new file mode 100644 index 0000000..53c2661 --- /dev/null +++ b/docs/tenant-onboarding-contract.md @@ -0,0 +1,123 @@ +# Tenant onboarding contract -- operator/tenant boundary, intake, topology (DOCFIX-091) + +Commissioned 2026-07-06 (operator A1 rulings). This is the authoritative statement of +WHO does WHAT at tenant onboarding and why, for Omega Cloud v1 and carried to Roosevelt. +Companions: `scripts/tenant-onboard.sh` (the ONLY sanctioned execution path), +`runbooks/tenant-onboarding-v2-DRAFT.md` (procedure narrative; superseded points noted +in its banner), `runbooks/appendix-C-identity-rbac.md`, `runbooks/appendix-D-magnum-trust-model.md`. +Decisions: D-051 (Domain Manager persona), D-064 (policy mechanics), D-066 (account +model), D-073 (list_trusts hardening, adopted 2026-07-06). + +## 1. Identity model -- roles, explicit (D-066 Option-3 triad) + +| identity | auth | role grants (EXACT) | purpose | +|---|---|---|---| +| `-domain-admin` | password | `manager` on the domain -- EXACTLY ONE assignment, nothing else | identity CRUD inside the domain: users, projects, in-domain role grants | +| `-cluster` | password | `member` + `load-balancer_member` on the project | cluster lifecycle (trust-capable); OWNS the nova keypair | +| `-svc` | password + UNRESTRICTED app credential | `member` + `load-balancer_member` on the project | non-trust automation: L3 build-out, templates, day-2 API work | + +**The domain admin is the `manager` role, NEVER keystone `admin`.** Keystone `admin` +is architecturally not domain-confinable (a domain-scoped admin can escalate beyond +the boundary -- upstream hard-coded limitation). The persona is the SCS Domain Manager +(scs-0302) enforced by the D-051/D-064 policy override. Any `admin` grant anywhere in +a tenant domain is an incident, not a convenience. + +**Why three identities, not one.** Keystone refuses trust creation from app-cred +tokens (regardless of `--unrestricted`), so Magnum cluster create MUST run as a +password identity (`-cluster`). Nova keypairs are USER-scoped and Magnum validates +the keypair in the cluster-creator/trustor context, so the keypair MUST be owned by +`-cluster` (a `-svc`-owned key 400s at cluster create). Identity CRUD stays with the +manager only. This split is PRESCRIPTIVE for tenants, not a suggestion. + +## 2. Operator-only surface (and why) + +| surface | why it is operator-only | +|---|---| +| domain lifecycle (create/disable/delete) | it IS the tenancy boundary | +| manager account provisioning + password reset | no self-service recovery exists; manager lockout is an operator support case (tenant-side bus factor -- tenants should name two custodians) | +| quotas (nova/cinder/neutron/octavia/magnum) | the capacity envelope is a commercial + capacity-planning decision; the manager role cannot self-raise quotas by design | +| external/provider networks (`provider-ext`), FIP pool | shared substrate; cross-tenant capacity | +| public Glance images, flavors | shared substrate; Magnum-capable image/flavor requirements are operator-validated | +| keystone policy layer (policyd-override zip) | it carries the isolation guarantee itself (D-051/D-064/D-073) | +| cloud control plane (juju, vault, ceph, ovn, octavia mgmt, magnum mgmt) | not tenant-visible, ever | + +Everything else is tenant self-service via the triad: projects, users, in-domain role +grants (manager); networks/subnets/routers (gateway to `provider-ext`), security +groups, floating IPs, load balancers, Kubernetes clusters, instances, volumes, +Barbican secrets/certificates, app credentials, keypairs (workload identities). +Tenants never receive SSH or console access to operator infrastructure. + +## 3. Tenant intake list (what the tenant provides BEFORE onboarding) + +1. **Client short-name** -- lowercase, DNS-safe; becomes the `` prefix on the + domain and every identity/resource name. +2. **Two named credential custodians** + an out-of-band handoff channel (credentials + are never sent in email/ticket bodies). Custodian #2 covers the manager-lockout + bus factor. +3. **Quota ask**: instances / vCPU / RAM, volumes + GB, networks/routers/FIPs, load + balancers, clusters + node counts. (Maps to the stage-1 quota envelope.) +4. **Tenant CIDR(s)** -- RFC1918, non-colliding with our allocations AND with any + on-prem/VPN ranges they may later interconnect. Stage 4 fails closed on collision, + but collecting it up front avoids a restart. +5. **Workload profile**: Kubernetes yes/no (sizing against the capi templates), LB + count expectations (amphora capacity), storage expectations. +6. **Authorized requesters** -- who may request quota changes / password resets. +7. (Optional) an SSH public key to import if they prefer their own key over the + generated `-key`. + +## 4. Account creation -- steps, requirements, dangers + +`bash scripts/tenant-onboard.sh [stage]` is the only sanctioned path; ad-hoc +onboarding is prohibited (deviations become script changes with a harness). Stages: + +| stage | actor/identity | does | danger the script guards | +|---|---|---|---| +| 0 | operator (read-only) | preflight: D-064 policy live (`PO:` active), roles exist, public kube image, clean slate for `` | proceeding onto a half-built or policy-less cloud | +| 1 | operator | domain + manager (+ quota envelope) | admin-instead-of-manager; quota must be set AFTER the project exists (or pre-create it) | +| 2 | manager (their creds) | project + `-cluster` + `-svc` + grants; then an ANTI-ESCALATION self-check (an `admin` grant attempt must be DENIED -- if it lands, STOP: the policy layer is broken) | privilege creep; policy-layer regression | +| 3 | `-svc`, then `-cluster` | `-svc` mints the unrestricted app cred; `-cluster` creates the keypair | THE KEYPAIR TRAP: key must be `-cluster`-owned or stage 6 400s | +| 4 | `-svc` app cred | L3: net/subnet/router/ext-gw to `provider-ext` | CIDR collision (fail-closed guard) | +| 5 | `-svc` app cred | cluster template, image pinned BY UUID, no `--keypair` | keypair on the template would bind the wrong owner; stage 6 supplies it | +| 6 | `-cluster` PASSWORD | cluster create | app-cred cluster create is impossible (keystone blocks trust from app creds) -- do not "simplify" to the app cred | + +Credential custody: per-identity 0600 files, never committed, handed off out-of-band +to the named custodians; verify by length/format, never by printing. + +## 5. Onboarding confirmation set ("access confirmed" means all of these) + +1. **Horizon login** as `-domain-admin`, domain-scoped -- at onboarding. +2. **API/CLI auth** for all three identities (the script asserts a token per identity + inline at each stage). +3. **Horizon identity self-service probe** -- manager creates and deletes a probe user + via the GUI. VERIFY-LIVE: Horizon has its own policy layer and the GUI path for + manager-role identity ops is historically the weak point; if it fails, the + contract documents CLI as the identity path until fixed. +4. **Tenant access-proof (the "SSH access" confirmation)**: boot a canary instance on + the tenant network, attach a FIP, SSH in with the tenant keypair, tear down. + Proves end-to-end that the tenant can reach workloads inside their domain. + PROPOSED as an optional stage-7 / first acceptance item -- not yet implemented. + +## 6. Hardening against technician knowledge gaps + +Existing (in the script today): script-as-procedure; stage-0 fail-closed preflight; +stage-2 anti-escalation self-check; fail-closed CIDR guard; capture-then-test output +validation; whitelist-write 0600 credentials; dynamic ID resolution (no pasted IDs). +Systemic backstop: the D-051/D-064 policy layer bounds what tenant-side identities can +do even if a technician errs; the residual risk is OPERATOR-side error (manual admin +grant, wrong domain reuse, quota fat-finger). + +PROPOSED (logged for operator prioritization; none implemented): + +- **H1 `tenant-assert.sh`** -- read-only post-onboard verifier: triad exists with the + EXACT grants of section 1 (and nothing more), keypair owner is `-cluster`, app cred + owner is `-svc`, manager has exactly one assignment, anti-escalation retest. Also + serves as an offboard preflight and a periodic all-tenants drift sweep. +- **H2** stage-3 app-cred idempotency re-run guard (already in the logged backlog). +- **H3** canary access-proof stage (section 5 item 4). +- **H4** `keystone-policy-drift.sh` periodic run (script backlog item 6) -- guards the + policy layer the whole model rests on. +- **H5** runbook rule: NOTHING outside the script; any needed deviation is a script + change shipped with its harness, not a live improvisation. + +**Related:** D-051, D-064, D-066, D-067, D-073, appendix-C, appendix-D, +`scripts/tenant-onboard.sh`, `runbooks/tenant-onboarding-v2-DRAFT.md`. diff --git a/docs/v1-redeploy-changelog.md b/docs/v1-redeploy-changelog.md index 910185d..d937139 100644 --- a/docs/v1-redeploy-changelog.md +++ b/docs/v1-redeploy-changelog.md @@ -2073,3 +2073,21 @@ REVERT: git checkout HEAD~ -- docs/design-decisions.md docs/security-ledger.md docs/session-ledger.md docs/v1-redeploy-changelog.md Next-free after this push (per scan, keep this line unwrapped): D-074, DOCFIX-091, BUNDLEFIX-012. + +### 2026-07-06 (addendum 21, jumphost stream) -- DOCFIX-091: tenant onboarding contract doc (A1 rulings) + +Operator commissioned (A1 rulings 1-4): NEW docs/tenant-onboarding-contract.md -- +authoritative operator/tenant boundary statement: explicit role table (manager +persona, NEVER admin, exact grants per identity), operator-only surface with +rationale, tenant intake list (7 items incl. two named credential custodians), +stage-by-stage creation steps/requirements/dangers (keypair trap, app-cred/trust +block, CIDR fail-closed), onboarding confirmation set (Horizon login, tri-identity +API auth, Horizon GUI identity probe VERIFY-LIVE, canary access-proof PROPOSED as +the "SSH access" confirmation), and a hardening register: existing guards + H1-H5 +proposals (tenant-assert.sh verifier, stage-3 idempotency guard, canary stage, +policy-drift periodic, no-ad-hoc rule) -- proposals LOGGED, none implemented. +Also: tenant-onboarding-v2-DRAFT.md banner gains point 4 deferring boundary/role/ +intake content to the contract doc. +REVERT: git rm docs/tenant-onboarding-contract.md; git checkout HEAD~ -- runbooks/tenant-onboarding-v2-DRAFT.md docs/session-ledger.md docs/v1-redeploy-changelog.md + +Next-free after this push (per scan, keep this line unwrapped): D-074, DOCFIX-092, BUNDLEFIX-012. diff --git a/runbooks/tenant-onboarding-v2-DRAFT.md b/runbooks/tenant-onboarding-v2-DRAFT.md index 2533a8d..5c48cbb 100644 --- a/runbooks/tenant-onboarding-v2-DRAFT.md +++ b/runbooks/tenant-onboarding-v2-DRAFT.md @@ -11,6 +11,9 @@ # currently dies at cert-gen until that live fix lands. # The runnable, corrected procedure is scripts/tenant-onboard.sh (Option-3). Stages 0-5 validated; # stage 6 gated on D-067. +# 4. (2026-07-06, DOCFIX-091) The operator/tenant boundary, role table (manager persona -- +# NEVER admin), tenant intake list, and hardening register are now authoritative in +# docs/tenant-onboarding-contract.md; this draft defers to it on all of those. # >>> END REVISION BANNER <<< # Tenant Onboarding v2 -- multi-tenant self-service + cluster creation (DRAFT)