From 82a51368ef1ab5bdcf5abf52dbf1a290228baa0d Mon Sep 17 00:00:00 2001 From: Spriha Anand Date: Wed, 21 Oct 2026 16:19:15 +0530 Subject: [PATCH 1/2] enforce non-zero max_message_bytes and document that port 4848 must sit behind an authenticating proxy --- docs/deploy.md | 21 +++++++++++++++++++++ src/client/config.rs | 21 +++++++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/deploy.md b/docs/deploy.md index 8ab2ca8..e54c3d2 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -291,6 +291,27 @@ depend on what the defaults happen to be: headers; - should not close idle WebSockets in under 60 s. +### Authentication + +**The server has no built-in client authentication.** It accepts every +WebSocket connection regardless of whether the client supplies an auth token or +a cookie. The application server's query endpoint is the authentication +boundary: it receives the connection's credentials (the HTTP `Cookie` header +and the `authToken` from the handshake) on every `transform` call, and must +verify them and return an AST only for queries the caller is allowed to see. +If the application server does not enforce authentication on its transform +endpoint, any client that can reach port 4848 can subscribe to queries and +receive rows. + +**Port 4848 must not be publicly reachable.** The server listens on +`0.0.0.0:4848` by default, which binds every interface. Put an authenticating +reverse proxy in front and restrict access to port 4848: + +- Set `STREAMGRES_ADDR=127.0.0.1:4848` when the proxy runs on the same host, + so port 4848 is not reachable from outside the machine. +- In a container environment, use a network policy to limit port 4848 to + traffic from the proxy's subnet only. + ## 7. Routing traffic Clients connect to `:///`. diff --git a/src/client/config.rs b/src/client/config.rs index 851d4eb..288d31f 100644 --- a/src/client/config.rs +++ b/src/client/config.rs @@ -122,7 +122,7 @@ pub struct Config { /// told to start over (262144; 0 keeps none). pub group_log_bytes: usize, /// `STREAMGRES_MAX_MESSAGE_BYTES`: the largest inbound WebSocket message - /// accepted (16 MiB). + /// accepted (16 MiB). Must be a positive number of bytes. pub max_message_bytes: usize, /// `STREAMGRES_BYTES_PER_PART`: how many bytes of serialized row /// operations one poke part carries at most (768000, i.e. 750 KiB). @@ -373,9 +373,15 @@ impl Config { None => 256 * 1024, }, max_message_bytes: match first(&["STREAMGRES_MAX_MESSAGE_BYTES"]) { - Some(text) => text.parse().map_err(|_| { - format!("STREAMGRES_MAX_MESSAGE_BYTES must be a number, got `{text}`") - })?, + Some(text) => text + .parse::() + .ok() + .filter(|bytes| *bytes > 0) + .ok_or_else(|| { + format!( + "STREAMGRES_MAX_MESSAGE_BYTES must be a positive number of bytes, got `{text}`" + ) + })?, None => 16 * 1024 * 1024, }, bytes_per_part: match first(&["STREAMGRES_BYTES_PER_PART"]) { @@ -630,6 +636,13 @@ mod tests { ); } + /// Zero disables the message size cap, so it is refused. + #[test] + fn zero_max_message_bytes_is_refused() { + let error = config(&[("STREAMGRES_MAX_MESSAGE_BYTES", "0")]).unwrap_err(); + assert!(error.contains("STREAMGRES_MAX_MESSAGE_BYTES"), "{error}"); + } + /// A value that is not a number is refused by the variable's name. #[test] fn a_malformed_keepalive_is_refused_by_name() { From 249f264aabb8285907b5d90a254157f816358429 Mon Sep 17 00:00:00 2001 From: Spriha Anand Date: Wed, 21 Oct 2026 19:24:40 +0530 Subject: [PATCH 2/2] reverting documentation --- docs/deploy.md | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/docs/deploy.md b/docs/deploy.md index e54c3d2..8ab2ca8 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -291,27 +291,6 @@ depend on what the defaults happen to be: headers; - should not close idle WebSockets in under 60 s. -### Authentication - -**The server has no built-in client authentication.** It accepts every -WebSocket connection regardless of whether the client supplies an auth token or -a cookie. The application server's query endpoint is the authentication -boundary: it receives the connection's credentials (the HTTP `Cookie` header -and the `authToken` from the handshake) on every `transform` call, and must -verify them and return an AST only for queries the caller is allowed to see. -If the application server does not enforce authentication on its transform -endpoint, any client that can reach port 4848 can subscribe to queries and -receive rows. - -**Port 4848 must not be publicly reachable.** The server listens on -`0.0.0.0:4848` by default, which binds every interface. Put an authenticating -reverse proxy in front and restrict access to port 4848: - -- Set `STREAMGRES_ADDR=127.0.0.1:4848` when the proxy runs on the same host, - so port 4848 is not reachable from outside the machine. -- In a container environment, use a network policy to limit port 4848 to - traffic from the proxy's subnet only. - ## 7. Routing traffic Clients connect to `:///`.