You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
- STREAM_GATE=off # see Security; 'token' once RomM sends the token
48
49
ports:
49
50
- 8000:8000# broker API
50
51
volumes:
@@ -124,6 +125,7 @@ Save states don't care about any of this. The requirement is only for memory-car
124
125
|`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. |
125
126
|`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. |
126
127
|`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). |
127
129
128
130
## API
129
131
@@ -214,7 +216,11 @@ Two credentials, guarding two different things.
214
216
215
217
**`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.
216
218
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`.
218
224
219
225
**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.
0 commit comments