230 lines
14 KiB
Markdown
230 lines
14 KiB
Markdown
# Users and access
|
||
|
||
Electric Cherry requires a user session for operator pages and management APIs.
|
||
HTTPS protects the connection; server-side role checks protect each operation.
|
||
There is no anonymous management mode or public registration.
|
||
|
||
Leaderboard API reads (`GET` and `HEAD /api/leaderboard`) are public and need no
|
||
session or credentials. This exception covers only that exact API route;
|
||
leaderboard/profile pages, match-history routes and management operations retain
|
||
their access checks. HTTPS requirements still apply. See the
|
||
[leaderboard API reference](leaderboard.md#read-api) for requests and response fields.
|
||
|
||
## First administrator and upgrades
|
||
|
||
1. Generate a setup secret: `openssl rand -hex 32`.
|
||
2. Set `EC_SETUP_TOKEN` to that value in the controller environment (or Compose
|
||
`.env`), set `EC_PUBLIC_URL` to the external HTTPS URL, and start EC. For a
|
||
direct Node deployment, set `ORIGIN` to that same URL.
|
||
3. Visit EC. `/setup` asks for the setup token, a username, display name and
|
||
password. Only the holder of the token can create the first administrator.
|
||
4. Remove `EC_SETUP_TOKEN` after setup and restart/recreate the controller.
|
||
Setup remains closed once any user exists, even if the token is still set.
|
||
5. Use **Admin → Users** to create additional accounts. Share temporary
|
||
passwords privately; each user must change theirs on first login.
|
||
|
||
Existing installations become protected on upgrade. Existing game servers and
|
||
agent enrollment credentials continue working independently of browser login.
|
||
Preserve the current database when upgrading: accounts and sessions are stored
|
||
there alongside the fleet metadata. Never publish a blank setup page with a
|
||
guessable token. An unset setup token holds setup closed.
|
||
|
||
## Roles
|
||
|
||
The Moderator role has Viewer-level fleet access plus private player moderation.
|
||
It permits live player actions, case review and server/game restrictions lasting
|
||
up to seven days, without granting fleet operations or access to raw configs and
|
||
credentials. Administrators also control permanent/fleet restrictions, integration
|
||
configuration and revoking another moderator's restriction. Super Viewers can
|
||
read moderation; Operators and Viewers cannot. See [Player moderation](moderation.md).
|
||
|
||
| Capability | Viewer | Super Viewer | Operator | Administrator |
|
||
| --------------------------------------------------------------------------------------------- | -------------- | ------------ | ----------- | ------------- |
|
||
| Fleet, metrics, matches and operation summaries | Yes | Yes | Yes | Yes |
|
||
| Raw configs, file diffs, logs, saved RCON and telemetry diagnostics | No | Read | Read | Read |
|
||
| Deploy/start/stop/restart/rebuild/remove servers and run diagnostic probes | No | No | Yes | Yes |
|
||
| Scripts, plugins, configs, campaigns, betas and collections | Read summaries | Read | Manage | Manage |
|
||
| Host setup, deletion, enrollment, credentials, runtime preparation and game-file upload/cache | Read status | Read status | Read status | Manage |
|
||
| Server keys and global settings/maintenance policies | No | Read | No | Manage |
|
||
| Users and account audit activity | No | Read | No | Manage |
|
||
| Create users, change roles, disable accounts and reset passwords | No | No | No | Yes |
|
||
| Own password and sign out | Yes | Yes | Yes | Yes |
|
||
|
||
Super Viewer is a read-only administrator. It includes the Admin navigation and
|
||
access to sensitive configuration and diagnostic content, but cannot change fleet
|
||
state, settings, keys, or other accounts. Cleanup previews remain available because
|
||
they do not save settings or run cleanup. Assign it only to people trusted to read
|
||
administrative information. Existing accounts and sessions are preserved on upgrade.
|
||
|
||
Operators can launch using available game-pool keys already saved by an administrator. They
|
||
cannot save keys indirectly through a launch request. An operator's content
|
||
permissions include executable scripts and plugins; assign that role only to
|
||
people trusted to operate the game fleet.
|
||
|
||
Drift previews (`/api/drift`) and GitHub release lookups
|
||
(`/api/plugins/github/release`) require operator or administrator access, including
|
||
`GET` and `HEAD` requests. These endpoints initiate fleet file scans or use GitHub
|
||
API quota; Viewers and Super Viewers cannot trigger them.
|
||
|
||
Saved RCON failure replies are available to Super Viewers, operators and administrators in the
|
||
server's Diagnostics tab. Player addresses are redacted before diagnostics are
|
||
saved; successful checks clear the saved reply. Fleet snapshots and live status
|
||
streams contain no reply snippets. Upgrades discard older saved RCON snippets.
|
||
|
||
Permissions are checked on the server for API calls, normal pages and Svelte
|
||
page-data requests. Hiding UI controls is a convenience, not the access boundary.
|
||
The controller prevents disabling or demoting the last enabled administrator.
|
||
Accounts are disabled rather than deleted so history retains their identity.
|
||
|
||
## Passwords, sessions and recovery
|
||
|
||
Passwords are 8–128 characters and stored using salted scrypt hashes. Temporary
|
||
passwords created or reset by administrators must be changed before any fleet
|
||
access. Password changes require the current password.
|
||
|
||
Sessions use random tokens in HTTP-only cookies, with only token hashes stored in
|
||
SQLite. HTTPS uses a Secure `__Host-` cookie. By default, sessions expire after
|
||
12 hours, or 30 minutes without authenticated requests; background dashboard
|
||
polling counts as activity.
|
||
|
||
Select **Keep me signed in on this device** at sign-in to stay signed in for
|
||
30 days, including between visits from a phone's home-screen app. This option is
|
||
unchecked by default and applies only to that browser or home-screen app. Remembered
|
||
sessions have no inactivity timeout, but still expire 30 days after sign-in;
|
||
activity does not extend that deadline. Use this option only on a device you trust.
|
||
Existing sessions keep their original limits after upgrading.
|
||
|
||
Password changes always require the current password. Remembered administrator
|
||
sessions also require the administrator's current password when creating accounts
|
||
or editing users, including access changes and password resets. A successful
|
||
password change replaces the current session, preserving its remember-device
|
||
choice with a fresh lifetime, and revokes all other sessions.
|
||
|
||
For API clients, `POST /api/auth/login` accepts an optional boolean `rememberMe`
|
||
(omitted or `false` keeps the default limits). When using a remembered session,
|
||
`POST /api/users` and `PATCH /api/users/:id` require the administrator's password
|
||
in `currentPassword`; the `password` field remains the target user's temporary
|
||
password. The password-change endpoint preserves the current session's choice.
|
||
|
||
Login replaces the existing browser session. Logout revokes it.
|
||
Changing a password, changing a role, disabling an account, or revoking that
|
||
user's sessions invalidates their existing sessions. Live event streams check
|
||
revocation every 15 seconds and then close.
|
||
|
||
Login and password changes for enabled Administrators and Super Viewers are exempt
|
||
from per-account and per-address rate limits, including existing exhausted windows.
|
||
Their attempts do not consume either limit. Other accounts use persistent
|
||
15-minute login windows of 10 attempts per username-and-address pair and 30 per
|
||
address across accounts. Attempts from one address do not exhaust another
|
||
address's allowance for the same username. Initial setup and password changes
|
||
retain their limits of 10 per account and 30 per address. Exemptions use the
|
||
account's current database role; password verification is still required.
|
||
|
||
A successful password login also sets a separate, HTTP-only login-device cookie
|
||
for 30 days. This cookie lets that browser skip its username-and-address limit,
|
||
while the password and shared address limit still apply. It does not sign you in,
|
||
and is independent of **Keep me signed in**. It survives ordinary sign-out;
|
||
password changes/resets, role changes, disabling an account, and revoking all of
|
||
its sessions invalidate its login-device cookies. Only token hashes are stored in
|
||
SQLite. Reusing a valid cookie does not extend its original expiry.
|
||
|
||
The database stores audit entries for login
|
||
results, account changes and successful management requests without recording
|
||
passwords, tokens or request bodies. Administrators and Super Viewers can review
|
||
recent entries in Users.
|
||
|
||
For every role, the password service runs at most two scrypt operations at once
|
||
and queues up to 16 more for up to two seconds. If that queue fills or the wait
|
||
expires, the request returns HTTP 429 with `Retry-After: 2`. The persistent rate
|
||
limits return `Retry-After: 900` when a non-exempt account or address reaches its
|
||
attempt limit.
|
||
|
||
An administrator can reset another user's password from Users. If the only
|
||
administrator forgets their password, an operator with local controller/container
|
||
access can run the recovery command in an interactive terminal:
|
||
|
||
```sh
|
||
docker compose exec electric-cherry node scripts/reset-admin.mjs admin
|
||
# Direct Node deployment, using the correct database:
|
||
DB_PATH=./data/cherry.db node scripts/reset-admin.mjs admin
|
||
```
|
||
|
||
Replace `admin` with the existing administrator's username. The command prompts
|
||
without echoing the password, resets that enabled administrator only, revokes
|
||
their sessions and trusted login devices, clears their account-specific login and
|
||
password-change rate limits, and records the recovery. Other users' limits and
|
||
shared address limits remain intact. It does not expose a remote recovery endpoint
|
||
or accept passwords as command arguments.
|
||
|
||
## Agent and telemetry connections
|
||
|
||
Host agents retain enrollment/host-token authentication under `/api/agent/`.
|
||
Installer and release files remain public so a new machine can enroll. A browser
|
||
login never substitutes for a host credential, and host tokens cannot manage EC.
|
||
The agent and standalone updater source contain no credentials. Automatic
|
||
updates fetch the controller's public HTTPS release manifest and verify its
|
||
checksums and compatibility. The installed host retains its own credential;
|
||
the updater accepts an upgrade only after a fresh authenticated heartbeat from
|
||
the new agent process. Updating agent code does not change browser sessions or
|
||
user roles. See [automatic agent updates](native-hosts.md#automatic-agent-updates)
|
||
for the one-time installer upgrade required by existing hosts.
|
||
Host deletion uses a separate operation-scoped receipt token so the uninstall
|
||
worker can confirm removal and retry its acknowledgment after host credentials
|
||
are revoked. That token authorizes only the corresponding cleanup receipt.
|
||
|
||
Game reporters must send a separate secret of at least 32 characters configured
|
||
as `EC_TELEMETRY_TOKEN`:
|
||
|
||
```http
|
||
POST /api/telemetry
|
||
Authorization: Bearer <telemetry-token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
`X-EC-Telemetry-Token` is also supported. Cookies, query parameters and JSON fields
|
||
cannot supply it. A missing server token disables ingestion; a missing or wrong
|
||
request token is rejected before parsing or recording the payload. Existing JSON
|
||
event fields are unchanged. Configure your uploaded reporter/plugin, or a trusted
|
||
relay if that reporter cannot set headers, before upgrading. Use HTTPS for the
|
||
reporter-facing endpoint. Rotating this shared token requires updating reporters.
|
||
|
||
## Proxy and local development
|
||
|
||
Keep Node's internal port behind your TLS proxy and configure `ORIGIN` correctly.
|
||
Management writes require an exact matching `Origin` header, including login,
|
||
JSON requests and multipart uploads. Do not configure a proxy to replace arbitrary
|
||
incoming origins with your trusted origin. Machine endpoints use their own token
|
||
checks and do not require a browser Origin.
|
||
|
||
Login throttling uses the address returned by SvelteKit's adapter. Without
|
||
`ADDRESS_HEADER`, this is the direct peer address, so users behind a reverse proxy
|
||
share the proxy's address limit. Compose passes `ADDRESS_HEADER` and `XFF_DEPTH`
|
||
from `.env` to the controller; direct Node deployments must set them in the
|
||
server's environment. For one trusted proxy that sets `X-Forwarded-For`, use:
|
||
|
||
```dotenv
|
||
ADDRESS_HEADER=x-forwarded-for
|
||
XFF_DEPTH=1
|
||
```
|
||
|
||
Recreate the Compose controller after changing these values with
|
||
`docker compose up -d`. For a proxy chain, set `XFF_DEPTH` to the number of trusted
|
||
proxies: the adapter selects that many positions from the right of the header.
|
||
For example, `client, proxy1` with two trusted proxies requires `XFF_DEPTH=2`.
|
||
The public-facing proxy must replace untrusted forwarding headers, and every
|
||
trusted hop must supply the expected address chain. Restrict direct access to
|
||
port 3000 so requests cannot bypass those proxies. Do not enable these settings
|
||
for an untrusted proxy or copy a depth that does not match your deployment.
|
||
See the [adapter's client-address settings](https://svelte.dev/docs/kit/adapter-node#Environment-variables-ADDRESS_HEADER-and-XFF_DEPTH).
|
||
Forwarded host or user headers do not authenticate users.
|
||
|
||
Local HTTP browser development requires `EC_AUTH_ALLOW_HTTP=true` and a loopback
|
||
hostname (`localhost`, `127.0.0.1` or `::1`). This uses a separate development
|
||
cookie. It never enables HTTP login for a LAN/public hostname. Agent HTTP
|
||
development remains separately controlled by `EC_AGENT_ALLOW_HTTP` and the agent's
|
||
loopback-only option.
|
||
|
||
The implementation follows OWASP's guidance for [authorization on every request](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html#validate-the-permissions-on-every-request),
|
||
[password hashing](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt),
|
||
and [session management](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html).
|