Gateway Caddy
The public front door. Caddy handles HTTPS, issues certificates on demand, signs operators in with AuthCrunch, and routes traffic to Phoenix and FRPS. This page also sets out which routes skip operator sign-in, and how Caddy stops anyone forging identity headers.
Owns
- The production
deploy/compose/caddy/Caddyfile, plusCaddyfile.laptopfor laptop mode andCaddyfile.devfor local development. - The Caddy image with the AuthCrunch plugin, built from
packages/caddy. - Mapping identity-provider groups to Nixstasis roles.
Identity provider
AuthCrunch signs operators in through any OpenID Connect or OAuth 2.0 identity provider it supports. It has built-in
drivers for Microsoft Entra ID (azure), Google, GitHub, GitLab, Okta, Meta (facebook), Amazon Cognito, Discord,
LinkedIn, and Nextcloud, and a generic driver for other OpenID Connect providers.
The production Caddyfile uses Microsoft Entra ID as its example. Its oauth identity provider azure block reads
CLIENT_ID, CLIENT_SECRET, and TENANT_ID, and the nixstasis_portal authentication portal enables it. To use
another provider, register Nixstasis with that provider, using https://auth.<base-domain> for its callbacks, then
replace the block with the provider's AuthCrunch configuration and enable it in the portal instead. Which settings a
driver needs, such as Entra's tenant ID, is covered in the AuthCrunch documentation.
The role mapping and every other gateway rule stay the same whichever provider you use; the entra_policy
authorization policy is named after the example, not tied to it. The role mapping matches the groups your provider
puts in the user's claims, so for a provider that sends no groups, write transform user rules that match another
claim, such as email.
Entry points
| Host | Purpose |
|---|---|
nixstasis.<base-domain> | Operator UI and APIs, authenticated by AuthCrunch, plus the device and E2E routes that skip it. |
*.<base-domain> | Device remote-access hosts, forwarded to the FRPS HTTP vhost after Phoenix authorizes the device. |
frp-admin.<base-domain> | The FRPS dashboard, limited to the nixstasis/admin role. |
On-demand TLS asks Phoenix GET /api/v1/check_domain before issuing a certificate. Device hosts are named
atom-<normalized-device-id>, optionally with a route suffix such as -provisioning, and are approved only while the
device has an unexpired remote-access lease.
Boundaries
- Routes that skip AuthCrunch: device registration, heartbeat, command results, and command payloads, under both
/api/v1/devicesand/api/json/device_runtime/devices, and the E2E API under/e2e/*. Phoenix authenticates those with the device token or the E2E runner credential. The generated device list still requires an operator. /internal/frp/*returns404from the public gateway; only FRPS calls it, inside the Compose network.- Claim headers: Caddy deletes any
X-Token-*headers a client sends as soon as the request arrives, before AuthCrunch adds the verified ones. Don't move that deletion into the operatorreverse_proxy: there it would remove the verified claims too. Phoenix trusts claims only alongside theX-Nixstasis-Proxy-Tokenproxy credential. - Connection-header trick: a request whose
Connectionheader lists anX-Token-*header orX-Nixstasis-Proxy-Tokenis rejected with400before sign-in. Otherwise a proxy could be tricked into stripping the verified headers as hop-by-hop headers. OrdinaryConnection: Upgradestill works. - Client IP: every proxy to Phoenix overwrites
X-Nixstasis-Client-IPwith the socket peer, and Phoenix uses it for rate limiting only when the proxy credential is present. - No device scope: Caddy never forwards device scope. Phoenix ignores
X-Token-Device-Idsand decides device access itself. - Roles: AuthCrunch
transform userblocks map the groups listed inNIXSTASIS_VIEWER_GROUPS,NIXSTASIS_OPERATOR_GROUPS, andNIXSTASIS_ADMIN_GROUPStonixstasis/viewer,nixstasis/operator, andnixstasis/admin, so Phoenix stays independent of the identity provider. - Device hosts: wildcard traffic deletes client claims, authenticates the operator, then asks Phoenix's internal FRP access endpoint whether that operator may reach the device named in the hostname.
deploy/compose/scripts/check_proxy_claims.pychecks these rules against both the production and laptop configurations; see Local Stack.