Commit graph

3 commits

Author SHA1 Message Date
7c1826e115 fix(setup): render the TOTP step instead of the enroll flow's JSON
The first-run wizard's `admin.totp.begin` delegated to the enroll flow's
`TOTPBegin`, which is `webauthn.js`'s JSON API: the browser was shown raw
`{"otpauth_url":…}` and the wizard stalled at step 2, rendering no secret.

Extract the issue step into a write-free seam, `flow.Actions.IssueTOTP`, and
keep `TOTPBegin`'s JSON contract by having it call the seam. The wizard now
issues via the seam and renders its own HTML page (`text/html`, inside the
shell, no inline scripts) showing the otpauth URL, the human-readable secret
for manual entry, and the verify form (pre-session CSRF, A15).

`setup.Enroller` drops `TOTPBegin` — the wizard no longer calls it, and a
JSON-writing method on a no-JS seam is the coupling that caused the bug.

Tighten the tests that missed it: `setup_wizard_test.go` asserts the `begin`
response is `text/html` and carries the secret and the verify form (not merely
200), keeping the end-to-end begin→code→303 path; `enroll_login_test.go` pins
the enroll flow's JSON contract (`application/json` + `otpauth_url`).

Spec: docs/specs/spec-005-setup-wizard.md §11.
2026-10-15 07:07:31 +00:00
f0927fb0f0 feat(server): gate /setup by the direct tunnel and trust the proxy scheme
Implements D-025 (revised 2026-10-15) and D-043.

Setup gate (D-025):
- Remove the bind refusal (CheckSetupBind/ErrNonLoopbackSetup/server.IsLoopback);
  Serve binds any address, so the app can listen on 0.0.0.0:8080 behind Zoraxy.
- The gate is now per request and strict (httpx.SetupRequestAllowed): a loopback
  TCP peer AND no X-Forwarded-For — the direct SSH tunnel. Anything else is 404
  before dispatch; an unparseable peer fails closed (AC-7/AC-10). The raw XFF is
  read before ClientIP SETS it (A6).

Proxy scheme (D-043):
- httpx.RequestIsHTTPS resolves the scheme per request: direct TLS, or a trusted
  proxy's X-Forwarded-Proto. The door carries it on the request context
  (httpx.WithSecure) so the session cookie, its __Host- prefix, the pre-session
  and ceremony cookies, and HSTS all agree (AC-13).
- Over plain http (the tunnel) cookies stay bare and non-Secure, which fixes the
  bootstrap from an https:// config.example.yaml; Config.IsSecure() is removed
  as now dead.

Tests: rewrite the setup-gate tests for AC-7/AC-10, add AC-13, and unit-test
SetupRequestAllowed / RequestIsHTTPS.
2026-10-15 05:40:36 +00:00
8268757773 fix(security): sign the setup ceremony with startup-generated keys
REVIEW-001 (M0) M1s: the /setup wizard's pre-session double-submit ceremony
(A15) was signed with csrf_key/cookie_secret left at the shipped CHANGE_ME
placeholder until `finish` persisted generated values — a publicly-known
signing key for the whole bootstrap.

Generate strong ephemeral values at startup in setup mode, mirroring the
TOTP key (R-6/D-1): `finish` skips a non-placeholder, so the wizard persists
the very values the process already uses.

Also add cmd/myday's first test — REVIEW-001 M2c: the D-1 invariant (the
startup TOTP key is the persisted key) was covered only by a manual smoke. It
generates the key, enrolls a secret, persists via the wizard, reopens the DB
with the persisted key and asserts the secret still unseals.
2026-10-14 11:30:09 +00:00