Skip to content

fix(dev): refuse bad dev auth origins early and constrain the dev CA - #2461

Merged
freeqaz merged 1 commit into
mainfrom
fix-1364-1365
Oct 14, 2026
Merged

freeqaz merged 1 commit into
mainfrom
fix-1364-1365

Conversation

@freeqaz

@freeqaz freeqaz commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

Two low-severity, development-only gaps from a review of the Compose browser proxy (#1750):

  1. docs/reference/settings/development.md says a development OCC_AUTH_BASE_URL must be an HTTP(S) origin on localhost, 127.0.0.1 or [::1], or HTTPS on a .localhost subdomain, with no path, query, fragment or user info. apps/controller/src/server.mjs checked only the hostname, so http://x.localhost, https://.localhost and http://localhost:3000/auth passed configuration() and startup failed later in createFastifyApp (logged as an unrelated startup code). Its message also still said "must identify a loopback host".
  2. The development CA from internal/occdev writeDevelopmentTLS (browser proxy for Compose and k3d, and the repository broker) had no name constraints, was valid for a year, and left <prefix>-ca.key on disk although nothing reads it after the leaf is signed. The docs tell developers to trust the browser CA in their browser.

Change

  • apps/controller/src/auth/configuration.ts: one validator, isDevelopmentBrowserOrigin, and the rule text DEVELOPMENT_BROWSER_ORIGIN_RULE. The value must equal its own URL.origin (so no path, trailing slash, query, fragment, user info or default port), use HTTP(S), and be loopback or HTTPS on a .localhost subdomain with no empty labels.
  • server.mjs configuration() applies it to the normalized development OCC_AUTH_BASE_URL, and the error names the setting and the rule. Startup now logs AUTH_BASE_URL_INVALID before reading the Installation. index.ts developmentBrowserOrigin uses the same validator.
  • writeDevelopmentTLS now uses generateDevelopmentTLS. The CA carries critical DNS name constraints equal to the leaf's names (a leading *. is stripped), uses path length zero and expires with the leaf after three months. Its private key signs the leaf in memory and is never written. A state directory from an earlier occ dev up keeps its old key until occ dev down removes the directory. A fresh occ dev up always creates a new state directory, so it gets the new CA.
  • Docs: the quickstart, troubleshooting, local repository credentials and Compose startup flow pages no longer mention a CA private key on disk, and they describe the name constraint.

Verification

  • node --test tests/integration/configuration-startup.test.mjs (new test, development OCC_AUTH_BASE_URL must be a loopback origin or an HTTPS localhost subdomain): accepts http://127.0.0.1:3000, http://[::1]:3000, http://localhost:3000 and https://console.occ-dev-example.oce.localhost:8443, and refuses each bad spelling. A real server.mjs with each of the three reported spellings exits with AUTH_BASE_URL_INVALID. With the old server.mjs the same test fails with DATABASE_CONFIGURATION_INVALID.
  • node --test tests/conformance/occ-api-security.test.mjs: 39/39. The refused browser-origin list now includes the three spellings.
  • go test ./internal/occdev/: pass. The new TestDevelopmentBrowserCAIsNameConstrainedToTheInstallation checks the constraints, path length and lifetime, that the leaf verifies, and that leaves signed by the same CA key for example.com, another installation's console, and a sibling host fail with CANotAuthorizedForThisName. The browser and repository tests assert that no CA key file is written.
  • Pinned 20/20 at 2 CPUs: the Go TLS tests, the new startup test, and the development admission conformance tests.
  • tsc --build (controller), eslint and prettier on the touched files: clean.

Risks

Development only. Production startup and the Helm chart are unchanged. A development OCC_AUTH_BASE_URL with a path, or HTTP on a .localhost subdomain, used to fail later anyway; it now fails earlier. The repository broker CA also gets name constraints. Its only name is the exact broker host, and Go, Node, OpenSSL and git all honor critical DNS name constraints.

Development OCC_AUTH_BASE_URL is now checked in server.mjs configuration()
with the same rule createFastifyApp applies to the development browser
origin (one shared validator in auth/configuration.ts): loopback HTTP(S)
or an HTTPS .localhost subdomain, written as an origin. http://x.localhost,
https://.localhost and http://localhost:3000/auth now fail at startup as
AUTH_BASE_URL_INVALID instead of later with an unrelated error.

The development CA written by writeDevelopmentTLS (browser and repository)
is now name-constrained to the leaf's DNS names, path length zero, lives as
long as the leaf (three months), and its private key is never written: it
signs the leaf in memory and is dropped.
@freeqaz
freeqaz requested a review from a team as a code owner October 14, 2026 20:54
@clawsweeper

clawsweeper Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@freeqaz
freeqaz merged commit f559710 into main Oct 14, 2026
40 of 42 checks passed
@freeqaz
freeqaz deleted the fix-1364-1365 branch October 14, 2026 21:00

This branch had an error being deployed

1 failed deployment
integration-qa-pr — da436dc5 Deployed Oct 14, 2026 by freeqaz via QA advisory (kubernetes) #946
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant