---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.
6.5 KiB
CLARIFY v0.12: Security Hardening (Zero-Trust Identity)
Status: resolved (full autonomy, 2026-08-07). All 10 clarifications
resolved with the operator's locked decisions (D-238..D-247). No open
questions remain for Phase 0. The --ideate flag was passed; the
threat-model review drove the requirements.
Resolved clarifications
C1 — Milestone version (resolved)
Question: v0.11 is complete; the v0.11 PRD deferred the v1.0.0 tag for UAT sign-off. Is this security-hardening milestone v1.0 (the UAT gate) or a minor v0.12?
Decision: v0.12 (minor, not v1.0). The v1.0.0 production-ready
tag stays deferred for post-v0.12 UAT, exactly as v0.11's PRD
specified. v0.12 is a minor feature milestone. Per-phase tags run on
the previous minor's patch line (v0.11.x): P0 -> v0.11.0, P01 ->
v0.11.1, ..., final phase patch = v0.11.29 = the v0.12 milestone
release (no separate v0.12.0 tag, per feature-milestone rule).
Affected: config.json milestone field, all tag computation.
C2 — OIDC provider model (resolved)
Question: Bring-your-own IdP, bundled opinionated provider, or both?
Decision: Bundled Dex by default, with BYO external IdP as a
config override. orca auth init-idp bootstraps a local Dex on the
lead (systemd unit + config template + Traefik route). oidc.issuer
in config can be repointed to an external IdP (Keycloak/Authentik/
Google/etc.) anytime. Orca stays minimal (no bundled opinionated
provider beyond Dex); Dex is the OIDC frontend, not a full IdP.
Affected REQs: REQ-144 (OIDC client + bundled Dex).
C3 — Bundled Dex upstream authenticator (resolved)
Question: Dex needs an upstream identity source. "No passwords anywhere" rules out a local password store. What is the password-free upstream?
Decision: WebAuthn (passkeys) connector. Bundled Dex gets a
custom orca-webauthn-connector (~300 LoC Go, go-webauthn library)
that serves registration + login HTML/JS pages behind Traefik at
https://<cluster>/orca/webauthn/{register,login}. The WebAuthn
ceremony (biometric/security key) produces a public-key credential;
Dex maps the credential ID to an OIDC sub. Passkeys are public-key
credentials -- the private key never leaves the authenticator -- so the
"no passwords/secrets" invariant (R-021) holds.
For BYO external IdP deployments, the operator's existing authenticator (WebAuthn, TOTP, LDAP, etc.) is used; Orca never sees the upstream credentials.
Affected REQs: REQ-148 (WebAuthn connector). Affected phase: P05 (new phase; wave B grows from 4 to 5 phases).
C4 — Master key sealing (resolved)
Question: How is the secrets master key protected at rest, given "no passwords anywhere"?
Decision: Seal to OIDC + Shamir 3-of-5 recovery. The master
key (32 random bytes) is encrypted (sealed) with a key derived from an
OIDC token exchange at unseal time. orca cluster unseal (operator
authenticates via OIDC -> token exchange -> unwrap master key into
memory -> zeroed on shutdown). orca cluster seal for manual re-seal.
The sealed blob is stored at ClusterDir()/master.key.sealed (0600).
The raw master key never touches disk.
Shamir recovery: at seal time, 5 shards are printed and the operator stores them offline. If the IdP is permanently lost AND a quorum of 3 shards is unavailable, the cluster is unrecoverable by design (documented residual risk; no backdoor).
For the mTLS-only offline path (no OIDC), the seal key is derived from the cluster's own CA -- the operator holds the CA (a cert, not a password). The Shamir recovery path applies to the OIDC-sealed mode.
Affected REQs: REQ-147 (master key seal). Affected phase: P08.
C5 — CLI browser flow (resolved)
Question: How does the CLI do the OIDC browser flow?
Decision: OIDC authorization-code + PKCE + local loopback
redirect. orca auth login opens the default browser to the Dex
WebAuthn endpoint. After the ceremony, Dex redirects to
127.0.0.1:<port>/callback (local loopback, ephemeral port). The CLI
exchanges the auth code for a short-lived ID token (1h) + refresh
token. Headless/CI fallback: device-code flow (no browser needed).
Affected REQs: REQ-144, REQ-148.
C6 — WebAuthn RP ID / secure context (resolved)
Question: WebAuthn requires a secure context (HTTPS). Where is the RP ID rooted?
Decision: Traefik-served cluster domain (step-ca cert, R-017).
Traefik already provides HTTPS on 127.0.0.1:8443 (nft DNAT from
:443). The RP ID is the cluster's Traefik-served domain, configurable
via orca auth init-idp --rp-id <domain>. For localhost dev, the
operator uses the bootstrapped step-ca cert (self-signed, but WebAuthn
accepts it for non-registerable credentials in dev mode).
Affected REQs: REQ-148.
C7 — Passkey storage (resolved)
Question: Where are WebAuthn credentials stored?
Decision: SQLite at ClusterDir()/webauthn-credentials.db
(0600). Public keys only. The DB stores credential IDs, public keys,
sign counts, and AAGUIDs. No private keys, no secrets, no passphrase
wrapping. 0600 file mode for integrity (tamper detection), not secrecy.
Affected REQs: REQ-148.
C8 — Breaking-change handling (resolved)
Question: P07 (remove all password/token paths) is a breaking change. How are existing v0.11 clusters handled?
Decision: orca upgrade refuses v0.11 clusters using
--password/bare-tokens without --accept-identity-migration. The
flag prints the cutover documentation and requires explicit
confirmation. No silent breakage. Documented in docs/oidc.md and the
migration guide.
Affected REQs: REQ-146, REQ-137.
C9 — Token storage at rest (resolved)
Question: Where are OIDC tokens stored locally?
Decision: ~/.orca/credentials.json (0600). Short-lived (1h) +
refresh. Standard OIDC token storage. 0600 file mode. Refresh
handles rotation; no long-lived Orca-issued tokens (the IdP issues
them; Orca only stores them).
Affected REQs: REQ-144.
C10 — Phase count (resolved)
Question: The threat model surfaced ~25 fix areas + the zero-trust identity work + docs + tests + final. More than 20 phases is acceptable per operator guidance. How many?
Decision: 29 phases (P0 + P01..P27 + P28 final). The operator explicitly accepted "more than 20 phases is acceptable if warranted." The GRILL stage may split/merge as needed (as v0.11 grill split P10 into P10a/P10b).
Affected: PLAN_v0.12.md, ROADMAP.md.
Open questions
None. All 10 clarifications resolved. Phase 0 proceeds to RESEARCH.