cf0df0f157
---ci--- project: orca phase: 0 milestone: v0.12 status: specify ---/ci--- Threat-model review of entire surface incl OS (25 findings F1..F25). Adopts R-021 (no Orca credentials: human=OIDC, machine=mTLS/SPIFFE). Bundled Dex + WebAuthn (passkeys) as default password-free authenticator. Master key seal-to-OIDC + Shamir 3-of-5 recovery. 30 net-new requirements (REQ-119..REQ-148). 29 phases. Binding conditions C-29..C-38.
171 lines
9.3 KiB
Markdown
171 lines
9.3 KiB
Markdown
# Grill: v0.12 Security Hardening (Zero-Trust Identity) — Phase 0 Adversarial Review
|
|
|
|
**Status**: PROCEED-WITH-CONDITIONS. The v0.12 plan is sound; 10
|
|
binding conditions (C-29..C-38) gate specific phases. The plan adopts
|
|
R-021 (no Orca credentials) and D-238..D-247 from the threat-model
|
|
review + operator decisions. The grill reviewed the plan
|
|
adversarially across the same 9 axes as GRILL_v0.9/v0.11 (vision,
|
|
feasibility, scope, risk, security, operational, cost, competitive,
|
|
exit).
|
|
|
|
## Forcing questions + verdicts
|
|
|
|
### FQ1 — R-021 is the largest behavioral change in project history
|
|
|
|
**Question**: R-021 ("no Orca credentials") removes all password and
|
|
token surfaces. P07 is explicitly breaking. Is the migration path
|
|
(C-34 `--accept-identity-migration`) sufficient, or does the breakage
|
|
extend beyond what's documented?
|
|
|
|
**Verdict**: BREAKAGE IS CONTAINED BUT UNDERESTIMATED. The plan
|
|
documents the Proxmox `--password` and step-ca `--password-file`
|
|
removal. But `KindToken` removal (P06) also breaks any existing
|
|
`acl.json` that uses token identities. The migration must rewrite
|
|
`acl.json` entries, not just refuse them.
|
|
|
|
**Binding condition C-29 (refined)**: P22 (`orca upgrade`) MUST
|
|
detect v0.11 `acl.json` entries with `KindToken` and either (a)
|
|
refuse without `--accept-identity-migration` + a documented
|
|
re-mapping, or (b) auto-stub them as `KindOidc` with a placeholder
|
|
`sub` requiring operator confirmation. No silent data loss.
|
|
|
|
**Confidence**: 0.90
|
|
|
|
### FQ2 — P08 master key seal is the riskiest phase
|
|
|
|
**Question**: A bug in seal/unseal corrupts all secrets at rest. Is
|
|
the recovery path (Shamir 3-of-5) actually testable, and does it
|
|
handle the "IdP lost AND shards partially lost" case?
|
|
|
|
**Verdict**: RECOVERY IS TESTABLE BUT THE EDGE CASES ARE UNDERTESTED.
|
|
The plan covers the happy path (3-of-5) and the failure case (< 3
|
|
shards -> unrecoverable). But the "IdP lost, 3 shards available, but
|
|
the OIDC-derived salt was also lost" case (the salt is in the sealed
|
|
blob, so this shouldn't happen -- but verify) needs an explicit test.
|
|
|
|
**Binding condition C-30 (refined)**: P08 MUST include a test that
|
|
recovers with 3-of-5 shards AFTER the IdP is simulated-down (seal key
|
|
reconstruction from shards, NOT from OIDC token). The sealed blob
|
|
must contain the salt (so recovery doesn't need the IdP). Document
|
|
that the salt is stored in the sealed blob, not derived from the
|
|
token at recovery time.
|
|
|
|
**Confidence**: 0.88
|
|
|
|
### FQ3 — P05 WebAuthn connector feasibility
|
|
|
|
**Question**: The custom Dex connector (~300 LoC) is new ground. Is
|
|
the `go-webauthn` library mature enough, and does the RP ID / secure
|
|
context requirement create a chicken-and-egg problem (Dex needs
|
|
Traefik, Traefik needs the cert, the cert needs step-ca, step-ca
|
|
needs the operator authenticated -- by Dex)?
|
|
|
|
**Verdict**: NO CHICKEN-AND-EGG, but the bootstrap sequence must be
|
|
explicit. The cert comes from step-ca's OIDC provisioner (P07), but
|
|
the FIRST operator must authenticate to step-ca. Resolution: the
|
|
first operator uses the mTLS-only path (cluster CA cert, held
|
|
offline) to mint the first Traefik cert. Dex then comes up. The
|
|
first WebAuthn registration happens via that first cert. The chicken-
|
|
and-egg is resolved by the mTLS-only bootstrap path.
|
|
|
|
**Binding condition C-31 (new)**: P04/P05 MUST document the bootstrap
|
|
sequence: (1) `orca init` bootstraps the cluster CA (step-ca, mTLS-
|
|
only), (2) `orca auth init-idp` deploys Dex behind Traefik using the
|
|
step-ca cert, (3) the first operator registers a passkey via the
|
|
mTLS-authenticated session, (4) subsequent operators use WebAuthn.
|
|
The mTLS-only path is the bootstrap escape hatch.
|
|
|
|
**Confidence**: 0.87
|
|
|
|
### FQ4 — P21 SQLite encryption CGO risk
|
|
|
|
**Question**: SQLCipher needs CGO (breaks D-008 cross-compile). The
|
|
C-31 fallback is "file-mode 0600 + documented threat." Is that
|
|
acceptable for a security-hardening milestone?
|
|
|
|
**Verdict**: FALLBACK IS ACCEPTABLE BUT MUST BE EXPLICIT. The
|
|
threat-model finding (F8) is "DBs unencrypted with no explicit file
|
|
mode." The minimum fix (0600 file mode) closes the "no explicit mode"
|
|
half. The "unencrypted" half is a documented residual risk if CGO is
|
|
infeasible. This is consistent with the project's "no CGO" invariant
|
|
(D-008) which is load-bearing for cross-compile.
|
|
|
|
**Binding condition C-32 (refined)**: P21 MUST evaluate at least one
|
|
CGO-free encryption option (e.g., application-level AES-GCM envelope
|
|
around the SQLite file, or a FUSE encryption layer). If all are
|
|
infeasible or too complex for v0.12, document the decision + residual
|
|
risk. The fallback is file-mode 0600 only. No CGO.
|
|
|
|
**Confidence**: 0.85
|
|
|
|
### FQ5 — Phase count (29) vs. sizing
|
|
|
|
**Question**: 29 phases is the largest milestone in project history
|
|
(v0.11 was 24, v0.9 was 14). Is any single phase too large to ship
|
|
atomically?
|
|
|
|
**Verdict**: TWO PHASES ARE LARGE. P04 (OIDC+Dex) and P08 (master
|
|
key seal) are each ~500-700 LoC + tests. They're within the v0.11
|
|
P10a/P10b sizing that the grill previously accepted, but the grill
|
|
split P10. If P04 or P08 grows during execution, the EXECUTE workflow
|
|
may split them (P04a/P04b, P08a/P08b).
|
|
|
|
**Binding condition C-33 (new)**: P04 and P08 are SPLIT CANDIDATES.
|
|
If either exceeds ~700 LoC + tests during EXECUTE, split: P04a (OIDC
|
|
client) / P04b (bundled Dex deploy); P08a (seal/unseal + Shamir) /
|
|
P08b (CLI + mTLS-only path). The planner monitors LoC during
|
|
execution.
|
|
|
|
**Confidence**: 0.82
|
|
|
|
### FQ6 — C-32 human gate (leaked GITEA_TOKEN) could stall the final ship
|
|
|
|
**Question**: If the operator doesn't rotate the token, P28 can't
|
|
ship. Is there an escalation path that doesn't block the milestone?
|
|
|
|
**Verdict**: ESCALATION PATH EXISTS. Ship as `v0.11.28-rc1` (release
|
|
candidate) if the token is not rotated by P28. The `v0.11.28` final
|
|
tag (milestone release) waits for confirmation. The milestone is
|
|
"complete" (all phases shipped); only the final tag is gated.
|
|
|
|
**Binding condition C-34 (refined)**: C-32 human-gate: if the
|
|
GITEA_TOKEN is not rotated by P28, ship `v0.11.28-rc1` (all phases
|
|
complete, release notes flag the pending rotation). The `v0.11.28`
|
|
final tag is cut when the operator confirms. The `---ci---` block
|
|
records `escalation: type=release_pending resolution=auto` -- does
|
|
not halt the pipeline.
|
|
|
|
**Confidence**: 0.90
|
|
|
|
## Adopted binding conditions (C-29..C-38)
|
|
|
|
| ID | Condition | Phase | Confidence |
|
|
|----|-----------|-------|------------|
|
|
| C-29 | P23 (dual-write closure) gated on P06/P08/P09/P11 all shipped. P22 must detect v0.11 `acl.json` `KindToken` entries and refuse/remap without `--accept-identity-migration`. | P22/P23 | 0.90 |
|
|
| C-30 | P14 (master key rotation) reversible; `--dry-run` mandatory; auto-rollback to old sealed key on any ns failure. | P14 | 0.88 |
|
|
| C-31 | P21 (SQLite encryption): evaluate at least one CGO-free option (app-level AES-GCM envelope, FUSE layer). If infeasible, file-mode 0600 + documented residual risk. No CGO. | P21 | 0.85 |
|
|
| C-32 | **Human-gate**: leaked GITEA_TOKEN (F17) rotated + `.env` re-seeded before `v0.11.28` final tag. If not rotated by P28, ship `v0.11.28-rc1`. History-scrub best-effort, non-blocking. Escalation hook in `---ci---`. | P28 | 0.90 |
|
|
| C-33 | P26 (security integration tests) in `.coreci.yml` `validate`, gates merges -- not opt-in. | P26 | 0.95 |
|
|
| C-34 | P07 (password/token removal) breaking. `orca upgrade` (P22) refuses v0.11 clusters using `--password`/bare-tokens/`KindToken` without `--accept-identity-migration`. No silent breakage. | P07/P22 | 0.90 |
|
|
| C-35 | P08 (Shamir recovery): 3-of-5 shards printed at seal time, operator stores offline. Sealed blob contains the salt (recovery doesn't need the IdP). If IdP lost AND < 3 shards -> unrecoverable by design (documented residual risk). No backdoor. Test recovery with IdP-down. | P08 | 0.88 |
|
|
| C-36 | OIDC client secret (confidential clients) at `ClusterDir()/oidc-client-secret` (0600), rotatable via `orca auth rotate-client-secret`, never committed. Public PKCE clients avoid even this. | P04 | 0.92 |
|
|
| C-37 | P04/P05 (bundled Dex + WebAuthn): document the bootstrap sequence (mTLS-only first cert -> Dex -> first passkey). The mTLS-only path is the bootstrap escape hatch. If WebAuthn proves infeasible, bundled Dex ships mTLS-client-cert-only (C-37 fallback). The "no Orca credentials" invariant holds regardless. | P04/P05 | 0.87 |
|
|
| C-38 | P05 (WebAuthn): RP ID must match the cluster's Traefik-served domain; `orca auth init-idp` configures it. HTTPS secure context via Traefik (step-ca cert). P26 integration tests use the WebAuthn virtual-authenticator API -- no hardware key required in CI. | P05 | 0.90 |
|
|
|
|
## Verdict: PROCEED-WITH-CONDITIONS
|
|
|
|
The v0.12 plan is sound. The 10 binding conditions gate the risky
|
|
phases. The 29-phase count is within the operator's "more than 20 if
|
|
warranted" guidance. The plan adopts R-021 (no Orca credentials) and
|
|
D-238..D-247. The grill does NOT recommend REPLAN.
|
|
|
|
## Phase challenges (PC-01..PC-05)
|
|
|
|
| ID | Challenge | Phase |
|
|
|----|-----------|-------|
|
|
| PC-01 | P04/P08 are split candidates if LoC exceeds ~700 (C-33) | P04/P08 |
|
|
| PC-02 | P07 breaking change -- migration must handle `KindToken` acl.json entries, not just passwords (C-29/C-34) | P07/P22 |
|
|
| PC-03 | P05 WebAuthn bootstrap sequence must be explicit (mTLS-only first cert) | P04/P05 |
|
|
| PC-04 | P21 SQLite encryption CGO evaluation -- document the decision + residual risk if fallback | P21 |
|
|
| PC-05 | C-32 human gate -- `v0.11.28-rc1` escalation if token not rotated | P28 |
|