Skip to content

Console security ​

The console is reachable from the internet, so it is locked down tighter than anything else in AutoRune. In short: one Discord account, short revocable sessions, no shell, a fixed list of operations, and an audit row for everything.

Who can get in ​

  • Discord only. OAuth2 with PKCE, scope identify, the same Discord application as the app. There is no password, no other provider and no fallback: if Discord sign-in isn't configured, or its redirect URI isn't registered, nobody gets in.
  • One account. The console has its own allowlist of Discord IDs (stable, unlike usernames), today the owner's only. It is checked at sign-in and on every request: removing an ID ends its live sessions at once.
  • Refusals are logged. Every denied sign-in lands in the audit log with the Discord ID that tried.

Sessions ​

  • A random 256-bit token in a cookie that is httpOnly, Secure, SameSite=Strict and __Host- prefixed (this host only, path /, no subdomain can set or read it).
  • Only a hash of the token is stored, server-side, so every session can be revoked: sign out, "sign out everywhere", or an allowlist change.
  • Sessions end after 12 hours whatever happens, and after 2 hours without use.

Requests ​

  • Everything under /api needs a session, apart from the sign-in redirect and its callback. Without one every route answers 401, unknown routes included.
  • CSRF: every write needs the session's CSRF token in a header, the console's exact Origin and a same-origin fetch, on top of the SameSite=Strict cookie.
  • Confirmation: every action needs an explicit confirm in its body; the app only sends it from the confirm dialog.
  • Rate limits per address in nginx and in the console API (sign-in much tighter), and per session for actions.
  • Headers: a strict Content Security Policy (scripts from the console itself only, no framing, no forms, no other origins), HSTS, nosniff, no referrer, a restrictive permissions policy and noindex on everything (plus a robots.txt that disallows all).

What it can do on the server ​

  • Its own system user. The console API runs as a dedicated user with no shell and no home, not as the account that maintains the server. It can't read the repository, the other services' secrets or anyone's home directory, and its systemd unit is sandboxed (read-only system, private /tmp, no kernel tunables).
  • A fixed list of root commands. Its only root rights are a sudoers drop-in with exact command lines: systemctl start|stop|restart for the allowlisted units and journalctl -u <unit> with fixed flags for reading logs. The drop-in is generated from the same code that builds the commands, checked with visudo and compared against the committed copy on every deploy. Any other command, unit or flag is refused by sudo itself.
  • No shell. Commands are started as fixed argument lists, never through a shell, and a unit name must be on the registry's list before anything runs. The console can't stop or restart itself.
  • Reading data: each environment's database through a read-only role that can only see the columns the console shows (no token hashes, no encrypted keys or game passwords), with read-only transactions and a statement timeout.
  • Changing an environment: through that environment's admin API, a separate listener of the environment's own API that is bound to loopback, never proxied by nginx, and requires a shared secret known only to the console. It can list avatars, start and stop them, and lock or unlock accounts. Nothing else.

Metrics ​

Netdata listens on loopback only. The console's Metrics page asks the console API for a fixed set of charts. The full Netdata dashboard is served under /netdata/ only after nginx has checked the console session; without one it answers 401. The dashboard gets its own CSP that lets it talk to this site only, so it never contacts Netdata Cloud. Netdata telemetry is off.

Known trade-off: the Netdata dashboard runs on the console's own origin, so a flaw in it could act with the console session. It only runs when the operator opens it. Moving it to its own hostname is planned.

Audit log ​

Every sign-in (allowed, refused or failed), sign-out and action is written before the response goes out: who (Discord ID and name), what, which environment, target, result (ok, error, denied), details (for allowlist edits: what was added and removed) and the client address. The log lives with the console's sessions in its private state directory and is shown in the console.

AutoRune is an experiment built on rs-sdk and LostCity. RuneScape is a trademark of Jagex Ltd; this project is not affiliated with Jagex.