Factorio Admin RCON Source code

Security model

What the panel protects, how, and what it does not claim to protect.

The panel grants access to RCON, which is complete control of the game server. What follows describes what it protects, how, and — just as usefully — what it does not claim to protect.

Roles and permissions#

One password per role, no usernames: the role follows from the password used to sign in.

RoleVariableCan
viewerVIEWER_PASSWORDStatus, players, version, seed, evolution, admins, ban list
moderatorMODERATOR_PASSWORD+ kick, ban, mute, server message, private message
adminADMIN_PASSWORD+ save, promote, custom commands, raw RCON console, audit log
Permissionviewermoderatoradmin
status:read✓✓✓
action:info✓✓✓
action:moderate—✓✓
action:server——✓
action:custom——✓
rcon:raw——✓
audit:read——✓

Permissions are enforced server-side: the catalogue is filtered by role before being sent, and /api/actions re-checks the permission before executing anything. The panel never trusts what the browser hands back.

Two execution paths#

RouteWhoWhat reaches RCON
POST /api/actionsEvery role, according to the action's permissionA command built by the server from an id and validated fields. The client cannot craft an arbitrary one.
POST /api/rconrcon:raw — administratorThe typed line, as-is.

This split is what makes custom commands interesting: they take the first path, so a moderator can be given a precise capability without being given the second.

Sessions#

Origin checking#

Every mutating request compares the Origin header against the host being served. It is the second barrier behind SameSite=Lax, and the one that survives a change of cookie policy. A request with no Origin is accepted: it did not come from a browser, so no CSRF is possible — which is what lets you call the API with curl.

Limits and shutdown#

Headers and CSP#

The content security policy carries a fresh 128-bit nonce per request on script-src, together with 'strict-dynamic': only scripts the server emitted run. 'unsafe-inline' becomes inert as soon as a nonce is present, which is exactly the intended effect.

style-src deliberately keeps 'unsafe-inline': the charts set style attributes on SVG elements, which style-src would block without it. CSS injection does not carry the reach of script injection — the trade-off is accepted, and it no longer concerns JavaScript.

Alongside it: nosniff, Referrer-Policy, Permissions-Policy, X-Frame-Options and HSTS. API routes, which never render HTML, get default-src 'none'.

Audit log#

Every action is recorded, refusals included: who, what, from which IP, with which outcome and how long it took.

Container isolation#

The panel and docker-proxy both run confined: read-only root, cap_drop: [ALL], no-new-privileges, memory and PID caps.

No Docker socket in the panel CPU and memory metrics go through the docker-proxy service, which holds the socket and only exposes GET /containers/…. Mounting the socket into the panel would hand the whole host to anyone who compromised it — and :ro would change nothing, the Docker API being a write API.

The factorio service is deliberately left alone: it writes its saves, mods and configuration, and a read-only root would break it.

Deployment assumptions#

Out of scope#

The Lua command confirmation is an interface aid, not a protection: an administrator account has full RCON access by definition. The panel does not try to protect against that — it tries to make sure not everyone needs to be an administrator.