coder: initial import of deployed stack (v2.35.3 compose, docker template, SSOT vault)
This commit is contained in:
84
q5vault/operations/auth-model.md
Normal file
84
q5vault/operations/auth-model.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: "🔐 Auth model"
|
||||
description: "Why code.loca.zone is deliberately not behind Authelia, why a browser-only gate is impossible, why the cookies do not collide, and how the initial Owner was bootstrapped over loopback."
|
||||
source: "verified deployment on the loca operator host, 2026-08-19; first-user bootstrap verified 2026-08-20"
|
||||
reviewed: 2026-08-20
|
||||
---
|
||||
|
||||
# 🔐 Auth model
|
||||
|
||||
## 🧾 Decision
|
||||
|
||||
- Coder is the identity provider for its own deployment; the `code.loca.zone` vhost has no `auth_request` gate and no Authelia access rule
|
||||
- this is the only loca.zone application host that is intentionally ungated at the proxy, and the reason is protocol, not laziness
|
||||
- Authelia was not modified for this stack: its `default_policy` is `bypass`, so a host with no rule is simply passed through. Adding nothing was the correct action
|
||||
|
||||
## 💥 Precise failure mode if an Authelia rule were added
|
||||
|
||||
- the site-wide gate answers any request without a valid `authelia_session` cookie with a `302` to `https://auth.loca.zone`
|
||||
- what that breaks, concretely:
|
||||
- the `coder` CLI, which authenticates with the `Coder-Session-Token` header and has no cookie jar; `coder login`, `coder templates push`, `coder ssh` and `coder port-forward` all receive a redirect where they expect JSON
|
||||
- workspace agents, which dial `CODER_ACCESS_URL` and hold long-lived `/api/v2` websockets; a redirect on the upgrade request means the agent never connects and the workspace sits in a connecting state forever
|
||||
- relayed workspace traffic, since the built-in DERP relay rides the same vhost
|
||||
- upstream is explicit that all clients and agents must be able to reach the server over HTTP or HTTPS and that any proxy in between must support websockets; an unconditional redirect violates both
|
||||
|
||||
## 🚫 Why a partial browser-only gate is impossible
|
||||
|
||||
- the dashboard is a single-page application served from the same origin as the API; the browser and the agents both call `/api/v2/...`
|
||||
- there is no stable path prefix that separates human traffic from machine traffic, and no header an nginx gate could trust to tell them apart, since a browser can be made to send anything a header-based rule would key on
|
||||
- gating `/` while exempting `/api/v2` would exempt the entire attack surface and leave only the static bundle protected, which is security theatre
|
||||
- the workspace app and web terminal routes (`/@user/workspace.agent/apps/...`, `/@user/workspace.agent/terminal`) are proxied by the same server and are used by both planes
|
||||
- conclusion: either the whole vhost is gated, which breaks the product, or none of it is, which is what is deployed
|
||||
|
||||
## 🍪 Why the cookies do not collide
|
||||
|
||||
- Authelia's session cookie is domain-scoped to `loca.zone`, so `authelia_session` is presented on requests to `code.loca.zone` as well
|
||||
- Coder ignores it; it reads only its own `coder_session_token`, which Coder sets host-scoped for `code.loca.zone`
|
||||
- nothing writes both names, so no overwrite, no truncation and no cross-application session confusion
|
||||
- practical consequence: being logged into `auth.loca.zone` grants nothing on this host, and being logged into Coder grants nothing on the Authelia-gated hosts
|
||||
|
||||
## 👤 First-user bootstrap is done
|
||||
|
||||
- state now, verified 2026-08-20: the initial Owner exists, so nothing here is waiting on a setup form
|
||||
- username `loca`, email `admin@loca.zone`, role Owner, status active
|
||||
- organization `coder`, the default one, id `9c28ebd1-4fe6-446b-83e2-f723954fec10`
|
||||
- created with `POST /api/v2/users/first` against `http://127.0.0.1:52226`, so the credential never traversed the public vhost
|
||||
- `GET /api/v2/users/first` flipped from `404` to `200` at that moment, which is upstream's own existence check for the initial user
|
||||
- how the password was handled, which is the only acceptable shape for an agent-run bootstrap:
|
||||
- randomly generated, 32 characters, written straight into `/home/loca/dev/coder/.admin-credentials` at mode 600 in a git-ignored path
|
||||
- never displayed on a terminal, in a log, in a ticket, in a chat message or in this wiki, and never read back into any document
|
||||
- that file also holds the session token minted by the same call, which is the second reason it stays at 600 and out of git
|
||||
- rotation is the operator's responsibility and nobody else's: sign in at https://code.loca.zone and set a new password under Account then Security, after which the stored copy is stale by design
|
||||
- what was verified alongside the bootstrap, none of it credential-bearing:
|
||||
- `curl -sSI https://code.loca.zone/` returns `200` with no auth redirect
|
||||
- `curl -sf http://127.0.0.1:52226/api/v2/buildinfo` reports `v2.35.3+65e2bfb` with `dashboard_url` `https://code.loca.zone`
|
||||
|
||||
## 🔒 No open signup
|
||||
|
||||
- `POST /api/v2/users/first` is the only account-creating endpoint reachable without authentication and it exists solely to create the initial Owner
|
||||
- `POST /api/v2/users` requires authentication, so every later account is created by an admin, either with `coder users create` or through Users then New user in the dashboard
|
||||
- there is no self-service registration in Coder OSS with password auth; a user appears only because an admin made it
|
||||
- password reset for the initial Owner is a host-side operation: `coder reset-password <username>` only works when run on the machine running the control plane, which here means inside the `coder` container
|
||||
|
||||
## 🔭 Future upgrade path: Authelia as an OIDC provider
|
||||
|
||||
Not implemented, and it requires operator approval because it means editing the shared Authelia deployment.
|
||||
|
||||
- shape of the change:
|
||||
- register a new OIDC client in Authelia for Coder, with redirect URI `https://code.loca.zone/api/v2/users/oidc/callback`
|
||||
- set `CODER_OIDC_ISSUER_URL`, `CODER_OIDC_CLIENT_ID`, `CODER_OIDC_CLIENT_SECRET` and, if wanted, `CODER_OIDC_EMAIL_DOMAIN` on the `coder` service
|
||||
- optionally add `offline_access` to `CODER_OIDC_SCOPES` so sessions survive access-token expiry
|
||||
- optionally set `CODER_DISABLE_PASSWORD_AUTH=true` once OIDC login is proven
|
||||
- what it does and does not change:
|
||||
- it unifies identity inside Coder, at the application layer, where the CLI and agents keep using their bearer tokens
|
||||
- it does not, and must not, add an nginx `auth_request` gate; the failure modes above still apply
|
||||
- prerequisites the operator must approve: a new Authelia client definition, a client secret placed in `.env`, and a restart of both stacks
|
||||
|
||||
## 📚 Sources
|
||||
|
||||
- https://coder.com/docs/reference/api/authentication
|
||||
- https://coder.com/docs/reference/api/users
|
||||
- https://coder.com/docs/admin/users
|
||||
- https://coder.com/docs/admin/users/oidc-auth
|
||||
- https://coder.com/docs/admin/networking
|
||||
- https://coder.com/docs/admin/setup
|
||||
Reference in New Issue
Block a user