Skip to content

Commit f6d5a19

Browse files
committed
fix(stream): document that the stream gate ships off
The Security section stated the token gate as settled fact, which stopped being true the moment STREAM_GATE landed. It now names the exposure in its own paragraph rather than burying it in a variable table: with the gate off, anyone reaching 3000 or 3001 gets the desktop and the ROM library at /files with no credential. It also names why, with both upstream PRs linked, because an operator weighing that risk needs to know it ends when rommapp/romm#3856 merges and that turning the gate on is one variable and a restart. The init.sh comment block claimed unconditional enforcement too. It now records the split: the gate is always injected, the broker decides whether to enforce, and that is what keeps a mode switch from needing a recreate in each direction.
1 parent 5399f27 commit f6d5a19

2 files changed

Lines changed: 14 additions & 1 deletion

File tree

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ services:
4545
- DOCKER_MODS=ghcr.io/loneangelfayt/pcsx2-romm-integration-mod:latest
4646
- BROKER_SECRET=your_secret_here
4747
- ROM_ROOT=/romm/library
48+
- STREAM_GATE=off # see Security; 'token' once RomM sends the token
4849
ports:
4950
- 8000:8000 # broker API
5051
volumes:
@@ -124,6 +125,7 @@ Save states don't care about any of this. The requirement is only for memory-car
124125
| `PUID` / `PGID` | `1000` | Standard LinuxServer UID/GID. Also used to chown files the broker writes for PCSX2, which runs as `abc` and has to overwrite them later. |
125126
| `STREAM_TOKEN_TTL` | `43200.0` | Idle seconds before a stream token stops working. Every admitted request slides it forward, so it only fires on a session nobody is watching. Stops an abandoned session leaving the stream gate open forever. |
126127
| `STREAM_TOKEN_GRACE` | `120.0` | How long the previous token keeps working after a relaunch mints a new one, so an already-open tab is not cut off mid-navigation. |
128+
| `STREAM_GATE` | `off` | `token` enforces the stream gate: the desktop on 3000/3001 admits only requests carrying the token `POST /launch` mints. `off` admits everything. Defaults to `off` because released RomM cannot send that token yet, see [Security](#security). |
127129

128130
## API
129131

@@ -214,7 +216,11 @@ Two credentials, guarding two different things.
214216

215217
**`BROKER_SECRET` guards the API on port 8000.** Sent as `X-Broker-Secret`, compared in constant time. `GET /health` and `GET /verify` are deliberately exempt: `/health` so it works as a container healthcheck, `/verify` because nginx's `auth_request` cannot forward the secret and the stream token is itself the credential there.
216218

217-
**The stream token guards the desktop on 3000/3001.** It is minted per session by `POST /launch`, 256 bits from `secrets.token_urlsafe`, and enforced by an nginx `auth_request` that the mod injects into *every* `server` block in the site config. That matters: the base image ships two identical vhosts, plain HTTP on 3000 and TLS on 3001, both proxying the same selkies stream and both serving `/config/Desktop` at `/files`. Gating only the TLS one left a complete bypass a port number away. The `stream_sid` cookie is `Secure`, so 3000 is usable only behind a TLS-terminating proxy — direct plain-HTTP browsing to it now fails closed.
219+
**The stream token guards the desktop on 3000/3001, when you turn it on.** It is minted per session by `POST /launch`, 256 bits from `secrets.token_urlsafe`, and enforced by an nginx `auth_request` that the mod injects into *every* `server` block in the site config. That matters: the base image ships two identical vhosts, plain HTTP on 3000 and TLS on 3001, both proxying the same selkies stream and both serving `/config/Desktop` at `/files`. Gating only the TLS one left a complete bypass a port number away. The `stream_sid` cookie is `Secure`, so 3000 is usable only behind a TLS-terminating proxy: direct plain-HTTP browsing to it fails closed.
220+
221+
**The gate ships off, and that is a real hole.** With `STREAM_GATE=off`, anyone who can reach port 3000 or 3001 gets the interactive desktop with your ROM library browsable at `/files`, no credential asked for. It defaults to off because RomM cannot send the token yet. The streaming feature people are running is [rommapp/romm#3211](https://github.com/rommapp/romm/pull/3211), which is merged and hands the browser your configured `host` with nothing appended; the half that carries the token into the iframe URL is [rommapp/romm#3856](https://github.com/rommapp/romm/pull/3856), still open. Enforcing against a client that cannot comply is not a gate, it is a black stream: nginx refuses the document, every asset and the WebSocket upgrade alike, with nothing on screen to say why.
222+
223+
So: keep the container on a network you trust until #3856 ships, then set `STREAM_GATE=token` and restart. No recreate is needed, because enforcement is decided in the broker and the nginx gate is already injected either way. The broker logs which mode it is in at startup, and `GET /status` reports it as `stream_gate`.
218224

219225
**Leaving `BROKER_SECRET` unset is a known hole, not a supported mode.** The broker runs as root inside the container, so the open API means root-privileged reads and writes under `/config`, plus arbitrary launches within `ROM_ROOT`. Worse, the two credentials stop being independent: `POST /launch` *returns* a stream token, so an unauthenticated broker hands out the credential that opens the desktop. Use it for local debugging on a trusted host and nothing else. The broker logs a warning at startup when it is unset.
220226

root/etc/s6-overlay/s6-rc.d/init-pcsx2-config/init.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ fi
119119
# The broker exempts /verify from its shared secret because nginx cannot forward
120120
# that secret and the stream token is the credential.
121121
#
122+
# The gate is injected unconditionally, but ENFORCEMENT is the broker's call:
123+
# /verify admits everything when STREAM_GATE=off, which is the default while
124+
# RomM has no way to send the token. That split is deliberate. This file writes
125+
# into the container's writable layer behind the marker grep below, so a mode
126+
# decided here would need a --force-recreate in each direction, while a mode
127+
# decided in the broker is a restart.
128+
#
122129
# EVERY server block is gated, not just the 3001 SSL vhost RomM points at. The
123130
# base image ships a second, identical plain-HTTP vhost on 3000 (same /websocket
124131
# proxy to selkies, same /files alias of /config/Desktop), so anchoring this on

0 commit comments

Comments
 (0)